@javierkaiser/opencode-indexing 0.2.4 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -1
- package/README.md +95 -25
- package/THIRD_PARTY_NOTICES.md +25 -0
- package/index.ts +18 -0
- package/install.mjs +15 -5
- package/install.ps1 +8 -2
- package/install.sh +5 -1
- package/package.json +1 -1
- package/src/codegraph.ts +265 -0
- package/src/config.ts +8 -0
- package/src/scanner.ts +8 -2
- package/src/settings-commands.ts +8 -0
- package/src/settings.ts +2 -0
- package/src/tools.ts +46 -5
- package/src/types.ts +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,106 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.1] - 2026-10-16
|
|
9
|
+
|
|
10
|
+
### Documentation
|
|
11
|
+
- The README's code-graph section is no longer a roadmap. It said the plugin
|
|
12
|
+
"deliberately stays on the semantic side" and offered `indexing_graph_*` tools
|
|
13
|
+
as a future major version — both untrue once codegraph became a companion.
|
|
14
|
+
Rewritten around the two indexes: what each costs to keep fresh, what the
|
|
15
|
+
benchmark does and does not measure, how to move from one to the other, and an
|
|
16
|
+
explicit note that the graph tools are withdrawn rather than pending. The
|
|
17
|
+
install-time detail moved to *Install*, where `--with-codegraph` already
|
|
18
|
+
lives, instead of being restated here.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **The installer named the wrong package.** `install.mjs` and the skip message
|
|
22
|
+
in `src/codegraph.ts` both said `codegraph`, which on npm is an unrelated 2024
|
|
23
|
+
library by a different author with no binary at all. `npm install -g codegraph`
|
|
24
|
+
exits 0, puts nothing on `PATH`, and the step then reports success — the worst
|
|
25
|
+
shape of failure for an installer. The real package is
|
|
26
|
+
`@colbymchenry/codegraph`, which is what the CLI already on this machine came
|
|
27
|
+
from. Both call sites now use the scoped name, and a test reads `install.mjs`
|
|
28
|
+
and asserts the unscoped install never appears.
|
|
29
|
+
- **The PowerShell wrapper could not pass `--with-codegraph`.** `install.mjs`
|
|
30
|
+
implemented the flag and `install.sh` forwards every argument, but
|
|
31
|
+
`install.ps1` declared only five switches, so on Windows the recommended
|
|
32
|
+
install was impossible through the wrapper. The switch exists now, and both
|
|
33
|
+
wrappers record the preferred `npx` form.
|
|
34
|
+
- The prompt-footer skill now tells the model that codegraph exists. It listed
|
|
35
|
+
`Grep` as the answer for exact symbols and never mentioned the graph tools at
|
|
36
|
+
all, so an assistant had no way to know the structural half was available —
|
|
37
|
+
the same class of problem as an indicator that renders nothing: the capability
|
|
38
|
+
was there and invisible. The guidance is conditional on the MCP server being
|
|
39
|
+
present, because it is optional.
|
|
40
|
+
- `THIRD_PARTY_NOTICES.md` was missing four packages it distributes:
|
|
41
|
+
`solid-js`, `@opentui/core`, `@opentui/solid` (shipped since 0.2.4) and
|
|
42
|
+
`apache-arrow`, which had never been listed. Licenses read from the installed
|
|
43
|
+
manifests, not from memory. CodeGraph gets its own section: not bundled, MIT
|
|
44
|
+
per its manifest, and the license text is linked rather than reproduced
|
|
45
|
+
because the package ships no `LICENSE` file and declares no author, so there
|
|
46
|
+
is no copyright line this project can verify.
|
|
47
|
+
- *How it works* and *Where each index lives* now show codegraph. The diagram
|
|
48
|
+
had one index and the table had two; there are three.
|
|
49
|
+
- `indexing_search` and `indexing_status` descriptions mention the companion.
|
|
50
|
+
|
|
51
|
+
### CI
|
|
52
|
+
- The release workflow now creates the GitHub Release itself, attaching the
|
|
53
|
+
tarball that was actually published and taking its notes from this file. It
|
|
54
|
+
also stopped hiding its own failures: the download ran under `--silent`
|
|
55
|
+
inside a `pipefail` pipeline, so a registry hiccup failed the step with no
|
|
56
|
+
output at all to explain it — which is how three versions shipped without a
|
|
57
|
+
release page.
|
|
58
|
+
- Dependabot groups only what can move together. The old type-based groups
|
|
59
|
+
paired dependencies that contradict each other (`apache-arrow` against
|
|
60
|
+
`@lancedb/lancedb`'s peer ceiling, `@opencode/plugin` against the pinned
|
|
61
|
+
`@opentui` packages), and `npm ci` failed with ERESOLVE before any test ran.
|
|
62
|
+
|
|
63
|
+
## [0.3.0] - 2026-10-16
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
- **`indexing_build` and `indexing_import` now also start codegraph** for the
|
|
67
|
+
workspace, so the semantic index and the code graph are built by the same
|
|
68
|
+
action instead of one silently going stale. `init` the first time a folder is
|
|
69
|
+
seen, `sync` afterwards — the second is what covers the case where codegraph's
|
|
70
|
+
daemon has already shut down, which it does after five minutes without
|
|
71
|
+
clients.
|
|
72
|
+
|
|
73
|
+
**codegraph is a companion, not a dependency.** Nothing about this plugin
|
|
74
|
+
requires it: with the CLI absent, `available()` returns false, nothing is
|
|
75
|
+
spawned, no build fails, and no line is added to the output. That path is
|
|
76
|
+
covered by a test that hides codegraph from `PATH` and runs the real spawner,
|
|
77
|
+
not a fake — an injected runner would have proved the logic while leaving the
|
|
78
|
+
wiring unchecked.
|
|
79
|
+
|
|
80
|
+
- `codegraph` setting, on by default (`/indexing-config set codegraph=false` to
|
|
81
|
+
turn it off). Read as `!== false`, because the key is in no config file anyone
|
|
82
|
+
has and a default that required it to be present would never apply.
|
|
83
|
+
|
|
84
|
+
- One line in `indexing_status` saying whether codegraph is indexed for the
|
|
85
|
+
folder. Read from the filesystem, so it costs no process — status runs on
|
|
86
|
+
every search through the footer.
|
|
87
|
+
|
|
88
|
+
### Changed
|
|
89
|
+
- The build summary reports the codegraph command it started, and always in the
|
|
90
|
+
past tense of a *launch*, never of a success: the process is detached, so at
|
|
91
|
+
the moment the tool answers, codegraph has only begun. Anything that goes
|
|
92
|
+
wrong afterwards lands in `.codegraph/daemon.log`, which is where the message
|
|
93
|
+
points.
|
|
94
|
+
|
|
95
|
+
### Notes for review
|
|
96
|
+
- A dangerous workspace root — the home directory or a drive root — is refused
|
|
97
|
+
rather than indexed. codegraph has `-f` for exactly that case, and passing it
|
|
98
|
+
because a tool call said so would index the whole machine.
|
|
99
|
+
- Launches are rate-limited per workspace to one per 15 seconds, because
|
|
100
|
+
codegraph runs its own daemon with a file watcher and two `sync` invocations
|
|
101
|
+
seconds apart contend for the same SQLite database.
|
|
102
|
+
- `.codegraph/` was already excluded from the scanner (every dot-directory that
|
|
103
|
+
is not explicitly allowed is skipped), and there is now a test pinning it:
|
|
104
|
+
codegraph writes a multi-megabyte SQLite database plus a WAL there, and
|
|
105
|
+
letting the scanner walk it would embed another indexer's storage into this
|
|
106
|
+
one, for money, forever.
|
|
107
|
+
|
|
8
108
|
## [0.2.4] - 2026-10-16
|
|
9
109
|
|
|
10
110
|
### Fixed
|
|
@@ -237,7 +337,12 @@ First public release.
|
|
|
237
337
|
- Cross-platform installer (`install.mjs`, plus `install.sh` / `install.ps1`
|
|
238
338
|
wrappers).
|
|
239
339
|
|
|
240
|
-
[Unreleased]: https://github.com/Javierkaiser/opencode-indexing/compare/v0.
|
|
340
|
+
[Unreleased]: https://github.com/Javierkaiser/opencode-indexing/compare/v0.3.1...HEAD
|
|
341
|
+
[0.3.1]: https://github.com/Javierkaiser/opencode-indexing/releases/tag/v0.3.1
|
|
342
|
+
[0.3.0]: https://github.com/Javierkaiser/opencode-indexing/releases/tag/v0.3.0
|
|
343
|
+
[0.2.4]: https://github.com/Javierkaiser/opencode-indexing/releases/tag/v0.2.4
|
|
344
|
+
[0.2.3]: https://github.com/Javierkaiser/opencode-indexing/releases/tag/v0.2.3
|
|
345
|
+
[0.2.1]: https://github.com/Javierkaiser/opencode-indexing/releases/tag/v0.2.1
|
|
241
346
|
[0.2.0]: https://github.com/Javierkaiser/opencode-indexing/releases/tag/v0.2.0
|
|
242
347
|
[0.1.1]: https://github.com/Javierkaiser/opencode-indexing/releases/tag/v0.1.1
|
|
243
348
|
[0.1.0]: https://github.com/Javierkaiser/opencode-indexing/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -55,6 +55,36 @@ npx @javierkaiser/opencode-indexing --with-codegraph
|
|
|
55
55
|
| `--no-restart` | Do not restart the service |
|
|
56
56
|
| `--with-codegraph` | Install and register codegraph as a companion MCP (opt-in, `--package` only) |
|
|
57
57
|
|
|
58
|
+
### The codegraph companion
|
|
59
|
+
|
|
60
|
+
[CodeGraph](https://github.com/colbymchenry/codegraph) is a separate tool — a
|
|
61
|
+
local code-graph indexer exposed as an MCP server — and this plugin drives it
|
|
62
|
+
rather than reimplementing it. If it is not installed yet:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install -g @colbymchenry/codegraph
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The package is scoped, and that matters: `codegraph` unscoped on npm is an
|
|
69
|
+
unrelated 2024 library with no CLI, so installing that name exits 0 and leaves
|
|
70
|
+
nothing on `PATH`.
|
|
71
|
+
|
|
72
|
+
Installing it is only half of it. `indexing_build` and `indexing_import` also
|
|
73
|
+
start the CLI for the workspace, so both indexes are built by the same action:
|
|
74
|
+
`codegraph init --yes` the first time a folder is seen, `codegraph sync`
|
|
75
|
+
afterwards. codegraph is a **companion, not a dependency** — with the CLI absent
|
|
76
|
+
nothing is spawned, no build fails, and no line is added to the output. Turn it
|
|
77
|
+
off with `/indexing-config set codegraph=false`, and see
|
|
78
|
+
[Semantic search and code graphs](#semantic-search-and-code-graphs) for why the
|
|
79
|
+
two belong together.
|
|
80
|
+
|
|
81
|
+
The launch is detached and reported as started, never as finished: on a large
|
|
82
|
+
monorepo `init` runs for minutes, and a tool call that blocked that long would
|
|
83
|
+
be worse than one that returns. Anything that fails afterwards lands in
|
|
84
|
+
`.codegraph/daemon.log`. A home directory or a drive root is refused rather than
|
|
85
|
+
indexed, and launches are limited to one per 15 seconds per workspace because
|
|
86
|
+
codegraph's own daemon already watches files.
|
|
87
|
+
|
|
58
88
|
### With the OpenCode CLI
|
|
59
89
|
|
|
60
90
|
```bash
|
|
@@ -82,7 +112,7 @@ The other six tools only read. OpenCode's base policy already allows everything
|
|
|
82
112
|
write them out only if you want to tighten the set.
|
|
83
113
|
|
|
84
114
|
`install.sh` and `install.ps1` are thin wrappers over it; PowerShell also accepts
|
|
85
|
-
`-Local`, `-Package`, `-Spec`, `-SkipDeps` and `-
|
|
115
|
+
`-Local`, `-Package`, `-Spec`, `-SkipDeps`, `-NoRestart` and `-WithCodegraph`.
|
|
86
116
|
|
|
87
117
|
`--package` deletes any copied plugin it finds: both sources define the same
|
|
88
118
|
plugin id and the config directory is scanned automatically, so leaving both
|
|
@@ -108,12 +138,17 @@ resolve from the config file containing the entry:
|
|
|
108
138
|
```
|
|
109
139
|
OpenCode ──► indexing_search ──┬──► Kilo's index (read-only) Qdrant ws-* · Kilo LanceDB
|
|
110
140
|
└──► own index (read/write) own LanceDB (default) · Qdrant oc-*
|
|
141
|
+
|
|
142
|
+
OpenCode ──► codegraph_* ───────► codegraph's graph (its own) .codegraph/codegraph.db
|
|
143
|
+
▲
|
|
144
|
+
└── indexing_build / indexing_import keep it in step
|
|
111
145
|
```
|
|
112
146
|
|
|
113
147
|
- **Read-only reuse**: the plugin computes the same collection name Kilo uses (`ws-` + sha256 of the workspace path, first 16 hex chars), validates the embedding profile stored in the metadata point, and queries with an embedding from the *same* model (e.g. Mistral `codestral-embed-2505`, 1536d).
|
|
114
148
|
- **Import without re-embedding**: `indexing_build` (or `indexing_import`) can copy vectors + chunks from an existing Kilo index — both its Qdrant collections and its local LanceDB database — at **zero embedding cost**, then incrementally index only the delta.
|
|
115
149
|
- **Own index**: manifest-based incremental indexer (per-file sha256) writing to `oc-*` collections or an embedded LanceDB database, using the same chunk payload shape as Kilo (`filePath`, `fileHash`, `codeChunk`, `startLine`, `endLine`, `segmentHash`, `pathSegments`).
|
|
116
150
|
- **Merge**: results from Kilo + own are merged; own (fresher) hits win on overlap; ghost hits (deleted files) are filtered out.
|
|
151
|
+
- **Companion graph**: the same build starts codegraph for the workspace, which keeps its own index in `.codegraph/` and serves it over its own MCP server. This plugin never reads or writes that database — see [Semantic search and code graphs](#semantic-search-and-code-graphs).
|
|
117
152
|
|
|
118
153
|
## Tools (namespace `indexing`)
|
|
119
154
|
|
|
@@ -155,6 +190,7 @@ Settable keys (case-insensitive):
|
|
|
155
190
|
| `ollamaurl` | Ollama base URL |
|
|
156
191
|
| `openaicompatibleurl` / `openaicompatiblekey` | OpenAI-compatible endpoint |
|
|
157
192
|
| `importfromkilo` | `true` to import from Kilo when building, `false` to start from scratch |
|
|
193
|
+
| `codegraph` | `false` to stop also running the `codegraph` CLI on build. On by default; a no-op when codegraph is not installed |
|
|
158
194
|
| `autorefresh` | Refresh the own index automatically on search |
|
|
159
195
|
| `enabled` | Master switch. `false` pauses writing and refreshing; search keeps reading Kilo's index |
|
|
160
196
|
| `searchmaxresults` | Result cap per search |
|
|
@@ -400,12 +436,13 @@ Word about data safety: Kilo's indexes (`ws-*` collections and its LanceDB direc
|
|
|
400
436
|
### Where each index lives
|
|
401
437
|
|
|
402
438
|
The own index and Kilo's index are independent, and each one is chosen on its
|
|
403
|
-
own:
|
|
439
|
+
own. codegraph keeps a third, and it is the one this plugin never writes to:
|
|
404
440
|
|
|
405
441
|
| Index | Default | Configured by |
|
|
406
442
|
| --- | --- | --- |
|
|
407
443
|
| Own index | embedded LanceDB, under `~/.local/state/opencode-indexing/lancedb` | `vectorStore` |
|
|
408
444
|
| Kilo index (read-only) | whatever `kilo.jsonc` says | Kilo itself |
|
|
445
|
+
| codegraph's graph | `.codegraph/` inside the workspace | codegraph itself |
|
|
409
446
|
|
|
410
447
|
Kilo's `vectorStore` used to decide where this plugin's own index was written,
|
|
411
448
|
because both were read from the same setting chain. That is no longer the case:
|
|
@@ -439,20 +476,31 @@ that. Set vectorStore=qdrant explicitly in
|
|
|
439
476
|
Set `vectorStore=qdrant` in `~/.config/opencode/indexing.json` (or run
|
|
440
477
|
`/indexing-config set vectorstore=qdrant`) to use a Qdrant server instead.
|
|
441
478
|
|
|
442
|
-
## Semantic search
|
|
479
|
+
## Semantic search and code graphs
|
|
480
|
+
|
|
481
|
+
Two indexes, not two options. This plugin builds and searches the semantic one;
|
|
482
|
+
it also keeps codegraph's code graph in step for the same folder, so a single
|
|
483
|
+
`indexing_build` leaves both current. Neither replaces the other, and this is not
|
|
484
|
+
a fork of codegraph — see [The codegraph companion](#the-codegraph-companion)
|
|
485
|
+
for the install side.
|
|
443
486
|
|
|
444
|
-
|
|
445
|
-
explicit about how it compares to **code-graph** tooling (call graphs, symbol
|
|
446
|
-
graphs, SCIP-style indexes), because they answer different questions and are
|
|
447
|
-
complementary:
|
|
487
|
+
### The two indexes
|
|
448
488
|
|
|
449
|
-
| | Semantic index (this plugin) | Code graph |
|
|
489
|
+
| | Semantic index (this plugin) | Code graph (codegraph) |
|
|
450
490
|
| --- | --- | --- |
|
|
451
491
|
| Question it answers | "Where is the code that does something like X?" | "Who calls X? What breaks if I change it? Path from A to B?" |
|
|
452
492
|
| Signal | Meaning (embeddings over chunks) | Structure (symbols, `CALLS`, `DEFINES`, `IMPORTS` edges) |
|
|
453
493
|
| Strengths | Natural language, concepts without known identifiers, cross-language | Exact structural facts, multi-hop traversals, no similarity false positives |
|
|
454
494
|
| Weaknesses | "Similar" is not "relevant"; no notion of connections | Cannot find a concept with no known symbol; fragile on dynamic code |
|
|
455
|
-
|
|
|
495
|
+
| What it costs to stay fresh | **Paid**: the embedding API, re-run when the model changes | **Free**: local parsing and a small graph store, re-run on every edit |
|
|
496
|
+
|
|
497
|
+
That last row is what shapes the setup. A semantic index is re-embedded only when
|
|
498
|
+
the model changes, and that is money; a graph is synced whenever a file is
|
|
499
|
+
touched, and that is CPU. Two cadences for two economies — which is the reason
|
|
500
|
+
to keep both pointed at one folder rather than at two projects that quietly
|
|
501
|
+
drift apart.
|
|
502
|
+
|
|
503
|
+
### Measuring the difference
|
|
456
504
|
|
|
457
505
|
We measured this rather than assuming it. [`bench/`](./bench) runs the same
|
|
458
506
|
real questions about code through four methods — word matching, symbol-name
|
|
@@ -471,23 +519,43 @@ What it found:
|
|
|
471
519
|
better on both: more files found on one codebase, a better position on the
|
|
472
520
|
other.
|
|
473
521
|
|
|
474
|
-
One caveat
|
|
475
|
-
query`, the one subcommand that matches names
|
|
476
|
-
graph is actually good for — who calls what, what breaks if you
|
|
477
|
-
paths between two places.
|
|
478
|
-
|
|
522
|
+
One caveat, and it is larger than it looks. The benchmark measures
|
|
523
|
+
`codegraph query`, the one subcommand that matches **names**. It does **not**
|
|
524
|
+
measure what a graph is actually good for — who calls what, what breaks if you
|
|
525
|
+
change it, paths between two places. So the fusion above is *semantic + name
|
|
526
|
+
matching*, **not** *semantic + graph traversal*. What would actually justify
|
|
527
|
+
reaching for the graph is not in those numbers, and this section does not claim
|
|
528
|
+
it is.
|
|
529
|
+
|
|
530
|
+
### Using them together
|
|
531
|
+
|
|
532
|
+
The division of labour is the one the wider field converges on: use embeddings
|
|
533
|
+
to find the entry node **by intent**, then walk the graph for **exact
|
|
534
|
+
relationships**.
|
|
479
535
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
536
|
+
1. `indexing_search` — "where is the retry logic for failed webhooks". Finds the
|
|
537
|
+
neighbourhood from meaning alone, with no identifier to know in advance.
|
|
538
|
+
2. `codegraph_explore` — the same area, returning the verbatim source of the
|
|
539
|
+
relevant symbols, the call paths between them, and what depends on them.
|
|
540
|
+
3. `codegraph_impact` / `codegraph_callers` — once the question becomes "what
|
|
541
|
+
breaks if I change this", the graph answers exactly, and a similarity search
|
|
542
|
+
cannot.
|
|
485
543
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
544
|
+
Both are live in the same session, so an assistant moves between them without
|
|
545
|
+
being asked to.
|
|
546
|
+
|
|
547
|
+
### Why there are no graph tools in this plugin
|
|
548
|
+
|
|
549
|
+
There are no `indexing_graph_*` tools here, and that is a decision rather than a
|
|
550
|
+
gap. An earlier version of this section proposed adding tree-sitter symbol and
|
|
551
|
+
edge extraction into SQLite as a candidate for a future major version. Once
|
|
552
|
+
codegraph was available as a companion, that plan would have reimplemented
|
|
553
|
+
something another tool already does better — in a second store, behind a second
|
|
554
|
+
indexer that has to be kept alive and fresh.
|
|
555
|
+
|
|
556
|
+
The plugin stays on the semantic side and coexists with grep/read (see the tool
|
|
557
|
+
descriptions and the bundled skill). What it adds for the structural half is to
|
|
558
|
+
keep it current, not to duplicate it.
|
|
491
559
|
|
|
492
560
|
## Development
|
|
493
561
|
|
|
@@ -505,7 +573,9 @@ node test/cli.ts build --vector-store lancedb --root .
|
|
|
505
573
|
|
|
506
574
|
## Acknowledgements
|
|
507
575
|
|
|
508
|
-
This plugin interoperates with the open-source [Kilo Code](https://github.com/Kilo-Org/kilocode) indexing format (MIT License, Copyright (c) 2026 Kilo Code). The line-based chunker and the tree-sitter queries are ports of Kilo Code's implementation. It also builds on [opencode](https://github.com/anomalyco/opencode) (MIT), [LanceDB](https://github.com/lancedb/lancedb) (Apache-2.0), [web-tree-sitter](https://github.com/tree-sitter/tree-sitter) (MIT), [tree-sitter-wasms](https://github.com/Gregoor/tree-sitter-wasms) (Unlicense)
|
|
576
|
+
This plugin interoperates with the open-source [Kilo Code](https://github.com/Kilo-Org/kilocode) indexing format (MIT License, Copyright (c) 2026 Kilo Code). The line-based chunker and the tree-sitter queries are ports of Kilo Code's implementation. It also builds on [opencode](https://github.com/anomalyco/opencode) (MIT), [LanceDB](https://github.com/lancedb/lancedb) (Apache-2.0), [Apache Arrow](https://github.com/apache/arrow) (Apache-2.0), [web-tree-sitter](https://github.com/tree-sitter/tree-sitter) (MIT), [tree-sitter-wasms](https://github.com/Gregoor/tree-sitter-wasms) (Unlicense), [ignore](https://github.com/kaelzhang/node-ignore) (MIT), and — for the terminal UI — [SolidJS](https://github.com/solidjs/solid) (MIT) and [OpenTUI](https://github.com/sst/opentui) (MIT).
|
|
577
|
+
|
|
578
|
+
The companion code graph is [CodeGraph](https://github.com/colbymchenry/codegraph) (MIT), a separate project this one installs and drives rather than bundles. Its npm package is `@colbymchenry/codegraph`; the unscoped `codegraph` on npm is a different, unrelated project.
|
|
509
579
|
|
|
510
580
|
Kilo Code is a trademark of its owners; this project is **unofficial and not affiliated** with Kilo Code or the OpenCode project.
|
|
511
581
|
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -56,10 +56,35 @@ SOFTWARE.
|
|
|
56
56
|
| Package | License | Copyright |
|
|
57
57
|
| --- | --- | --- |
|
|
58
58
|
| [`@lancedb/lancedb`](https://github.com/lancedb/lancedb) | Apache-2.0 | LanceDB, Inc. |
|
|
59
|
+
| [`apache-arrow`](https://github.com/apache/arrow) | Apache-2.0 | The Apache Software Foundation |
|
|
59
60
|
| [`web-tree-sitter`](https://github.com/tree-sitter/tree-sitter) | MIT | Tree-sitter contributors |
|
|
60
61
|
| [`tree-sitter-wasms`](https://github.com/Gregoor/tree-sitter-wasms) | Unlicense | Gregor (packaging); grammars by their respective authors |
|
|
61
62
|
| [`ignore`](https://github.com/kaelzhang/node-ignore) | MIT | Kael Zhang and contributors |
|
|
63
|
+
| [`solid-js`](https://github.com/solidjs/solid) | MIT | Ryan Carniato and contributors |
|
|
64
|
+
| [`@opentui/core`](https://github.com/sst/opentui) | MIT | SST and OpenTUI contributors |
|
|
65
|
+
| [`@opentui/solid`](https://github.com/sst/opentui) | MIT | SST and OpenTUI contributors |
|
|
66
|
+
|
|
67
|
+
`solid-js`, `@opentui/core` and `@opentui/solid` are what the terminal UI is
|
|
68
|
+
rendered with. They are optional dependencies: without them the prompt-footer
|
|
69
|
+
indicator and the settings page are absent, and everything else works.
|
|
62
70
|
|
|
63
71
|
Tree-sitter grammar `.wasm` files bundled by `tree-sitter-wasms` are distributed
|
|
64
72
|
under their upstream licenses (mostly MIT or Apache-2.0); see the
|
|
65
73
|
`tree-sitter-wasms` package for details.
|
|
74
|
+
|
|
75
|
+
## CodeGraph (github.com/colbymchenry/codegraph)
|
|
76
|
+
|
|
77
|
+
Not bundled and not required. CodeGraph is a separate program that this plugin
|
|
78
|
+
can install, register as an MCP server and keep in step; it is listed here
|
|
79
|
+
because the plugin drives it and points at it by name.
|
|
80
|
+
|
|
81
|
+
License: **MIT**, as declared in the package manifest
|
|
82
|
+
(`@colbymchenry/codegraph@1.6.2`).
|
|
83
|
+
|
|
84
|
+
The published package carries no `LICENSE` file of its own and declares no
|
|
85
|
+
author, so the authoritative license text is the one in the upstream repository:
|
|
86
|
+
<https://github.com/colbymchenry/codegraph>. It is not reproduced here rather
|
|
87
|
+
than reproduced with a copyright holder this project cannot verify.
|
|
88
|
+
|
|
89
|
+
The npm package is `@colbymchenry/codegraph`. The unscoped `codegraph` on npm is
|
|
90
|
+
an unrelated project and is not what this plugin installs.
|
package/index.ts
CHANGED
|
@@ -79,6 +79,24 @@ plugin's own incremental index (Qdrant or LanceDB backend).
|
|
|
79
79
|
- Verifying whether code exists that must be fresh: the index may lag behind
|
|
80
80
|
recent edits; check \`indexing_status\` (freshness) or use Grep for certainty.
|
|
81
81
|
|
|
82
|
+
## If codegraph is also installed
|
|
83
|
+
|
|
84
|
+
When the codegraph MCP server is present, its tools are available alongside this
|
|
85
|
+
one, and together they answer more than either can alone:
|
|
86
|
+
|
|
87
|
+
- \`codegraph_explore\` — the area around a finding, with the verbatim source of
|
|
88
|
+
the relevant symbols, the call paths between them, and what depends on them.
|
|
89
|
+
- \`codegraph_callers\` / \`codegraph_impact\` — "who calls this" and "what breaks
|
|
90
|
+
if I change it". Exact, structural answers that a similarity search cannot
|
|
91
|
+
give.
|
|
92
|
+
- \`indexing_search\` still goes first when the intent is known and the
|
|
93
|
+
identifier is not: it finds the entry node by meaning, and the graph then
|
|
94
|
+
explains it.
|
|
95
|
+
|
|
96
|
+
The two indexes are kept current together, so neither is stale relative to the
|
|
97
|
+
other. If those tools are not available, everything above still applies and
|
|
98
|
+
Grep remains the right fallback.
|
|
99
|
+
|
|
82
100
|
## Configuration
|
|
83
101
|
|
|
84
102
|
\`indexing_config\` reports the effective settings (backend, model, credentials —
|
package/install.mjs
CHANGED
|
@@ -330,6 +330,15 @@ function hasCodegraphBin() {
|
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
+
/**
|
|
334
|
+
* The npm package that provides the `codegraph` CLI.
|
|
335
|
+
*
|
|
336
|
+
* Scoped, and it has to be. The unscoped `codegraph` on npm is an unrelated
|
|
337
|
+
* 2024 library with no binary, so installing that name succeeds and leaves
|
|
338
|
+
* nothing on PATH — a silent no-op that looks like a working install.
|
|
339
|
+
*/
|
|
340
|
+
const CODEGRAPH_PACKAGE = "@colbymchenry/codegraph"
|
|
341
|
+
|
|
333
342
|
/**
|
|
334
343
|
* Install codegraph and register its MCP with OpenCode.
|
|
335
344
|
*
|
|
@@ -342,16 +351,17 @@ function installCodegraph() {
|
|
|
342
351
|
log(" installing codegraph as a companion MCP server...")
|
|
343
352
|
|
|
344
353
|
if (!hasCodegraphBin()) {
|
|
345
|
-
log(
|
|
354
|
+
log(` codegraph CLI missing; running npm install -g ${CODEGRAPH_PACKAGE}...`)
|
|
346
355
|
const npm = isWindows ? "npm.cmd" : "npm"
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
|
|
356
|
+
// Scoped on purpose. `codegraph` on the unscoped registry is an unrelated
|
|
357
|
+
// 2024 library with no CLI at all, and installing it would look successful
|
|
358
|
+
// while leaving nothing on PATH to register.
|
|
359
|
+
const installed = spawnSync(npm, ["install", "-g", CODEGRAPH_PACKAGE], {
|
|
350
360
|
stdio: "inherit",
|
|
351
361
|
shell: isWindows,
|
|
352
362
|
})
|
|
353
363
|
if (installed.error || installed.status !== 0) {
|
|
354
|
-
warn(
|
|
364
|
+
warn(` could not install codegraph (npm install -g ${CODEGRAPH_PACKAGE} failed); skipping MCP registration`)
|
|
355
365
|
return
|
|
356
366
|
}
|
|
357
367
|
}
|
package/install.ps1
CHANGED
|
@@ -2,14 +2,19 @@
|
|
|
2
2
|
# Thin wrapper: the cross-platform logic lives in install.mjs, so Windows, Linux
|
|
3
3
|
# and macOS all run the same code instead of drifting copies.
|
|
4
4
|
#
|
|
5
|
+
# The preferred form from npm is:
|
|
6
|
+
# npx @javierkaiser/opencode-indexing --with-codegraph
|
|
7
|
+
# This wrapper forwards to the same install.mjs, so it accepts the same flags.
|
|
8
|
+
#
|
|
5
9
|
# Usage:
|
|
6
|
-
# powershell -ExecutionPolicy Bypass -File install.ps1 [-Local|-Package] [-Spec <spec>] [-SkipDeps] [-NoRestart]
|
|
10
|
+
# powershell -ExecutionPolicy Bypass -File install.ps1 [-Local|-Package] [-Spec <spec>] [-SkipDeps] [-NoRestart] [-WithCodegraph]
|
|
7
11
|
param(
|
|
8
12
|
[switch]$Local,
|
|
9
13
|
[switch]$Package,
|
|
10
14
|
[string]$Spec,
|
|
11
15
|
[switch]$SkipDeps,
|
|
12
|
-
[switch]$NoRestart
|
|
16
|
+
[switch]$NoRestart,
|
|
17
|
+
[switch]$WithCodegraph
|
|
13
18
|
)
|
|
14
19
|
|
|
15
20
|
$ErrorActionPreference = "Stop"
|
|
@@ -20,6 +25,7 @@ if ($Package) { $forward += "--package" }
|
|
|
20
25
|
if ($Spec) { $forward += $Spec }
|
|
21
26
|
if ($SkipDeps) { $forward += "--skip-deps" }
|
|
22
27
|
if ($NoRestart) { $forward += "--no-restart" }
|
|
28
|
+
if ($WithCodegraph) { $forward += "--with-codegraph" }
|
|
23
29
|
|
|
24
30
|
if (Get-Command node -ErrorAction SilentlyContinue) {
|
|
25
31
|
& node $script @forward
|
package/install.sh
CHANGED
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
# Installs opencode-indexing into the global OpenCode config directory.
|
|
3
3
|
# Thin wrapper: the cross-platform logic lives in install.mjs.
|
|
4
4
|
#
|
|
5
|
+
# The preferred form from npm is:
|
|
6
|
+
# npx @javierkaiser/opencode-indexing --with-codegraph
|
|
7
|
+
# This wrapper forwards to the same install.mjs, so it accepts the same flags.
|
|
8
|
+
#
|
|
5
9
|
# Usage:
|
|
6
|
-
# sh install.sh [--local|--package] [spec] [--skip-deps] [--no-restart]
|
|
10
|
+
# sh install.sh [--local|--package] [spec] [--skip-deps] [--no-restart] [--with-codegraph]
|
|
7
11
|
set -eu
|
|
8
12
|
|
|
9
13
|
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@javierkaiser/opencode-indexing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "OpenCode plugin: semantic code search that reuses Kilo Code indexes (Qdrant/LanceDB) and maintains its own independent index",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Carlos Javier Lust <javierkaiser@gmail.com>",
|
package/src/codegraph.ts
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import * as fs from "node:fs"
|
|
2
|
+
import * as path from "node:path"
|
|
3
|
+
|
|
4
|
+
import { assessScope } from "./scope.ts"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Companion integration with CodeGraph, the code-graph indexer.
|
|
8
|
+
*
|
|
9
|
+
* Two indexes, two signals: this plugin answers "where is the code that does
|
|
10
|
+
* something like X", codegraph answers "who calls X, what breaks if I change
|
|
11
|
+
* it". They are complementary, and indexing only one leaves the other stale.
|
|
12
|
+
*
|
|
13
|
+
* The decision this module makes is narrow on purpose. It never searches, never
|
|
14
|
+
* parses, never reads codegraph's database: it either launches `codegraph init`
|
|
15
|
+
* or `codegraph sync` and reports what it launched. Everything it knows about
|
|
16
|
+
* codegraph comes from running its CLI.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Directory codegraph writes its index into. Its presence is the "is it indexed" answer. */
|
|
20
|
+
const CODEGRAPH_DIR = ".codegraph"
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The npm package that provides the `codegraph` CLI.
|
|
24
|
+
*
|
|
25
|
+
* Scoped, and it has to be: the unscoped `codegraph` on npm is an unrelated
|
|
26
|
+
* 2024 library with no binary. Installing that name exits 0 and leaves nothing
|
|
27
|
+
* on PATH, which is the worst shape of failure for an installer step — it looks
|
|
28
|
+
* like it worked.
|
|
29
|
+
*/
|
|
30
|
+
const CODEGRAPH_PACKAGE = "@colbymchenry/codegraph"
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Minimum seconds between launches for one workspace.
|
|
34
|
+
*
|
|
35
|
+
* codegraph is not the only writer of `.codegraph/`: it runs a daemon with a
|
|
36
|
+
* file watcher that auto-syncs on change. Two `sync` invocations seconds apart
|
|
37
|
+
* contend for the same database, so the second one is skipped rather than
|
|
38
|
+
* allowed to collide. Also the cheap defence against a user hammering build.
|
|
39
|
+
*/
|
|
40
|
+
const MIN_LAUNCH_INTERVAL_MS = 15_000
|
|
41
|
+
|
|
42
|
+
export interface CodegraphLaunch {
|
|
43
|
+
/** `init` on a folder that was never indexed, `sync` otherwise. */
|
|
44
|
+
action: "init" | "sync"
|
|
45
|
+
/** Command as run, for the message the user sees. */
|
|
46
|
+
command: string
|
|
47
|
+
/** Why it ran, or why it did not. Always present. */
|
|
48
|
+
detail: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type CodegraphOutcome =
|
|
52
|
+
| { kind: "launched"; launch: CodegraphLaunch }
|
|
53
|
+
/** Nothing was started; `reason` is user-facing and complete on its own. */
|
|
54
|
+
| { kind: "skipped"; reason: string }
|
|
55
|
+
/** Could not start at all. Never fatal: the semantic index does not depend on this. */
|
|
56
|
+
| { kind: "failed"; reason: string }
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The seam that keeps the test suite away from real processes.
|
|
60
|
+
*
|
|
61
|
+
* `src/` had no `child_process` use before this, so there is no precedent to
|
|
62
|
+
* follow. Defaulting to the real spawner and injecting in tests keeps the module
|
|
63
|
+
* honest without making every test shell out.
|
|
64
|
+
*/
|
|
65
|
+
export interface CodegraphRunner {
|
|
66
|
+
/** Resolves true when the `codegraph` CLI answers. Never throws. */
|
|
67
|
+
available(): Promise<boolean>
|
|
68
|
+
/**
|
|
69
|
+
* Starts the command and returns immediately.
|
|
70
|
+
*
|
|
71
|
+
* Deliberately not awaited to completion: `init` on a large monorepo can run
|
|
72
|
+
* for minutes, and a tool call that blocks that long is worse than one that
|
|
73
|
+
* finishes. The caller therefore reports "started", never "succeeded".
|
|
74
|
+
*/
|
|
75
|
+
launch(args: string[], cwd: string): Promise<void>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Monotonic-enough clock, injectable so tests do not have to wait 15 seconds. */
|
|
79
|
+
export interface CodegraphClock {
|
|
80
|
+
now(): number
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const realClock: CodegraphClock = { now: () => Date.now() }
|
|
84
|
+
|
|
85
|
+
/** Last launch per workspace, so the interval gate survives across calls. */
|
|
86
|
+
const lastLaunch = new Map<string, number>()
|
|
87
|
+
|
|
88
|
+
/** Whether `.codegraph/` exists. Sync rather than async: it runs on every build. */
|
|
89
|
+
export function isCodegraphInitialized(root: string): boolean {
|
|
90
|
+
try {
|
|
91
|
+
return fs.existsSync(path.join(root, CODEGRAPH_DIR))
|
|
92
|
+
} catch {
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Spawn options shared by both commands.
|
|
99
|
+
*
|
|
100
|
+
* `shell` is true on Windows and false elsewhere, and it is not a stylistic
|
|
101
|
+
* choice: npm installs its executables as `codegraph.cmd` / `codegraph.ps1`
|
|
102
|
+
* shims, which `spawn` cannot resolve on its own — it reports ENOENT for a
|
|
103
|
+
* binary that is installed and working. Verified by running both shapes.
|
|
104
|
+
*
|
|
105
|
+
* The arguments are fixed literals chosen here, never user input, so there is
|
|
106
|
+
* no path for quoting to matter; `shell` is what buys Windows compatibility.
|
|
107
|
+
*/
|
|
108
|
+
function spawnOptions(cwd?: string): Record<string, unknown> {
|
|
109
|
+
return {
|
|
110
|
+
cwd,
|
|
111
|
+
stdio: "ignore",
|
|
112
|
+
windowsHide: true,
|
|
113
|
+
shell: process.platform === "win32",
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The CLI runner.
|
|
119
|
+
*
|
|
120
|
+
* `version` is the cheapest command that proves both existence and runnability;
|
|
121
|
+
* `init --help` would do too but prints help. stdio is ignored rather than
|
|
122
|
+
* inherited so codegraph's output cannot corrupt the TUI, and `--yes` on init is
|
|
123
|
+
* what keeps it from blocking on a prompt nobody can answer.
|
|
124
|
+
*/
|
|
125
|
+
export const realRunner: CodegraphRunner = {
|
|
126
|
+
async available() {
|
|
127
|
+
const { spawn } = await import("node:child_process")
|
|
128
|
+
return new Promise<boolean>((resolve) => {
|
|
129
|
+
try {
|
|
130
|
+
const child = spawn("codegraph", ["version"], spawnOptions())
|
|
131
|
+
child.once("error", () => resolve(false))
|
|
132
|
+
child.once("close", (code) => resolve(code === 0))
|
|
133
|
+
} catch {
|
|
134
|
+
resolve(false)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
},
|
|
138
|
+
async launch(args, cwd) {
|
|
139
|
+
const { spawn } = await import("node:child_process")
|
|
140
|
+
await new Promise<void>((resolve) => {
|
|
141
|
+
let child: ReturnType<typeof spawn>
|
|
142
|
+
try {
|
|
143
|
+
child = spawn("codegraph", args, {
|
|
144
|
+
...spawnOptions(cwd),
|
|
145
|
+
// Detached so the index keeps building after the service restarts.
|
|
146
|
+
// Without this a restart mid-init orphans a half-written database.
|
|
147
|
+
// Not on Windows: a detached shell process is not unref-able the same
|
|
148
|
+
// way, and the host holds the handle anyway.
|
|
149
|
+
detached: process.platform !== "win32",
|
|
150
|
+
})
|
|
151
|
+
// Nothing is awaited on purpose: see the interface note. `unref` only
|
|
152
|
+
// exists on non-Windows, where detached was requested at all.
|
|
153
|
+
child.unref()
|
|
154
|
+
} catch {
|
|
155
|
+
resolve()
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
child.once("error", () => resolve())
|
|
159
|
+
child.once("spawn", () => resolve())
|
|
160
|
+
})
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface CodegraphOptions {
|
|
165
|
+
root: string
|
|
166
|
+
enabled: boolean
|
|
167
|
+
runner?: CodegraphRunner
|
|
168
|
+
clock?: CodegraphClock
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Launch codegraph for a workspace, if it makes sense to.
|
|
173
|
+
*
|
|
174
|
+
* Returns what happened; the caller decides how to say it. Never throws: a
|
|
175
|
+
* companion indexer must not be able to fail the semantic one.
|
|
176
|
+
*/
|
|
177
|
+
export async function indexWithCodegraph(options: CodegraphOptions): Promise<CodegraphOutcome> {
|
|
178
|
+
const { root, enabled } = options
|
|
179
|
+
if (!enabled) return { kind: "skipped", reason: "codegraph indexing is turned off (set codegraph=true)" }
|
|
180
|
+
|
|
181
|
+
const runner = options.runner ?? realRunner
|
|
182
|
+
const clock = options.clock ?? realClock
|
|
183
|
+
|
|
184
|
+
// A home directory or a drive root is reported rather than indexed. codegraph
|
|
185
|
+
// has `-f` for exactly this case; passing it here would index the user's whole
|
|
186
|
+
// machine because a tool call said so.
|
|
187
|
+
const scope = assessScope(root)
|
|
188
|
+
if (scope.level === "warn") {
|
|
189
|
+
return { kind: "skipped", reason: `${root} is ${scope.reason}; codegraph indexing was not started` }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const last = lastLaunch.get(root)
|
|
193
|
+
const now = clock.now()
|
|
194
|
+
if (last !== undefined && now - last < MIN_LAUNCH_INTERVAL_MS) {
|
|
195
|
+
return { kind: "skipped", reason: "codegraph was launched for this workspace moments ago" }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// "Not installed" arrives as either a false or a throw: spawn() reports a
|
|
199
|
+
// missing binary as ENOENT on the error event. Both mean the same ordinary
|
|
200
|
+
// thing, so both are a skip. A throw here is not a failure of this plugin —
|
|
201
|
+
// it is the absence of an optional companion, which is the case the whole
|
|
202
|
+
// design has to get right.
|
|
203
|
+
let available: boolean
|
|
204
|
+
try {
|
|
205
|
+
available = await runner.available()
|
|
206
|
+
} catch {
|
|
207
|
+
available = false
|
|
208
|
+
}
|
|
209
|
+
if (!available) {
|
|
210
|
+
return { kind: "skipped", reason: `the codegraph CLI is not installed (npm i -g ${CODEGRAPH_PACKAGE})` }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const initialized = isCodegraphInitialized(root)
|
|
214
|
+
const args = initialized ? ["sync"] : ["init", "--yes"]
|
|
215
|
+
const action = initialized ? "sync" : "init"
|
|
216
|
+
|
|
217
|
+
// Recorded before launching, so a failed launch still rate-limits: otherwise
|
|
218
|
+
// a broken CLI would be spawned again on every single build.
|
|
219
|
+
lastLaunch.set(root, now)
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
await runner.launch(args, root)
|
|
223
|
+
} catch (error) {
|
|
224
|
+
return { kind: "failed", reason: `could not start \`codegraph ${args.join(" ")}\`: ${message(error)}` }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
kind: "launched",
|
|
229
|
+
launch: {
|
|
230
|
+
action,
|
|
231
|
+
command: `codegraph ${args.join(" ")}`,
|
|
232
|
+
detail: initialized
|
|
233
|
+
? "syncing changes since the last index"
|
|
234
|
+
: "building the first index for this folder",
|
|
235
|
+
},
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** Test seam: forget the rate-limit state. */
|
|
240
|
+
export function resetCodegraphLaunchState(): void {
|
|
241
|
+
lastLaunch.clear()
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* One line for `indexing_status`, or "" when there is nothing worth saying.
|
|
246
|
+
*
|
|
247
|
+
* Deliberately does not run the CLI: status is called on every search via the
|
|
248
|
+
* footer, and spawning a process to decorate a status line is not a trade worth
|
|
249
|
+
* making. The directory's existence is the whole answer.
|
|
250
|
+
*/
|
|
251
|
+
export function codegraphStatusLine(root: string): string {
|
|
252
|
+
try {
|
|
253
|
+
if (!fs.existsSync(root)) return ""
|
|
254
|
+
const initialized = isCodegraphInitialized(root)
|
|
255
|
+
return initialized
|
|
256
|
+
? "codegraph: indexed (synced on the next indexing_build)"
|
|
257
|
+
: "codegraph: not indexed (initialised on the next indexing_build)"
|
|
258
|
+
} catch {
|
|
259
|
+
return ""
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function message(error: unknown): string {
|
|
264
|
+
return error instanceof Error ? error.message : String(error)
|
|
265
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface PluginOptionsInput {
|
|
|
38
38
|
/** Master switch: when false the own index is neither written nor refreshed. */
|
|
39
39
|
enabled?: boolean
|
|
40
40
|
importFromKilo?: boolean
|
|
41
|
+
/** Also launch the codegraph CLI on build. Absent means on; see IndexingSettings. */
|
|
42
|
+
codegraph?: boolean
|
|
41
43
|
/** Disable reading Kilo's config file entirely. */
|
|
42
44
|
ignoreKiloConfig?: boolean
|
|
43
45
|
homeDir?: string
|
|
@@ -283,6 +285,11 @@ export function resolveSettings(options: PluginOptionsInput): IndexingSettings {
|
|
|
283
285
|
// but nothing is written and nothing is auto-refreshed. Toggled from the TUI
|
|
284
286
|
// footer indicator or `/indexing-config set enabled false`.
|
|
285
287
|
const enabled = options.enabled ?? file?.enabled !== false
|
|
288
|
+
// Companion indexer. On by default, and a no-op when codegraph is not
|
|
289
|
+
// installed, so enabling it costs nothing for someone who does not use it.
|
|
290
|
+
// `!== false` rather than `=== true`: the key is absent from most configs and
|
|
291
|
+
// absent has to mean "on" or the default would never apply.
|
|
292
|
+
const codegraph = options.codegraph ?? file?.codegraph !== false
|
|
286
293
|
|
|
287
294
|
return {
|
|
288
295
|
provider,
|
|
@@ -306,6 +313,7 @@ export function resolveSettings(options: PluginOptionsInput): IndexingSettings {
|
|
|
306
313
|
autoRefresh,
|
|
307
314
|
importFromKilo,
|
|
308
315
|
enabled,
|
|
316
|
+
codegraph,
|
|
309
317
|
credentials,
|
|
310
318
|
warnings,
|
|
311
319
|
}
|
package/src/scanner.ts
CHANGED
|
@@ -59,8 +59,14 @@ export const DEFAULT_IGNORE_GLOBS: readonly string[] = [
|
|
|
59
59
|
|
|
60
60
|
const DEFAULT_MAX_FILE_SIZE_BYTES = 1024 * 1024
|
|
61
61
|
|
|
62
|
-
/**
|
|
63
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Dot-directories that are still walked because they hold indexable config.
|
|
64
|
+
*
|
|
65
|
+
* Exported for the test that pins `.codegraph` out of it: codegraph's SQLite
|
|
66
|
+
* database sits in that directory, and letting the scanner walk it would embed
|
|
67
|
+
* another indexer's storage into this one.
|
|
68
|
+
*/
|
|
69
|
+
export const ALLOWED_DOT_DIRS: ReadonlySet<string> = new Set([
|
|
64
70
|
".github",
|
|
65
71
|
".vscode",
|
|
66
72
|
".gitlab",
|
package/src/settings-commands.ts
CHANGED
|
@@ -265,6 +265,14 @@ const KEY_SPECS: Record<string, KeySpec> = {
|
|
|
265
265
|
toPatch: (value) => ({ enabled: Boolean(value) }),
|
|
266
266
|
display: (settings) => (settings.enabled === false ? "false" : "true"),
|
|
267
267
|
},
|
|
268
|
+
codegraph: {
|
|
269
|
+
group: "behavior",
|
|
270
|
+
kind: "boolean",
|
|
271
|
+
description: "Also run the codegraph CLI on build (companion code graph; no-op when codegraph is not installed)",
|
|
272
|
+
toPatch: (value) => ({ codegraph: Boolean(value) }),
|
|
273
|
+
// `!== false`, matching how the value resolves: absent means on.
|
|
274
|
+
display: (settings) => (settings.codegraph === false ? "false" : "true"),
|
|
275
|
+
},
|
|
268
276
|
searchmaxresults: {
|
|
269
277
|
group: "search",
|
|
270
278
|
kind: "integer",
|
package/src/settings.ts
CHANGED
|
@@ -39,6 +39,8 @@ export interface SettingsFile {
|
|
|
39
39
|
autoRefresh?: boolean
|
|
40
40
|
/** Master switch: when false the own index is neither written nor refreshed. */
|
|
41
41
|
enabled?: boolean
|
|
42
|
+
/** Also launch the codegraph CLI on build. Absent means on; a no-op if not installed. */
|
|
43
|
+
codegraph?: boolean
|
|
42
44
|
searchMinScore?: number | null
|
|
43
45
|
searchMaxResults?: number
|
|
44
46
|
embeddingBatchSize?: number
|
package/src/tools.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { indexStatusLine } from "./index-state.ts"
|
|
|
5
5
|
import { discoverKiloSources, importFromKilo, type KiloSourceInfo } from "./import.ts"
|
|
6
6
|
import { createOwnStore, createSettingsQdrant } from "./store-factory.ts"
|
|
7
7
|
import { assessScope } from "./scope.ts"
|
|
8
|
+
import { codegraphStatusLine, indexWithCodegraph, type CodegraphOutcome } from "./codegraph.ts"
|
|
8
9
|
import type { IndexingSettings, KVStore } from "./types.ts"
|
|
9
10
|
import type { Qdrant } from "./qdrant.ts"
|
|
10
11
|
|
|
@@ -72,7 +73,8 @@ Use this tool early for open-ended exploration when you know the intent but not
|
|
|
72
73
|
Notes:
|
|
73
74
|
- The index combines Kilo Code's index (when present and profile-compatible) with the plugin's own index, which can be backed by Qdrant or LanceDB.
|
|
74
75
|
- If the index reports stale/incomplete, results may be incomplete — indexed files might not include recent edits.
|
|
75
|
-
- Pass \`path\` to limit results to a subdirectory, relative to the workspace root
|
|
76
|
+
- Pass \`path\` to limit results to a subdirectory, relative to the workspace root.
|
|
77
|
+
- This finds code by meaning. For structure — who calls a symbol, what depends on it, what breaks if it changes — use the codegraph tools when that MCP server is available; a similarity search cannot answer those.`
|
|
76
78
|
|
|
77
79
|
const STATUS_DESCRIPTION = `Report the state of the project's semantic code index.
|
|
78
80
|
|
|
@@ -81,6 +83,7 @@ Shows:
|
|
|
81
83
|
- Kilo Code index sources available for import (Qdrant collection and/or local LanceDB database), with embedding profile and compatibility.
|
|
82
84
|
- The plugin's own index (points, completion, embedding profile, last run).
|
|
83
85
|
- Freshness: number of workspace files that were never indexed, possibly changed, or deleted since the last run.
|
|
86
|
+
- The codegraph companion's state for this folder, when that setting is on: whether it is indexed, and that the next build will sync it.
|
|
84
87
|
- A recommended next action (import/build/refresh).`
|
|
85
88
|
|
|
86
89
|
const REFRESH_DESCRIPTION = `Incrementally update the plugin's own code index for this workspace.
|
|
@@ -95,6 +98,11 @@ Behavior:
|
|
|
95
98
|
- \`rebuild: true\` drops the own index first and re-imports/re-indexes everything.
|
|
96
99
|
- \`skipImport: true\` forces indexing from scratch even when a Kilo index exists.
|
|
97
100
|
|
|
101
|
+
If the codegraph CLI is installed, it is also started for this folder in the
|
|
102
|
+
background (\`init\` the first time, \`sync\` afterwards) so the companion code
|
|
103
|
+
graph stays in step. It never blocks this tool and never fails it: without
|
|
104
|
+
codegraph this build is identical.
|
|
105
|
+
|
|
98
106
|
This can take a while for large workspaces and may consume embedding API credits (except when importing).`
|
|
99
107
|
|
|
100
108
|
const IMPORT_DESCRIPTION = `Import an existing Kilo Code index into the plugin's own index WITHOUT re-embedding.
|
|
@@ -103,7 +111,9 @@ Vectors and chunks are copied as-is (zero embedding API cost), which requires a
|
|
|
103
111
|
- Kilo's Qdrant collection (\`ws-*\`).
|
|
104
112
|
- Kilo's local LanceDB database for this workspace, when present.
|
|
105
113
|
|
|
106
|
-
Use indexing_status to list detected sources. If profiles do not match, rebuild with the matching model instead
|
|
114
|
+
Use indexing_status to list detected sources. If profiles do not match, rebuild with the matching model instead.
|
|
115
|
+
|
|
116
|
+
If the codegraph CLI is installed, it is also started for this folder in the background, so the companion code graph covers the files this import brought in. Without codegraph this import is identical.`
|
|
107
117
|
|
|
108
118
|
const jsonSchema = (properties: Record<string, unknown>, required: string[] = []): Record<string, unknown> => ({
|
|
109
119
|
type: "object",
|
|
@@ -242,6 +252,13 @@ export function createTools(deps: ToolDeps): ToolDefinition[] {
|
|
|
242
252
|
`Freshness: ${status.freshness.total} files scanned, ${status.freshness.added} new, ${status.freshness.maybeStale} possibly changed, ${status.freshness.deleted} deleted.`,
|
|
243
253
|
)
|
|
244
254
|
}
|
|
255
|
+
// Only when the companion indexer is actually in play: saying
|
|
256
|
+
// "codegraph: not indexed" to someone who never asked for codegraph is
|
|
257
|
+
// noise. Read from the filesystem, so this costs no process.
|
|
258
|
+
if (settings.codegraph !== false) {
|
|
259
|
+
const codegraph = codegraphStatusLine(root)
|
|
260
|
+
if (codegraph) lines.push(codegraph)
|
|
261
|
+
}
|
|
245
262
|
lines.push(`Recommendation: ${status.recommendation}`)
|
|
246
263
|
if (status.warnings.length > 0) lines.push(`Warnings: ${status.warnings.join("; ")}`)
|
|
247
264
|
return { output: lines.join("\n"), metadata: status as unknown as Record<string, unknown> }
|
|
@@ -298,9 +315,9 @@ export function createTools(deps: ToolDeps): ToolDefinition[] {
|
|
|
298
315
|
{ mode: "build", rebuild: input.rebuild === true, skipImport: input.skipImport === true },
|
|
299
316
|
)
|
|
300
317
|
const summary = formatReport("build", report)
|
|
301
|
-
const
|
|
318
|
+
const lines = [scopeWarning(root), summary, await codegraphLine(root, settings)].filter(Boolean)
|
|
302
319
|
return {
|
|
303
|
-
output:
|
|
320
|
+
output: lines.join("\n"),
|
|
304
321
|
metadata: report as unknown as Record<string, unknown>,
|
|
305
322
|
}
|
|
306
323
|
},
|
|
@@ -368,14 +385,38 @@ export function createTools(deps: ToolDeps): ToolDefinition[] {
|
|
|
368
385
|
const lines = [
|
|
369
386
|
`Imported ${report.imported} chunks from ${report.source.kind} ${report.source.name} in ${(report.durationMs / 1000).toFixed(1)}s.`,
|
|
370
387
|
`Batches: ${report.batches}, skipped: ${report.skipped}. Embedding API calls: 0.`,
|
|
388
|
+
await codegraphLine(root, settings),
|
|
371
389
|
]
|
|
372
390
|
if (report.errors.length > 0) lines.push(`Errors: ${report.errors.join("; ")}`)
|
|
373
|
-
return { output: lines.join("\n"), metadata: report as unknown as Record<string, unknown> }
|
|
391
|
+
return { output: lines.filter(Boolean).join("\n"), metadata: report as unknown as Record<string, unknown> }
|
|
374
392
|
},
|
|
375
393
|
},
|
|
376
394
|
]
|
|
377
395
|
}
|
|
378
396
|
|
|
397
|
+
/**
|
|
398
|
+
* One line about the companion indexer, for the write tools.
|
|
399
|
+
*
|
|
400
|
+
* Never throws and never reports success: the launch is detached, so by the time
|
|
401
|
+
* this returns the CLI has only started. Anything that went wrong afterwards
|
|
402
|
+
* lands in `.codegraph/daemon.log`, which is where the message points.
|
|
403
|
+
*/
|
|
404
|
+
async function codegraphLine(root: string, settings: IndexingSettings): Promise<string> {
|
|
405
|
+
let outcome: CodegraphOutcome
|
|
406
|
+
try {
|
|
407
|
+
outcome = await indexWithCodegraph({ root, enabled: settings.codegraph !== false })
|
|
408
|
+
} catch (error) {
|
|
409
|
+
return `Codegraph: not indexed (${error instanceof Error ? error.message : String(error)})`
|
|
410
|
+
}
|
|
411
|
+
if (outcome.kind === "launched") {
|
|
412
|
+
return `Codegraph: \`${outcome.launch.command}\` started in the background — ${outcome.launch.detail}.`
|
|
413
|
+
}
|
|
414
|
+
// A skip while the CLI is absent is the common case and not worth a line on
|
|
415
|
+
// every build; the other two say something the user may want to act on.
|
|
416
|
+
if (outcome.reason.startsWith("the codegraph CLI is not installed")) return ""
|
|
417
|
+
return `Codegraph: not indexed (${outcome.reason})`
|
|
418
|
+
}
|
|
419
|
+
|
|
379
420
|
function makeProgress(context: { progress?: (m: unknown) => Promise<void> }): (update: {
|
|
380
421
|
phase: string
|
|
381
422
|
processed: number
|
package/src/types.ts
CHANGED
|
@@ -96,6 +96,16 @@ export interface IndexingSettings extends EmbeddingProfile {
|
|
|
96
96
|
importFromKilo: boolean
|
|
97
97
|
/** Master switch: when false nothing is written to nor refreshed in the own index. */
|
|
98
98
|
enabled: boolean
|
|
99
|
+
/**
|
|
100
|
+
* Also launch the codegraph CLI when the workspace is indexed.
|
|
101
|
+
*
|
|
102
|
+
* Optional so that every fixture, every caller and every consumer that builds
|
|
103
|
+
* settings by hand keeps compiling: a companion that must be threaded through
|
|
104
|
+
* eight test doubles to exist is a dependency, not a companion. Callers read
|
|
105
|
+
* it as `settings.codegraph !== false`, which treats absent as "try it" —
|
|
106
|
+
* and "try it" is a no-op when the CLI is not installed.
|
|
107
|
+
*/
|
|
108
|
+
codegraph?: boolean
|
|
99
109
|
credentials: ProviderCredentials
|
|
100
110
|
/** Warnings produced while resolving configuration. */
|
|
101
111
|
warnings: string[]
|