Skip to content

Commit a9a72b7

Browse files
authored
feat(cli, nvim): Add files subcommand for file manipulation (#230)
* feat(cli): add `files` subcommand for file manipulation * docs(cli): mark `files` subcommand feature as completed * tests(cli): fix test coverage for existing codebase * test(cli): test for `files ls` * tests(cli): add tests for `files rm` * tests(cli): add tests for `files` subcommand * feat(cli): make sure `files` commands honor `--project_root` * docs(cli): document `files` subcommand in cli.md * Auto generate docs * feat(cli): remove empty collection after removing files * tests(cli): fix broken tests due to wrong mocking * refactor(cli): refactor `files ls` to reuse file listing logic * feat(cli): add `files` subcommand to the LSP server * feat(nvim): add `files_ls` tool to CodeCompanion * fix(cli): improve `files rm` feedback and fix progress handling * feat(nvim): add `files_rm` tool to CodeCompanion * docs(cli): document pipe mode of `files ls` subcommand in cli.md * Auto generate docs * docs(cli): formatting * Auto generate docs --------- Co-authored-by: Davidyz <[email protected]>
1 parent d57bea4 commit a9a72b7

File tree

24 files changed

+859
-74
lines changed

24 files changed

+859
-74
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@ This project follows an adapted semantic versioning:
9494
- [x] implement some sort of project-root anchors (such as `.git` or a custom
9595
`.vectorcode.json`) that enhances automatic project-root detection.
9696
**Implemented project-level `.vectorcode/` and `.git` as root anchor**
97-
- [ ] ability to view and delete files in a collection (atm you can only `drop`
98-
and `vectorise` again);
97+
- [x] ability to view and delete files in a collection;
9998
- [x] joint search (kinda, using codecompanion.nvim/MCP);
10099
- [x] Nix support (unofficial packages [here](https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=vectorcode));
101100
- [ ] Query rewriting (#124).

doc/VectorCode-cli.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Table of Contents *VectorCode-cli-table-of-contents*
3535
- |VectorCode-cli-removing-a-collection|
3636
- |VectorCode-cli-checking-project-setup|
3737
- |VectorCode-cli-cleaning-up|
38+
- |VectorCode-cli-inspecting-and-manupulating-files-in-an-indexed-project|
3839
- |VectorCode-cli-debugging-and-diagnosing|
3940
- |VectorCode-cli-shell-completion|
4041
- |VectorCode-cli-hardware-acceleration|
@@ -43,6 +44,7 @@ Table of Contents *VectorCode-cli-table-of-contents*
4344
- |VectorCode-cli-`vectorcode-query`|
4445
- |VectorCode-cli-`vectorcode-vectorise`|
4546
- |VectorCode-cli-`vectorcode-ls`|
47+
- |VectorCode-cli-`vectorcode-files-ls`|
4648
- |VectorCode-cli-lsp-mode|
4749
- |VectorCode-cli-mcp-server|
4850
- |VectorCode-cli-writing-prompts|
@@ -556,6 +558,15 @@ For empty collections and collections for removed projects, you can use the
556558
`vectorcode clean` command to remove them at once.
557559

558560

561+
INSPECTING AND MANUPULATING FILES IN AN INDEXED PROJECT ~
562+
563+
- `vectorcode files ls` prints a list of files that are indexed in the project.
564+
- `vectorcode files rm file1 file2` removes the embeddings that belong to the
565+
specified files from the project.
566+
567+
Both commands will honor the `--project_root` flag.
568+
569+
559570
DEBUGGING AND DIAGNOSING ~
560571

561572
When something doesn’t work as expected, you can enable logging by setting
@@ -718,6 +729,11 @@ A JSON array of collection information of the following format will be printed:
718729
- `"num_files"`number of files that have been vectorised in the project.
719730

720731

732+
VECTORCODE FILES LS
733+
734+
A JSON array of strings (the absolute paths to the files in the collection).
735+
736+
721737
LSP MODE ~
722738

723739
There’s an experimental implementation of VectorCode CLI, which accepts

docs/cli.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* [Removing a Collection](#removing-a-collection)
2525
* [Checking Project Setup](#checking-project-setup)
2626
* [Cleaning up](#cleaning-up)
27+
* [Inspecting and Manupulating Files in an Indexed Project](#inspecting-and-manupulating-files-in-an-indexed-project)
2728
* [Debugging and Diagnosing](#debugging-and-diagnosing)
2829
* [Shell Completion](#shell-completion)
2930
* [Hardware Acceleration](#hardware-acceleration)
@@ -32,6 +33,7 @@
3233
* [`vectorcode query`](#vectorcode-query)
3334
* [`vectorcode vectorise`](#vectorcode-vectorise)
3435
* [`vectorcode ls`](#vectorcode-ls)
36+
* [`vectorcode files ls`](#vectorcode-files-ls)
3537
* [LSP Mode](#lsp-mode)
3638
* [MCP Server](#mcp-server)
3739
* [Writing Prompts](#writing-prompts)
@@ -507,6 +509,14 @@ some_message` and then getting an empty results.
507509
For empty collections and collections for removed projects, you can use the
508510
`vectorcode clean` command to remove them at once.
509511

512+
### Inspecting and Manupulating Files in an Indexed Project
513+
514+
- `vectorcode files ls` prints a list of files that are indexed in the project.
515+
- `vectorcode files rm file1 file2` removes the embeddings that belong to the
516+
specified files from the project.
517+
518+
Both commands will honor the `--project_root` flag.
519+
510520
### Debugging and Diagnosing
511521

512522
When something doesn't work as expected, you can enable logging by setting the
@@ -647,6 +657,10 @@ A JSON array of collection information of the following format will be printed:
647657
- `"size"`: number of chunks stored in the database;
648658
- `"num_files"`: number of files that have been vectorised in the project.
649659
660+
#### `vectorcode files ls`
661+
662+
A JSON array of strings (the absolute paths to the files in the collection).
663+
650664
### LSP Mode
651665
652666
There's an experimental implementation of VectorCode CLI, which accepts requests

lua/codecompanion/_extensions/vectorcode/init.lua

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---@module "codecompanion"
22

3-
---@alias sub_cmd "ls"|"query"|"vectorise"
3+
---@alias sub_cmd "ls"|"query"|"vectorise"|"files_ls"|"files_rm"
44

55
---@class VectorCode.CodeCompanion.ExtensionOpts
66
--- A table where the keys are the subcommand name (`ls`, `query`, `vectorise`)
@@ -17,15 +17,29 @@ local use_lsp = vc_config.get_user_config().async_backend == "lsp"
1717
---@type VectorCode.CodeCompanion.ExtensionOpts|{}
1818
local default_extension_opts = {
1919
tool_opts = {
20-
ls = { use_lsp = use_lsp, requires_approval = false },
21-
query = { use_lsp = use_lsp, requires_approval = false },
22-
vectorise = { use_lsp = use_lsp, requires_approval = true },
20+
ls = { use_lsp = use_lsp, requires_approval = false, include_in_toolbox = true },
21+
query = { use_lsp = use_lsp, requires_approval = false, include_in_toolbox = true },
22+
vectorise = {
23+
use_lsp = use_lsp,
24+
requires_approval = true,
25+
include_in_toolbox = true,
26+
},
27+
files_ls = {
28+
use_lsp = use_lsp,
29+
requires_approval = false,
30+
include_in_toolbox = false,
31+
},
32+
files_rm = {
33+
use_lsp = use_lsp,
34+
requires_approval = true,
35+
include_in_toolbox = false,
36+
},
2337
},
2438
tool_group = { enabled = true, collapse = true, extras = {} },
2539
}
2640

2741
---@type sub_cmd[]
28-
local valid_tools = { "ls", "query", "vectorise" }
42+
local valid_tools = { "ls", "query", "vectorise", "files_ls", "files_rm" }
2943

3044
---@type CodeCompanion.Extension
3145
local M = {
@@ -65,6 +79,9 @@ local M = {
6579
if opts.tool_group.enabled then
6680
local included_tools = vim
6781
.iter(valid_tools)
82+
:filter(function(cmd_name)
83+
return opts.tool_opts[cmd_name].include_in_toolbox
84+
end)
6885
:map(function(s)
6986
return "vectorcode_" .. s
7087
end)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---@module "codecompanion"
2+
3+
local cc_common = require("vectorcode.integrations.codecompanion.common")
4+
5+
---@param opts VectorCode.CodeCompanion.FilesLsToolOpts
6+
---@return CodeCompanion.Agent.Tool
7+
return function(opts)
8+
local job_runner =
9+
require("vectorcode.integrations.codecompanion.common").initialise_runner(
10+
opts.use_lsp
11+
)
12+
local tool_name = "vectorcode_files_ls"
13+
---@type CodeCompanion.Agent.Tool|{}
14+
return {
15+
name = tool_name,
16+
cmds = {
17+
---@param agent CodeCompanion.Agent
18+
---@param action {project_root: string}
19+
---@return nil|{ status: string, data: string }
20+
function(agent, action, _, cb)
21+
local args = { "files", "ls", "--pipe" }
22+
if action ~= nil then
23+
action.project_root = action.project_root
24+
or vim.fs.root(0, { ".vectorcode", ".git" })
25+
if action.project_root ~= nil then
26+
action.project_root = vim.fs.normalize(action.project_root)
27+
local stat = vim.uv.fs_stat(action.project_root)
28+
if stat and stat.type == "directory" then
29+
vim.list_extend(args, { "--project_root", action.project_root })
30+
end
31+
end
32+
end
33+
job_runner.run_async(args, function(result, error)
34+
if vim.islist(result) and #result > 0 then
35+
cb({ status = "success", data = result })
36+
else
37+
if type(error) == "table" then
38+
error = cc_common.flatten_table_to_string(error)
39+
end
40+
cb({
41+
status = "error",
42+
data = error,
43+
})
44+
end
45+
end, agent.chat.bufnr)
46+
end,
47+
},
48+
schema = {
49+
type = "function",
50+
["function"] = {
51+
name = tool_name,
52+
description = "Retrieve a list of files that have been added to the database for a given project.",
53+
parameters = {
54+
type = "object",
55+
properties = {
56+
project_root = {
57+
type = "string",
58+
description = "The project for which the indexed files will be listed. Leave this empty for the current project.",
59+
},
60+
},
61+
},
62+
},
63+
},
64+
output = {
65+
---@param agent CodeCompanion.Agent
66+
---@param stdout string[][]
67+
success = function(_, agent, _, stdout)
68+
stdout = stdout[1]
69+
local user_message
70+
for i, col in ipairs(stdout) do
71+
if i == 1 then
72+
user_message =
73+
string.format("**VectorCode `files_ls` Tool**: Found %d files.", #stdout)
74+
else
75+
user_message = ""
76+
end
77+
agent.chat:add_tool_output(
78+
agent.tool,
79+
string.format("<path>%s</path>", col),
80+
user_message
81+
)
82+
end
83+
end,
84+
},
85+
}
86+
end
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---@module "codecompanion"
2+
3+
local cc_common = require("vectorcode.integrations.codecompanion.common")
4+
5+
---@alias FilesRmArgs { paths: string[], project_root: string }
6+
7+
---@param opts VectorCode.CodeCompanion.FilesRmToolOpts
8+
---@return CodeCompanion.Agent.Tool
9+
return function(opts)
10+
local tool_name = "vectorcode_files_rm"
11+
local job_runner = cc_common.initialise_runner(opts.use_lsp)
12+
13+
---@type CodeCompanion.Agent.Tool|{}
14+
return {
15+
name = tool_name,
16+
schema = {
17+
type = "function",
18+
["function"] = {
19+
name = tool_name,
20+
description = "Remove files from the VectorCode database. The files will remain in the file system.",
21+
parameters = {
22+
type = "object",
23+
properties = {
24+
paths = {
25+
type = "array",
26+
items = { type = "string" },
27+
description = "Paths to the files to be removed from the database.",
28+
},
29+
project_root = {
30+
type = "string",
31+
description = "The project that the files belong to. Either use a path from the `vectorcode_ls` tool, or leave empty to use the current git project. If the user did not specify a path, use empty string for this parameter.",
32+
},
33+
},
34+
required = { "paths", "project_root" },
35+
additionalProperties = false,
36+
},
37+
strict = true,
38+
},
39+
},
40+
cmds = {
41+
---@param agent CodeCompanion.Agent
42+
---@param action VectoriseToolArgs
43+
---@return nil|{ status: string, data: string }
44+
function(agent, action, _, cb)
45+
local args = { "files", "rm", "--pipe" }
46+
local project_root = vim.fs.abspath(vim.fs.normalize(action.project_root or ""))
47+
if project_root ~= "" then
48+
local stat = vim.uv.fs_stat(project_root)
49+
if stat and stat.type == "directory" then
50+
vim.list_extend(args, { "--project_root", project_root })
51+
else
52+
return { status = "error", data = "Invalid path " .. project_root }
53+
end
54+
else
55+
project_root = vim.fs.root(".", { ".vectorcode", ".git" }) or ""
56+
if project_root == "" then
57+
return {
58+
status = "error",
59+
data = "Please specify a project root. You may use the `vectorcode_ls` tool to find a list of existing projects.",
60+
}
61+
end
62+
end
63+
if project_root ~= "" then
64+
action.project_root = project_root
65+
end
66+
vim.list_extend(
67+
args,
68+
vim
69+
.iter(action.paths)
70+
:filter(
71+
---@param item string
72+
function(item)
73+
local stat = vim.uv.fs_stat(item)
74+
if stat and stat.type == "file" then
75+
return true
76+
else
77+
return false
78+
end
79+
end
80+
)
81+
:totable()
82+
)
83+
job_runner.run_async(
84+
args,
85+
---@param result VectoriseResult
86+
function(result, error, code, _)
87+
if result then
88+
cb({ status = "success", data = result })
89+
else
90+
cb({ status = "error", data = { error = error, code = code } })
91+
end
92+
end,
93+
agent.chat.bufnr
94+
)
95+
end,
96+
},
97+
output = {
98+
---@param self CodeCompanion.Agent.Tool
99+
prompt = function(self, _)
100+
return string.format(
101+
"Remove %d files from VectorCode database?",
102+
#self.args.paths
103+
)
104+
end,
105+
---@param self CodeCompanion.Agent.Tool
106+
---@param agent CodeCompanion.Agent
107+
success = function(self, agent, _, _)
108+
agent.chat:add_tool_output(self, "**VectorCode `files_rm` tool**: successful.")
109+
end,
110+
},
111+
}
112+
end

lua/vectorcode/integrations/codecompanion/init.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ return {
3535
}
3636
end),
3737

38-
---@param subcommand "ls"|"query"|"vectorise"
38+
---@param subcommand sub_cmd
3939
---@param opts VectorCode.CodeCompanion.ToolOpts
4040
---@return CodeCompanion.Agent.Tool
4141
make_tool = function(subcommand, opts)

lua/vectorcode/types.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,15 @@
7676
--- Whether to use the LSP backend. Default: `false`
7777
---@field use_lsp boolean?
7878
---@field requires_approval boolean?
79+
--- Whether this tool should be included in `vectorcode_toolbox`
80+
---@field include_in_toolbox boolean?
7981

8082
---@class VectorCode.CodeCompanion.LsToolOpts: VectorCode.CodeCompanion.ToolOpts
8183

84+
---@class VectorCode.CodeCompanion.FilesLsToolOpts: VectorCode.CodeCompanion.ToolOpts
85+
86+
---@class VectorCode.CodeCompanion.FilesRmToolOpts: VectorCode.CodeCompanion.ToolOpts
87+
8288
---@class VectorCode.CodeCompanion.QueryToolOpts: VectorCode.CodeCompanion.ToolOpts
8389
--- Maximum number of results provided to the LLM.
8490
--- You may set this to a table to configure different values for document/chunk mode.

0 commit comments

Comments
 (0)