allow graft mcp to be used within opencode

This commit is contained in:
2026-08-11 20:48:00 +02:00
parent b47fcdffcb
commit ca51546be0
4 changed files with 91 additions and 24 deletions
+3
View File
@@ -35,3 +35,6 @@ chesstrainer-*.tar
npm-debug.log npm-debug.log
/assets/node_modules/ /assets/node_modules/
# graft's local graph cache — regenerable, not committed (run `graft build`).
graft/
+5
View File
@@ -0,0 +1,5 @@
# graft's cards are gitignored but should stay greppable: ripgrep reads
# .ignore before .gitignore, so this re-admits the tree to search only.
!graft/
graft/.cache/
graft/.graph/
+54 -5
View File
@@ -1,5 +1,49 @@
This is a web application written using the Phoenix web framework. This is a web application written using the Phoenix web framework.
<!-- graft:start -->
## Graft — repo context graph
This repo is indexed in `graft/`: small linked markdown nodes that explain each
system and carry exact file:line spans, kept in sync with the code through git.
For ANY task here — understanding how something works, finding where code lives,
or scoping a change — get context from the graph before grepping or opening
source files. Re-ask freely (it's cheap) and reuse literal identifiers you
already have (symbol, error string, file name) as the query. New to this repo?
Run `graft map` first — a token-budgeted orientation (dir clusters, hubs,
hotspots), no LLM, no key.
- Run `graft ask "<your question>" --source` → ranked nodes with the relevant
code spans inlined (each hit's ≤8-line crux by default; `--full` for whole
definitions when the crux isn't enough). Match the tool to the task shape:
for understanding or editing, the top node IS the answer — cite its
`covers:` file:line spans and edit straight from `--source`. For
exhaustive tasks ("every occurrence / every caller of this pattern"), ranked
results are top-N, not complete — run `graft grep "<literal>"` instead
(exhaustive over indexed files, grouped by enclosing symbol), falling back
to raw `grep -rn` only for unindexed files.
- `graft skeleton <file>` → every definition's signature + span, ~10× cheaper
than reading the file; use it to skim an API surface.
- `graft callers <symbol>` gives precomputed, exact edges — who calls this.
Add `--direction out` for what it calls, or `--depth N` to walk
transitively for the full blast radius. For structural questions, skip
ranking and use this directly.
- Or browse: `graft/INDEX.md` lists every node; follow the links.
- Monorepos and folders of multiple repos rank fairly across sub-projects —
hits carry `[scope/]` labels naming which one they're from. Narrow with
`graft ask "<task>" --in <scope>/` once you know where you're working.
If a returned span is truncated ("+N more lines"), open the file at that exact
range before finalizing. Only open source files when a node genuinely lacks a
needed detail, and then at the exact file:line the node points to — never
re-read whole files.
After big code changes, refresh the graph with `graft build` (deterministic,
no API key, $0).
<!-- graft:end -->
## Project guidelines ## Project guidelines
- Use `mix precommit` alias when you are done with all changes and fix any pending issues - Use `mix precommit` alias when you are done with all changes and fix any pending issues
@@ -43,10 +87,10 @@ custom classes must fully style the input
- Ensure **clean typography, spacing, and layout balance** for a refined, premium look - Ensure **clean typography, spacing, and layout balance** for a refined, premium look
- Focus on **delightful details** like hover effects, loading states, and smooth page transitions - Focus on **delightful details** like hover effects, loading states, and smooth page transitions
<!-- usage-rules-start --> <!-- usage-rules-start -->
<!-- phoenix:elixir-start --> <!-- phoenix:elixir-start -->
## Elixir guidelines ## Elixir guidelines
- Elixir lists **do not support index based access via the access syntax** - Elixir lists **do not support index based access via the access syntax**
@@ -64,7 +108,7 @@ custom classes must fully style the input
Enum.at(mylist, i) Enum.at(mylist, i)
- Elixir variables are immutable, but can be rebound, so for block expressions like `if`, `case`, `cond`, etc - Elixir variables are immutable, but can be rebound, so for block expressions like `if`, `case`, `cond`, etc
you *must* bind the result of the expression to a variable if you want to use it and you CANNOT rebind the result inside the expression, ie: you _must_ bind the result of the expression to a variable if you want to use it and you CANNOT rebind the result inside the expression, ie:
# INVALID: we are rebinding inside the `if` and the result never gets assigned # INVALID: we are rebinding inside the `if` and the result never gets assigned
if connected?(socket) do if connected?(socket) do
@@ -104,6 +148,7 @@ custom classes must fully style the input
<!-- phoenix:elixir-end --> <!-- phoenix:elixir-end -->
<!-- phoenix:phoenix-start --> <!-- phoenix:phoenix-start -->
## Phoenix guidelines ## Phoenix guidelines
- Remember Phoenix router `scope` blocks include an optional alias which is prefixed for all routes within the scope. **Always** be mindful of this when creating routes within a scope to avoid duplicate module prefixes. - Remember Phoenix router `scope` blocks include an optional alias which is prefixed for all routes within the scope. **Always** be mindful of this when creating routes within a scope to avoid duplicate module prefixes.
@@ -122,6 +167,7 @@ custom classes must fully style the input
<!-- phoenix:phoenix-end --> <!-- phoenix:phoenix-end -->
<!-- phoenix:ecto-start --> <!-- phoenix:ecto-start -->
## Ecto Guidelines ## Ecto Guidelines
- **Always** preload Ecto associations in queries when they'll be accessed in templates, ie a message that needs to reference the `message.user.email` - **Always** preload Ecto associations in queries when they'll be accessed in templates, ie a message that needs to reference the `message.user.email`
@@ -134,6 +180,7 @@ custom classes must fully style the input
<!-- phoenix:ecto-end --> <!-- phoenix:ecto-end -->
<!-- phoenix:html-start --> <!-- phoenix:html-start -->
## Phoenix HTML guidelines ## Phoenix HTML guidelines
- Phoenix templates **always** use `~H` or .html.heex files (known as HEEx), **never** use `~E` - Phoenix templates **always** use `~H` or .html.heex files (known as HEEx), **never** use `~E`
@@ -163,7 +210,7 @@ custom classes must fully style the input
... ...
<% end %> <% end %>
- HEEx require special tag annotation if you want to insert literal curly's like `{` or `}`. If you want to show a textual code snippet on the page in a `<pre>` or `<code>` block you *must* annotate the parent tag with `phx-no-curly-interpolation`: - HEEx require special tag annotation if you want to insert literal curly's like `{` or `}`. If you want to show a textual code snippet on the page in a `<pre>` or `<code>` block you _must_ annotate the parent tag with `phx-no-curly-interpolation`:
<code phx-no-curly-interpolation> <code phx-no-curly-interpolation>
let obj = {key: "val"} let obj = {key: "val"}
@@ -210,9 +257,11 @@ custom classes must fully style the input
{if @invalid_block_construct do} {if @invalid_block_construct do}
{end} {end}
</div> </div>
<!-- phoenix:html-end --> <!-- phoenix:html-end -->
<!-- phoenix:liveview-start --> <!-- phoenix:liveview-start -->
## Phoenix LiveView guidelines ## Phoenix LiveView guidelines
- **Never** use the deprecated `live_redirect` and `live_patch` functions, instead **always** use the `<.link navigate={href}>` and `<.link patch={href}>` in templates, and `push_navigate` and `push_patch` functions LiveViews - **Never** use the deprecated `live_redirect` and `live_patch` functions, instead **always** use the `<.link navigate={href}>` and `<.link patch={href}>` in templates, and `push_navigate` and `push_patch` functions LiveViews
@@ -235,7 +284,7 @@ custom classes must fully style the input
</div> </div>
</div> </div>
- LiveView streams are *not* enumerable, so you cannot use `Enum.filter/2` or `Enum.reject/2` on them. Instead, if you want to filter, prune, or refresh a list of items on the UI, you **must refetch the data and re-stream the entire stream collection, passing reset: true**: - LiveView streams are _not_ enumerable, so you cannot use `Enum.filter/2` or `Enum.reject/2` on them. Instead, if you want to filter, prune, or refresh a list of items on the UI, you **must refetch the data and re-stream the entire stream collection, passing reset: true**:
def handle_event("filter", %{"filter" => filter}, socket) do def handle_event("filter", %{"filter" => filter}, socket) do
# re-fetch the messages based on the filter # re-fetch the messages based on the filter
@@ -248,7 +297,7 @@ custom classes must fully style the input
|> stream(:messages, messages, reset: true)} |> stream(:messages, messages, reset: true)}
end end
- LiveView streams *do not support counting or empty states*. If you need to display a count, you must track it using a separate assign. For empty states, you can use Tailwind classes: - LiveView streams _do not support counting or empty states_. If you need to display a count, you must track it using a separate assign. For empty states, you can use Tailwind classes:
<div id="tasks" phx-update="stream"> <div id="tasks" phx-update="stream">
<div class="hidden only:block">No tasks yet</div> <div class="hidden only:block">No tasks yet</div>
+10
View File
@@ -0,0 +1,10 @@
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"graft": {
"type": "local",
"command": ["npx", "-y", "@nanonets/graft", "mcp"],
"enabled": true
}
}
}