mirror of
https://github.com/EthanMarti/infio-copilot.git
synced 2026-01-16 16:31:56 +00:00
add switch mode tool
This commit is contained in:
parent
9a5e5f3880
commit
4aa321dffc
@ -14,6 +14,7 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
|
import { ModeSelect } from './chat-input/ModeSelect'
|
||||||
import { ApplyViewState } from '../../ApplyView'
|
import { ApplyViewState } from '../../ApplyView'
|
||||||
import { APPLY_VIEW_TYPE } from '../../constants'
|
import { APPLY_VIEW_TYPE } from '../../constants'
|
||||||
import { useApp } from '../../contexts/AppContext'
|
import { useApp } from '../../contexts/AppContext'
|
||||||
@ -90,7 +91,7 @@ export type ChatProps = {
|
|||||||
|
|
||||||
const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
||||||
const app = useApp()
|
const app = useApp()
|
||||||
const { settings } = useSettings()
|
const { settings, setSettings } = useSettings()
|
||||||
const { getRAGEngine } = useRAG()
|
const { getRAGEngine } = useRAG()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -565,6 +566,25 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
mentionables: [],
|
mentionables: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (toolArgs.type === 'switch_mode') {
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
mode: toolArgs.mode,
|
||||||
|
})
|
||||||
|
const formattedContent = `[switch_mode to ${toolArgs.mode}] Result: successfully switched to ${toolArgs.mode}\n`
|
||||||
|
return {
|
||||||
|
type: 'switch_mode',
|
||||||
|
applyMsgId,
|
||||||
|
applyStatus: ApplyStatus.Applied,
|
||||||
|
returnMsg: {
|
||||||
|
role: 'user',
|
||||||
|
applyStatus: ApplyStatus.Idle,
|
||||||
|
content: null,
|
||||||
|
promptContent: formattedContent,
|
||||||
|
id: uuidv4(),
|
||||||
|
mentionables: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to apply changes', error)
|
console.error('Failed to apply changes', error)
|
||||||
@ -745,7 +765,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
return (
|
return (
|
||||||
<div className="infio-chat-container">
|
<div className="infio-chat-container">
|
||||||
<div className="infio-chat-header">
|
<div className="infio-chat-header">
|
||||||
<h1 className="infio-chat-header-title"> CHAT </h1>
|
<ModeSelect />
|
||||||
<div className="infio-chat-header-buttons">
|
<div className="infio-chat-header-buttons">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleNewChat()}
|
onClick={() => handleNewChat()}
|
||||||
|
|||||||
82
src/components/chat-view/MarkdownSwitchModeBlock.tsx
Normal file
82
src/components/chat-view/MarkdownSwitchModeBlock.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { Check, Loader2, Settings2, X } from 'lucide-react'
|
||||||
|
import { PropsWithChildren, useState } from 'react'
|
||||||
|
|
||||||
|
import { useDarkModeContext } from '../../contexts/DarkModeContext'
|
||||||
|
import { ApplyStatus, ToolArgs } from '../../types/apply'
|
||||||
|
|
||||||
|
import { MemoizedSyntaxHighlighterWrapper } from './SyntaxHighlighterWrapper'
|
||||||
|
|
||||||
|
export default function MarkdownSwitchModeBlock({
|
||||||
|
mode,
|
||||||
|
applyStatus,
|
||||||
|
onApply,
|
||||||
|
reason,
|
||||||
|
finish,
|
||||||
|
}: PropsWithChildren<{
|
||||||
|
mode: string
|
||||||
|
applyStatus: ApplyStatus
|
||||||
|
onApply: (args: ToolArgs) => void
|
||||||
|
reason: string
|
||||||
|
finish: boolean
|
||||||
|
}>) {
|
||||||
|
const [applying, setApplying] = useState(false)
|
||||||
|
const { isDarkMode } = useDarkModeContext()
|
||||||
|
|
||||||
|
const handleApply = async () => {
|
||||||
|
if (applyStatus !== ApplyStatus.Idle) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setApplying(true)
|
||||||
|
onApply({
|
||||||
|
type: 'switch_mode',
|
||||||
|
mode: mode,
|
||||||
|
reason: reason,
|
||||||
|
finish: finish,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`infio-chat-code-block has-filename`}>
|
||||||
|
<div className={'infio-chat-code-block-header'}>
|
||||||
|
<div className={'infio-chat-code-block-header-filename'}>
|
||||||
|
<Settings2 size={10} className="infio-chat-code-block-header-icon" />
|
||||||
|
Switch to "{mode.charAt(0).toUpperCase() + mode.slice(1)}" mode
|
||||||
|
</div>
|
||||||
|
<div className={'infio-chat-code-block-header-button'}>
|
||||||
|
<button
|
||||||
|
onClick={handleApply}
|
||||||
|
style={{ color: '#008000' }}
|
||||||
|
disabled={applyStatus !== ApplyStatus.Idle || applying}
|
||||||
|
>
|
||||||
|
{applyStatus === ApplyStatus.Idle ? (
|
||||||
|
applying ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="spinner" size={14} /> Allowing...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'Allow'
|
||||||
|
)
|
||||||
|
) : applyStatus === ApplyStatus.Applied ? (
|
||||||
|
<>
|
||||||
|
<Check size={14} /> Success
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<X size={14} /> Failed
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<MemoizedSyntaxHighlighterWrapper
|
||||||
|
isDarkMode={isDarkMode}
|
||||||
|
language="markdown"
|
||||||
|
hasFilename={true}
|
||||||
|
wrapLines={true}
|
||||||
|
isOpen={true}
|
||||||
|
>
|
||||||
|
{reason}
|
||||||
|
</MemoizedSyntaxHighlighterWrapper>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -16,6 +16,7 @@ import MarkdownRegexSearchFilesBlock from './MarkdownRegexSearchFilesBlock'
|
|||||||
import MarkdownSearchAndReplace from './MarkdownSearchAndReplace'
|
import MarkdownSearchAndReplace from './MarkdownSearchAndReplace'
|
||||||
import MarkdownSearchWebBlock from './MarkdownSearchWebBlock'
|
import MarkdownSearchWebBlock from './MarkdownSearchWebBlock'
|
||||||
import MarkdownSemanticSearchFilesBlock from './MarkdownSemanticSearchFilesBlock'
|
import MarkdownSemanticSearchFilesBlock from './MarkdownSemanticSearchFilesBlock'
|
||||||
|
import MarkdownSwitchModeBlock from './MarkdownSwitchModeBlock'
|
||||||
import MarkdownWithIcons from './MarkdownWithIcon'
|
import MarkdownWithIcons from './MarkdownWithIcon'
|
||||||
function ReactMarkdown({
|
function ReactMarkdown({
|
||||||
applyStatus,
|
applyStatus,
|
||||||
@ -132,6 +133,15 @@ function ReactMarkdown({
|
|||||||
markdownContent={
|
markdownContent={
|
||||||
`<icon name='ask_followup_question' size={14} className="infio-markdown-icon" />
|
`<icon name='ask_followup_question' size={14} className="infio-markdown-icon" />
|
||||||
${block.question && block.question.trimStart()}`} />
|
${block.question && block.question.trimStart()}`} />
|
||||||
|
) : block.type === 'switch_mode' ? (
|
||||||
|
<MarkdownSwitchModeBlock
|
||||||
|
key={"switch-mode-" + index}
|
||||||
|
applyStatus={applyStatus}
|
||||||
|
onApply={onApply}
|
||||||
|
mode={block.mode}
|
||||||
|
reason={block.reason}
|
||||||
|
finish={block.finish}
|
||||||
|
/>
|
||||||
) : block.type === 'search_web' ? (
|
) : block.type === 'search_web' ? (
|
||||||
<MarkdownSearchWebBlock
|
<MarkdownSearchWebBlock
|
||||||
key={"search-web-" + index}
|
key={"search-web-" + index}
|
||||||
|
|||||||
53
src/components/chat-view/chat-input/ModeSelect.tsx
Normal file
53
src/components/chat-view/chat-input/ModeSelect.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
||||||
|
import { ChevronDown, ChevronUp } from 'lucide-react'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import { useSettings } from '../../../contexts/SettingsContext'
|
||||||
|
import { modes } from '../../../utils/modes'
|
||||||
|
|
||||||
|
export function ModeSelect() {
|
||||||
|
const { settings, setSettings } = useSettings()
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
const [mode, setMode] = useState(settings.mode)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMode(settings.mode)
|
||||||
|
}, [settings.mode])
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||||
|
<DropdownMenu.Trigger className="infio-chat-input-model-select">
|
||||||
|
<div className="infio-chat-input-model-select__icon">
|
||||||
|
{isOpen ? <ChevronUp size={12} /> : <ChevronDown size={12} />}
|
||||||
|
</div>
|
||||||
|
<div className="infio-chat-input-model-select__model-name">
|
||||||
|
{modes.find((m) => m.slug === mode)?.name}
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
|
<DropdownMenu.Portal>
|
||||||
|
<DropdownMenu.Content
|
||||||
|
className="infio-popover">
|
||||||
|
<ul>
|
||||||
|
{modes.map((mode) => (
|
||||||
|
<DropdownMenu.Item
|
||||||
|
key={mode.slug}
|
||||||
|
onSelect={() => {
|
||||||
|
setMode(mode.slug)
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
mode: mode.slug,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<li>{mode.name}</li>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -18,15 +18,16 @@ export function ModelSelect() {
|
|||||||
try {
|
try {
|
||||||
const models = await GetProviderModelIds(settings.chatModelProvider)
|
const models = await GetProviderModelIds(settings.chatModelProvider)
|
||||||
setProviderModels(models)
|
setProviderModels(models)
|
||||||
|
setChatModelId(settings.chatModelId)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch provider models:', error)
|
console.error('Failed to fetch provider models:', error)
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchModels()
|
fetchModels()
|
||||||
}, [settings.chatModelProvider])
|
}, [settings])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu.Root open={isOpen} onOpenChange={setIsOpen}>
|
<DropdownMenu.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import { ImageUploadButton } from './ImageUploadButton'
|
|||||||
import LexicalContentEditable from './LexicalContentEditable'
|
import LexicalContentEditable from './LexicalContentEditable'
|
||||||
import MentionableBadge from './MentionableBadge'
|
import MentionableBadge from './MentionableBadge'
|
||||||
import { ModelSelect } from './ModelSelect'
|
import { ModelSelect } from './ModelSelect'
|
||||||
|
import { ModeSelect } from './ModeSelect'
|
||||||
import { MentionNode } from './plugins/mention/MentionNode'
|
import { MentionNode } from './plugins/mention/MentionNode'
|
||||||
import { NodeMutations } from './plugins/on-mutation/OnMutationPlugin'
|
import { NodeMutations } from './plugins/on-mutation/OnMutationPlugin'
|
||||||
import { SubmitButton } from './SubmitButton'
|
import { SubmitButton } from './SubmitButton'
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { promises as fs } from "fs"
|
// import { promises as fs } from "fs"
|
||||||
import * as path from "path"
|
// import * as path from "path"
|
||||||
import * as vscode from "vscode"
|
// import * as vscode from "vscode"
|
||||||
|
|
||||||
import { ModeConfig, getAllModesWithPrompts } from "../../../utils/modes"
|
import { ModeConfig, getAllModesWithPrompts } from "../../../utils/modes"
|
||||||
|
|
||||||
export async function getModesSection(context: vscode.ExtensionContext): Promise<string> {
|
export async function getModesSection(): Promise<string> {
|
||||||
const settingsDir = path.join(context.globalStorageUri.fsPath, "settings")
|
// const settingsDir = path.join(context.globalStorageUri.fsPath, "settings")
|
||||||
await fs.mkdir(settingsDir, { recursive: true })
|
// await fs.mkdir(settingsDir, { recursive: true })
|
||||||
const customModesPath = path.join(settingsDir, "cline_custom_modes.json")
|
// const customModesPath = path.join(settingsDir, "cline_custom_modes.json")
|
||||||
|
|
||||||
// Get all modes with their overrides from extension state
|
// Get all modes with their overrides from extension state
|
||||||
const allModes = await getAllModesWithPrompts(context)
|
const allModes = await getAllModesWithPrompts()
|
||||||
|
|
||||||
return `====
|
return `====
|
||||||
|
|
||||||
@ -18,43 +18,5 @@ MODES
|
|||||||
|
|
||||||
- These are the currently available modes:
|
- These are the currently available modes:
|
||||||
${allModes.map((mode: ModeConfig) => ` * "${mode.name}" mode (${mode.slug}) - ${mode.roleDefinition.split(".")[0]}`).join("\n")}
|
${allModes.map((mode: ModeConfig) => ` * "${mode.name}" mode (${mode.slug}) - ${mode.roleDefinition.split(".")[0]}`).join("\n")}
|
||||||
|
`
|
||||||
- Custom modes can be configured in two ways:
|
|
||||||
1. Globally via '${customModesPath}' (created automatically on startup)
|
|
||||||
2. Per-workspace via '.roomodes' in the workspace root directory
|
|
||||||
|
|
||||||
When modes with the same slug exist in both files, the workspace-specific .roomodes version takes precedence. This allows projects to override global modes or define project-specific modes.
|
|
||||||
|
|
||||||
If asked to create a project mode, create it in .roomodes in the workspace root. If asked to create a global mode, use the global custom modes file.
|
|
||||||
|
|
||||||
- The following fields are required and must not be empty:
|
|
||||||
* slug: A valid slug (lowercase letters, numbers, and hyphens). Must be unique, and shorter is better.
|
|
||||||
* name: The display name for the mode
|
|
||||||
* roleDefinition: A detailed description of the mode's role and capabilities
|
|
||||||
* groups: Array of allowed tool groups (can be empty). Each group can be specified either as a string (e.g., "edit" to allow editing any file) or with file restrictions (e.g., ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }] to only allow editing markdown files)
|
|
||||||
|
|
||||||
- The customInstructions field is optional.
|
|
||||||
|
|
||||||
- For multi-line text, include newline characters in the string like "This is the first line.\\nThis is the next line.\\n\\nThis is a double line break."
|
|
||||||
|
|
||||||
Both files should follow this structure:
|
|
||||||
{
|
|
||||||
"customModes": [
|
|
||||||
{
|
|
||||||
"slug": "designer", // Required: unique slug with lowercase letters, numbers, and hyphens
|
|
||||||
"name": "Designer", // Required: mode display name
|
|
||||||
"roleDefinition": "You are Infio, a UI/UX expert specializing in design systems and frontend development. Your expertise includes:\\n- Creating and maintaining design systems\\n- Implementing responsive and accessible web interfaces\\n- Working with CSS, HTML, and modern frontend frameworks\\n- Ensuring consistent user experiences across platforms", // Required: non-empty
|
|
||||||
"groups": [ // Required: array of tool groups (can be empty)
|
|
||||||
"read", // Read files group (read_file, search_files, list_files)
|
|
||||||
"edit", // Edit files group (apply_diff, write_to_file) - allows editing any file
|
|
||||||
// Or with file restrictions:
|
|
||||||
// ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], // Edit group that only allows editing markdown files
|
|
||||||
"browser", // Browser group (browser_action)
|
|
||||||
"command", // Command group (execute_command)
|
|
||||||
"mcp" // MCP group (use_mcp_tool, access_mcp_resource)
|
|
||||||
],
|
|
||||||
"customInstructions": "Additional instructions for the Designer mode" // Optional
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import {
|
|||||||
addCustomInstructions,
|
addCustomInstructions,
|
||||||
getCapabilitiesSection,
|
getCapabilitiesSection,
|
||||||
getMcpServersSection,
|
getMcpServersSection,
|
||||||
// getModesSection,
|
getModesSection,
|
||||||
getObjectiveSection,
|
getObjectiveSection,
|
||||||
getRulesSection,
|
getRulesSection,
|
||||||
getSharedToolUseSection,
|
getSharedToolUseSection,
|
||||||
@ -44,7 +44,7 @@ async function generatePrompt(
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
const searchTool = "semantic"
|
const searchTool = "semantic"
|
||||||
|
|
||||||
// If diff is disabled, don't pass the diffStrategy
|
// If diff is disabled, don't pass the diffStrategy
|
||||||
const effectiveDiffStrategy = diffEnabled ? diffStrategy : undefined
|
const effectiveDiffStrategy = diffEnabled ? diffStrategy : undefined
|
||||||
|
|
||||||
@ -52,9 +52,12 @@ async function generatePrompt(
|
|||||||
const modeConfig = getModeBySlug(mode, customModeConfigs) || modes.find((m) => m.slug === mode) || modes[0]
|
const modeConfig = getModeBySlug(mode, customModeConfigs) || modes.find((m) => m.slug === mode) || modes[0]
|
||||||
const roleDefinition = promptComponent?.roleDefinition || modeConfig.roleDefinition
|
const roleDefinition = promptComponent?.roleDefinition || modeConfig.roleDefinition
|
||||||
|
|
||||||
const mcpServersSection = modeConfig.groups.some((groupEntry) => getGroupName(groupEntry) === "mcp")
|
const [modesSection, mcpServersSection] = await Promise.all([
|
||||||
? await getMcpServersSection(mcpHub, effectiveDiffStrategy, enableMcpServerCreation)
|
getModesSection(),
|
||||||
: ""
|
modeConfig.groups.some((groupEntry) => getGroupName(groupEntry) === "mcp")
|
||||||
|
? getMcpServersSection(mcpHub, effectiveDiffStrategy, enableMcpServerCreation)
|
||||||
|
: Promise.resolve(""),
|
||||||
|
])
|
||||||
|
|
||||||
const basePrompt = `${roleDefinition}
|
const basePrompt = `${roleDefinition}
|
||||||
|
|
||||||
@ -77,19 +80,21 @@ ${getToolUseGuidelinesSection()}
|
|||||||
${mcpServersSection}
|
${mcpServersSection}
|
||||||
|
|
||||||
${getCapabilitiesSection(
|
${getCapabilitiesSection(
|
||||||
mode,
|
mode,
|
||||||
cwd,
|
cwd,
|
||||||
searchTool,
|
searchTool,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
${modesSection}
|
||||||
|
|
||||||
${getRulesSection(
|
${getRulesSection(
|
||||||
mode,
|
mode,
|
||||||
cwd,
|
cwd,
|
||||||
searchTool,
|
searchTool,
|
||||||
supportsComputerUse,
|
supportsComputerUse,
|
||||||
effectiveDiffStrategy,
|
effectiveDiffStrategy,
|
||||||
experiments,
|
experiments,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${getSystemInfoSection(cwd)}
|
${getSystemInfoSection(cwd)}
|
||||||
|
|
||||||
|
|||||||
@ -76,4 +76,11 @@ export type FetchUrlsContentToolArgs = {
|
|||||||
finish?: boolean;
|
finish?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ToolArgs = ReadFileToolArgs | WriteToFileToolArgs | InsertContentToolArgs | SearchAndReplaceToolArgs | ListFilesToolArgs | RegexSearchFilesToolArgs | SemanticSearchFilesToolArgs | SearchWebToolArgs | FetchUrlsContentToolArgs;
|
export type SwitchModeToolArgs = {
|
||||||
|
type: 'switch_mode';
|
||||||
|
mode: string;
|
||||||
|
reason: string;
|
||||||
|
finish?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ToolArgs = ReadFileToolArgs | WriteToFileToolArgs | InsertContentToolArgs | SearchAndReplaceToolArgs | ListFilesToolArgs | RegexSearchFilesToolArgs | SemanticSearchFilesToolArgs | SearchWebToolArgs | FetchUrlsContentToolArgs | SwitchModeToolArgs;
|
||||||
|
|||||||
@ -219,6 +219,9 @@ export const InfioSettingsSchema = z.object({
|
|||||||
embeddingModelProvider: z.nativeEnum(ApiProvider).catch(ApiProvider.Google),
|
embeddingModelProvider: z.nativeEnum(ApiProvider).catch(ApiProvider.Google),
|
||||||
embeddingModelId: z.string().catch(''),
|
embeddingModelId: z.string().catch(''),
|
||||||
|
|
||||||
|
// Mode
|
||||||
|
mode: z.string().catch('ask'),
|
||||||
|
|
||||||
/// [compatible]
|
/// [compatible]
|
||||||
// activeModels [compatible]
|
// activeModels [compatible]
|
||||||
activeModels: z.array(
|
activeModels: z.array(
|
||||||
|
|||||||
@ -248,15 +248,15 @@ export const defaultPrompts: Readonly<CustomModePrompts> = Object.freeze(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Helper function to get all modes with their prompt overrides from extension state
|
// Helper function to get all modes with their prompt overrides from extension state
|
||||||
export async function getAllModesWithPrompts(context: vscode.ExtensionContext): Promise<ModeConfig[]> {
|
export async function getAllModesWithPrompts(): Promise<ModeConfig[]> {
|
||||||
const customModes = (await context.globalState.get<ModeConfig[]>("customModes")) || []
|
// const customModes = (await context.globalState.get<ModeConfig[]>("customModes")) || []
|
||||||
const customModePrompts = (await context.globalState.get<CustomModePrompts>("customModePrompts")) || {}
|
// const customModePrompts = (await context.globalState.get<CustomModePrompts>("customModePrompts")) || {}
|
||||||
|
|
||||||
const allModes = getAllModes(customModes)
|
const allModes = getAllModes()
|
||||||
return allModes.map((mode) => ({
|
return allModes.map((mode) => ({
|
||||||
...mode,
|
...mode,
|
||||||
roleDefinition: customModePrompts[mode.slug]?.roleDefinition ?? mode.roleDefinition,
|
roleDefinition: mode.roleDefinition,
|
||||||
customInstructions: customModePrompts[mode.slug]?.customInstructions ?? mode.customInstructions,
|
customInstructions: mode.customInstructions,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -68,6 +68,11 @@ export type ParsedMsgBlock =
|
|||||||
type: 'fetch_urls_content'
|
type: 'fetch_urls_content'
|
||||||
urls: string[]
|
urls: string[]
|
||||||
finish: boolean
|
finish: boolean
|
||||||
|
} | {
|
||||||
|
type: 'switch_mode'
|
||||||
|
mode: string
|
||||||
|
reason: string
|
||||||
|
finish: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseMsgBlocks(
|
export function parseMsgBlocks(
|
||||||
@ -399,7 +404,6 @@ export function parseMsgBlocks(
|
|||||||
result,
|
result,
|
||||||
})
|
})
|
||||||
lastEndOffset = endOffset
|
lastEndOffset = endOffset
|
||||||
|
|
||||||
} else if (node.nodeName === 'ask_followup_question') {
|
} else if (node.nodeName === 'ask_followup_question') {
|
||||||
if (!node.sourceCodeLocation) {
|
if (!node.sourceCodeLocation) {
|
||||||
throw new Error('sourceCodeLocation is undefined')
|
throw new Error('sourceCodeLocation is undefined')
|
||||||
@ -423,8 +427,40 @@ export function parseMsgBlocks(
|
|||||||
question,
|
question,
|
||||||
})
|
})
|
||||||
lastEndOffset = endOffset
|
lastEndOffset = endOffset
|
||||||
}
|
} else if (node.nodeName === 'switch_mode') {
|
||||||
else if (node.nodeName === 'search_web') {
|
if (!node.sourceCodeLocation) {
|
||||||
|
throw new Error('sourceCodeLocation is undefined')
|
||||||
|
}
|
||||||
|
const startOffset = node.sourceCodeLocation.startOffset
|
||||||
|
const endOffset = node.sourceCodeLocation.endOffset
|
||||||
|
if (startOffset > lastEndOffset) {
|
||||||
|
parsedResult.push({
|
||||||
|
type: 'string',
|
||||||
|
content: input.slice(lastEndOffset, startOffset),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let mode: string = ''
|
||||||
|
let reason: string = ''
|
||||||
|
|
||||||
|
for (const childNode of node.childNodes) {
|
||||||
|
if (childNode.nodeName === 'mode_slug' && childNode.childNodes.length > 0) {
|
||||||
|
// @ts-ignore - 忽略 value 属性的类型错误
|
||||||
|
mode = childNode.childNodes[0].value
|
||||||
|
} else if (childNode.nodeName === 'reason' && childNode.childNodes.length > 0) {
|
||||||
|
// @ts-ignore - 忽略 value 属性的类型错误
|
||||||
|
reason = childNode.childNodes[0].value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedResult.push({
|
||||||
|
type: 'switch_mode',
|
||||||
|
mode,
|
||||||
|
reason,
|
||||||
|
finish: node.sourceCodeLocation.endTag !== undefined
|
||||||
|
})
|
||||||
|
lastEndOffset = endOffset
|
||||||
|
} else if (node.nodeName === 'search_web') {
|
||||||
if (!node.sourceCodeLocation) {
|
if (!node.sourceCodeLocation) {
|
||||||
throw new Error('sourceCodeLocation is undefined')
|
throw new Error('sourceCodeLocation is undefined')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import {
|
|||||||
MentionableVault
|
MentionableVault
|
||||||
} from '../types/mentionable'
|
} from '../types/mentionable'
|
||||||
import { InfioSettings } from '../types/settings'
|
import { InfioSettings } from '../types/settings'
|
||||||
import { defaultModeSlug, getFullModeDetails } from "../utils/modes"
|
import { Mode, defaultModeSlug, getFullModeDetails, getModeBySlug } from "../utils/modes"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
readTFileContent
|
readTFileContent
|
||||||
@ -157,8 +157,8 @@ export class PromptGenerator {
|
|||||||
similaritySearchResults,
|
similaritySearchResults,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
console.log('this.settings.mode', this.settings.mode)
|
||||||
const systemMessage = await this.getSystemMessageNew()
|
const systemMessage = await this.getSystemMessageNew(this.settings.mode)
|
||||||
|
|
||||||
const requestMessages: RequestMessage[] = [
|
const requestMessages: RequestMessage[] = [
|
||||||
systemMessage,
|
systemMessage,
|
||||||
@ -225,7 +225,7 @@ export class PromptGenerator {
|
|||||||
details += `\n\n# Current Time\n${formatter.format(now)} (${timeZone}, UTC${timeZoneOffsetStr})`
|
details += `\n\n# Current Time\n${formatter.format(now)} (${timeZone}, UTC${timeZoneOffsetStr})`
|
||||||
|
|
||||||
// Add current mode details
|
// Add current mode details
|
||||||
const currentMode = defaultModeSlug
|
const currentMode = this.settings.mode
|
||||||
const modeDetails = await getFullModeDetails(currentMode)
|
const modeDetails = await getFullModeDetails(currentMode)
|
||||||
details += `\n\n# Current Mode\n`
|
details += `\n\n# Current Mode\n`
|
||||||
details += `<slug>${currentMode}</slug>\n`
|
details += `<slug>${currentMode}</slug>\n`
|
||||||
@ -446,8 +446,8 @@ export class PromptGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getSystemMessageNew(): Promise<RequestMessage> {
|
private async getSystemMessageNew(mode: Mode): Promise<RequestMessage> {
|
||||||
const systemPrompt = await SYSTEM_PROMPT(this.app.vault.getRoot().path, false)
|
const systemPrompt = await SYSTEM_PROMPT(this.app.vault.getRoot().path, false, mode)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
role: 'system',
|
role: 'system',
|
||||||
|
|||||||
@ -493,7 +493,7 @@ button:not(.clickable-icon).infio-chat-list-dropdown {
|
|||||||
/* position: fixed; */
|
/* position: fixed; */
|
||||||
border: 1px solid var(--background-modifier-border);
|
border: 1px solid var(--background-modifier-border);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 240px;
|
min-width: 60px;
|
||||||
max-width: 240px;
|
max-width: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user