diff --git a/inst/WORDLIST b/inst/WORDLIST
index 682b94f9..8d12f439 100644
--- a/inst/WORDLIST
+++ b/inst/WORDLIST
@@ -8,6 +8,7 @@ Init
JS
JSON
JSX
+LLM
LatinR
LibSass
Pharmaverse
diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml
index afd14cf8..a4f9d397 100644
--- a/pkgdown/_pkgdown.yml
+++ b/pkgdown/_pkgdown.yml
@@ -112,6 +112,8 @@ navbar:
- text: User tools
- text: Box-module auto-complete in VSCode
href: articles/how-to/box-lsp.html
+ - text: Build Rhino apps with LLM tools
+ href: articles/how-to/build-rhino-apps-with-llm-tools.html
- text: -------
- text: Migration guides
- text: Migration to Rhino 1.6
diff --git a/vignettes/how-to/build-rhino-apps-with-llm-tools.Rmd b/vignettes/how-to/build-rhino-apps-with-llm-tools.Rmd
new file mode 100644
index 00000000..3753f4cd
--- /dev/null
+++ b/vignettes/how-to/build-rhino-apps-with-llm-tools.Rmd
@@ -0,0 +1,154 @@
+---
+title: "How-to: Build Rhino apps with LLM tools"
+output: rmarkdown::html_vignette
+vignette: >
+ %\VignetteIndexEntry{How-to: Build Rhino apps with LLM tools}
+ %\VignetteEngine{knitr::rmarkdown}
+ %\VignetteEncoding{UTF-8}
+---
+
+LLM tools like [GitHub Copilot](https://github.com/features/copilot) can be extremely helpful when building apps with Rhino.
+However, the unique project structure and the use of the box package for module imports can make it harder for these tools to understand and assist effectively.
+The good news is that their performance can be significantly improved by providing custom instructions.
+
+# Custom instructions for GitHub Copilot
+
+To optimize GitHub Copilot for working with Rhino projects,
+you can create a file named `copilot-instructions.md` in the `.github/` directory of your repository.
+To see instructions below as markdown [visit vignette source](https://github.com/Appsilon/rhino/blob/main/vignettes/how-to/build-rhino-apps-with-llm-tools.Rmd).
+
+Example instructions:
+
+
+## Importing and exporting
+
+Use only `box::use` for imports. Using `library` and `::` is forbidden.
+
+`box::use` statement (if needed) should be located at the top of the file.
+
+There can be two `box::use` statements per file. First one should include only R packages, second should only import other scripts.
+
+Imports in `box::use` should be sorted alphabetically.
+
+Using `[...]` is forbidden.
+
+All external functions in a script should be imported. This includes operators, like `%>%`.
+
+A script should only import functions that it uses.
+
+### Ways of importing
+
+There are two ways a package or a script can be imported:
+
+1. List imported functions - functions imported are listed in []
+
+```r
+box::use(
+ dplyr[filter],
+)
+
+filter(mtcars, cyl > 4)
+```
+
+Use it if there are no more than 8 functions imported from this package/script.
+
+2. Import package and access functions with `$`
+
+```r
+box::use(
+ dplyr,
+)
+dplyr$filter(mtcars, cyl > 4)
+```
+
+When moving function into a different script, remember to adjust imports in `box::use`:
+
+1. Add import for all required functions to the file where you moved the function.
+2. Make sure to follow the correct way of importing (direct or using $) in the new file. Modify it if needed.
+3. Remove redundant imports from the original file.
+4. Import the moved function in the original file.
+
+Use it if there are more than 8 functions imported from this package/script.
+
+### Exporting
+
+If a function is used only inside a script, it should not be exported.
+
+If a function is used by other scripts, it should be exported by adding `#' @export` before the function.
+
+## Rhino modules
+
+When creating a new module in `app/view`, use the template:
+```r
+box::use(
+ shiny[moduleServer, NS]
+)
+
+#' @export
+ui <- function(id) {
+ ns <- NS(id)
+
+}
+
+#' @export
+server <- function(id) {
+ moduleServer(id, function(input, output, session) {
+
+ })
+}
+```
+
+## Unit tests
+
+All R unit tests are located in `tests/testthat`.
+
+There should be only one test file per script, named `test-{script name}.R`.
+
+If testing private functions (ones that are not exported), use this pattern:
+
+```r
+box::use(app/logic/mymod)
+
+impl <- attr(mymod, "namespace")
+
+test_that('{test description}', {
+ expect_true(impl$this_works())
+})
+```
+
+### Testing exported and non-exported functions
+
+When testing a box module that contains both exported and non-exported functions:
+
+1. Import the entire module without specifying individual functions:
+
+```r
+box::use(
+ app/logic/mymodule,
+)
+```
+
+2. Access exported functions using the module name with `$`:
+
+```r
+test_that("exported function works", {
+ expect_equal(mymodule$exported_function(1), 2)
+})
+```
+
+3. For testing non-exported functions, get the module's namespace at the start of the test file:
+
+```r
+impl <- attr(mymodule, "namespace")
+
+test_that("non-exported function works", {
+ expect_equal(impl$internal_function(1), 2)
+})
+```
+
+This pattern allows testing both public and private functions while maintaining proper encapsulation.
+
+## Code style
+
+The maximum line length is 100 characters.
+