Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion doc/VectorCode-cli.txt
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,10 @@ If you want to integrate VectorCode in your LLM application, you may want to
write some prompt that tells the LLM how to use this tool. Apart from the
function signatures, a list of instructions used by the MCP server is included
in this package. This can be retrieved by running the `vectorcode prompts`
command.
command. This commands optionally accepts names of other subcommands as
arguments. It’ll print a list of pre-defined prompts that are suitable for
the specified subcommands. You may run `vectorcode prompts --help` for the
supported options.

Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>

Expand Down
3 changes: 3 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -719,3 +719,6 @@ If you want to integrate VectorCode in your LLM application, you may want to
write some prompt that tells the LLM how to use this tool. Apart from the
function signatures, a list of instructions used by the MCP server is included in
this package. This can be retrieved by running the `vectorcode prompts` command.
This commands optionally accepts names of other subcommands as arguments. It'll
print a list of pre-defined prompts that are suitable for the specified
subcommands. You may run `vectorcode prompts --help` for the supported options.
14 changes: 12 additions & 2 deletions lua/vectorcode/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,19 @@ function M.check(check_item, stdout_cb)
return return_code == 0
end

---@alias prompt_type "ls"|"query"|"vectorise"
---@param item prompt_type|prompt_type[]|nil
---@return string[]
M.prompts = vc_config.check_cli_wrap(function()
local result, error = jobrunner.run({ "prompts", "-p" }, -1, 0)
M.prompts = vc_config.check_cli_wrap(function(item)
local args = { "prompts", "-p" }
if item then
if type(item) == "string" then
table.insert(args, item)
else
vim.list_extend(args, item)
end
end
local result, error = jobrunner.run(args, -1, 0)
if result == nil or vim.tbl_isempty(result) then
logger.warn(vim.inspect(error))
if vc_config.get_user_config().notify then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ return check_cli_wrap(function(opts)
guidelines,
vim.tbl_map(function(line)
return " - " .. line
end, require("vectorcode").prompts())
end, require("vectorcode").prompts({ "query", "ls" }))
)
if opts.ls_on_start then
job_runner = cc_common.initialise_runner(opts.use_lsp)
Expand Down
19 changes: 18 additions & 1 deletion src/vectorcode/cli_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ def to_header(self) -> str:
return f"{self.value.capitalize()}: "


class PromptCategory(StrEnum):
query = "query"
vectorise = "vectorise"
ls = "ls"


class CliAction(Enum):
vectorise = "vectorise"
query = "query"
Expand Down Expand Up @@ -96,6 +102,7 @@ class Config:
filetype_map: dict[str, list[str]] = field(default_factory=dict)
encoding: str = "utf8"
hooks: bool = False
prompt_categories: Optional[list[str]] = None

@classmethod
async def import_from(cls, config_dict: dict[str, Any]) -> "Config":
Expand Down Expand Up @@ -344,11 +351,19 @@ def get_cli_parser():
help="Remove empty collections in the database.",
)

subparsers.add_parser(
prompts_parser = subparsers.add_parser(
"prompts",
parents=[shared_parser],
help="Print a list of guidelines intended to be used as system prompts for an LLM.",
)
prompts_parser.add_argument(
"prompt_categories",
choices=PromptCategory,
type=PromptCategory,
nargs="*",
help="The subcommand(s) to get the prompts for. When not provided, VectorCode will print the prompts for `query`.",
default=None,
)

chunks_parser = subparsers.add_parser(
"chunks",
Expand Down Expand Up @@ -400,6 +415,8 @@ async def parse_cli_args(args: Optional[Sequence[str]] = None):
configs_items["chunk_size"] = main_args.chunk_size
configs_items["overlap_ratio"] = main_args.overlap
configs_items["encoding"] = main_args.encoding
case "prompts":
configs_items["prompt_categories"] = main_args.prompt_categories
return Config(**configs_items)


Expand Down
6 changes: 4 additions & 2 deletions src/vectorcode/mcp_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
load_config_file,
)
from vectorcode.common import get_client, get_collection, get_collections
from vectorcode.subcommands.prompt import prompt_strings
from vectorcode.subcommands.prompt import prompt_by_categories
from vectorcode.subcommands.query import get_query_result_files

logger = logging.getLogger(name=__name__)
Expand Down Expand Up @@ -167,7 +167,9 @@ async def mcp_server():
except InvalidCollectionException: # pragma: nocover
default_collection = None

default_instructions = "\n".join(prompt_strings)
default_instructions = "\n".join(
"\n".join(i) for i in prompt_by_categories.values()
)
if default_client is None:
if mcp_config.ls_on_start: # pragma: nocover
logger.warning(
Expand Down
56 changes: 38 additions & 18 deletions src/vectorcode/subcommands/prompt.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
import json
import logging

from vectorcode.cli_utils import Config
from vectorcode.cli_utils import Config, PromptCategory

prompt_strings = [
"**Use at your discretion** when you feel you don't have enough information about the repository or project",
"**Don't escape** special characters",
"separate phrases into distinct keywords when appropriate",
"If a class, type or function has been imported from another file, this tool may be able to find its source. Add the name of the imported symbol to the query",
"Avoid retrieving one single file because the retrieval mechanism may not be very accurate",
"When providing answers based on VectorCode results, try to give references such as paths to files and line ranges, unless you're told otherwise (but do not include the full source code context)",
"VectorCode is the name of this tool. Do not include it in the query unless the user explicitly asks",
"If the retrieval results do not contain the needed context, increase the file count so that the result will more likely contain the desired files",
"If the returned paths are relative, they are relative to the root of the project directory",
"Do not suggest edits to retrieved files that are outside of the current working directory, unless the user instructed otherwise",
"When specifying the `project_root` parameter when making a query, make sure you run the `ls` tool first to retrieve a list of valid, indexed projects",
"If a query failed to retrieve desired results, a new attempt should use different keywords that are orthogonal to the previous ones but with similar meanings",
"Do not use exact query keywords that you have used in a previous tool call in the conversation, unless the user instructed otherwise, or with different count/project_root",
]
logger = logging.getLogger(name=__name__)

prompt_by_categories: dict[str | PromptCategory, list[str]] = {
PromptCategory.query: [
"separate phrases into distinct keywords when appropriate",
"If a class, type or function has been imported from another file, this tool may be able to find its source. Add the name of the imported symbol to the query",
"When providing answers based on VectorCode results, try to give references such as paths to files and line ranges, unless you're told otherwise (but do not include the full source code context)",
"Avoid retrieving one single file because the retrieval mechanism may not be very accurate",
"If the query results do not contain the needed context, increase the file count so that the result will more likely contain the desired files",
"If the returned paths are relative, they are relative to the root of the project directory",
"Do not suggest edits to retrieved files that are outside of the current working directory, unless the user instructed otherwise",
"When specifying the `project_root` parameter when making a query, make sure you run the `ls` tool first to retrieve a list of valid, indexed projects",
"If a query failed to retrieve desired results, a new attempt should use different keywords that are orthogonal to the previous ones but with similar meanings",
"Do not use exact query keywords that you have used in a previous tool call in the conversation, unless the user instructed otherwise, or with different count/project_root",
"Include related keywords as the search query. For example, when querying for `function`, include `return value`, `parameter`, `arguments` and alike.",
],
PromptCategory.ls: [
"Use `ls` tool to obtain a list of indexed projects that are available to be queried by the `query` command."
],
PromptCategory.vectorise: [
"When vectorising the files, provide accurate and case-sensitive paths to the file"
],
"general": [
"VectorCode is the name of this tool. Do not include it in the query unless the user explicitly asks",
"**Use at your discretion** when you feel you don't have enough information about the repository or project",
"**Don't escape** special characters",
],
}
prompt_strings = []


def prompts(configs: Config) -> int:
results = prompt_by_categories["general"].copy()
for item in sorted(set(configs.prompt_categories or [PromptCategory.query])):
logger.info(f"Loading {len(prompt_by_categories[item])} prompts for {item}")
results.extend(prompt_by_categories[item])
results.sort()
if configs.pipe:
print(json.dumps(prompt_strings))
print(json.dumps(results))
else:
for i in prompt_strings:
for i in results:
print(f"- {i}")
return 0
12 changes: 7 additions & 5 deletions tests/subcommands/test_prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
import json
import sys

from vectorcode.cli_utils import Config
from vectorcode.cli_utils import Config, PromptCategory
from vectorcode.subcommands import prompt


def test_prompts_pipe_true():
configs = Config(pipe=True)
configs = Config(pipe=True, prompt_categories=PromptCategory)

# Mock stdout
captured_output = io.StringIO()
Expand All @@ -17,13 +17,15 @@ def test_prompts_pipe_true():

sys.stdout = sys.__stdout__ # Reset stdout

expected_output = json.dumps(prompt.prompt_strings) + "\n"
expected_output = (
json.dumps(sorted(sum(prompt.prompt_by_categories.values(), start=[]))) + "\n"
)
assert captured_output.getvalue() == expected_output
assert return_code == 0


def test_prompts_pipe_false():
configs = Config(pipe=False)
configs = Config(pipe=False, prompt_categories=PromptCategory)

# Mock stdout
captured_output = io.StringIO()
Expand All @@ -34,7 +36,7 @@ def test_prompts_pipe_false():
sys.stdout = sys.__stdout__ # Reset stdout

expected_output = ""
for i in prompt.prompt_strings:
for i in sorted(sum(prompt.prompt_by_categories.values(), start=[])):
expected_output += f"- {i}\n"

assert captured_output.getvalue() == expected_output
Expand Down
9 changes: 9 additions & 0 deletions tests/test_cli_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from vectorcode.cli_utils import (
CliAction,
Config,
PromptCategory,
QueryInclude,
cleanup_path,
expand_envs_in_dict,
Expand Down Expand Up @@ -482,6 +483,14 @@ async def test_parse_cli_args_init():
assert config.action == CliAction.init


@pytest.mark.asyncio
async def test_parse_cli_args_prompts():
with patch("sys.argv", ["vectorcode", "prompts", "ls"]):
config = await parse_cli_args()
assert config.action == CliAction.prompts
assert config.prompt_categories == [PromptCategory.ls]


@pytest.mark.asyncio
async def test_parse_cli_args_chunks():
with patch(
Expand Down