bchelkowski.kopytko v1.13.1 Kopytko VS Code Extension
A full-featured BrightScript language server extension for VS Code. IntelliSense, diagnostics, go-to-definition, call hierarchy, formatting, debugging, and Roku device management — all in one.
IntelliSense
Context-aware completions covering every BrightScript built-in, user-defined functions, local variables, type annotations, ro* component constructors, and Kopytko module exports.
- ✓ Built-in functions and keywords from the BrightScript stdlib
- ✓ User functions from current file, @imports, XML siblings, and /source/
- ✓ Local variables, parameters, and for-loop variables
- ✓ CreateObject("...") — SceneGraph component name completions
- ✓ Type inference: member completions after CreateObject() or typed params
- ✓ m.top field completions from XML interface and parent SG components
- ✓ @import / @mock path auto-complete with package-resolution
- ✓ Kopytko module exports — auto-inserts @import on selection
- ✓ Test framework completions: expect(), mockFunction(), test globals
IntelliSense completion popup
Completion popup showing BrightScript built-ins, user functions, and type-inferred members
→ site/public/screenshots/intellisense-completion-popup.png
Hover documentation
Hover card on a built-in function showing its signature, description, and return type
→ site/public/screenshots/hover-documentation.png
Navigation
Navigate your codebase with precision — from go-to-definition on @import paths to workspace-wide call hierarchy and SceneGraph type hierarchy. All navigation respects the @import graph and XML sibling relationships.
Go-to-definition on import
Definition peek popup on an @import path, jumping to the imported .brs file
→ site/public/screenshots/go-to-definition-on-import.png
Call hierarchy panel
Incoming calls panel showing which functions call the selected function
→ site/public/screenshots/call-hierarchy-panel.png
Diagnostics & Quick Fixes
BrightScript diagnostics
BrightScript file with multiple diagnostic squiggles: undefined function, missing type annotation, unused import
→ site/public/screenshots/brightscript-diagnostics.png
Quick fix menu
Light-bulb quick-fix menu with options: Add missing import, Remove unused parameter, Add type annotation
→ site/public/screenshots/quick-fix-menu.png
Real-time diagnostics powered by the kopytko-linter engine. 32 rules reported as you type, with code actions for auto-fixable issues.
identifier/undefined-function Highlights calls to undeclared functions
identifier/undefined-variable Variable used before assignment
import/unresolved @import path cannot be found on disk
import/unused Imported file never referenced
type/missing-return-type Missing as Type on function declaration
callback/undefined-observer-callback observeFieldScoped() callback not found
… and 19 more rules. Full rule list →
One of them is project-wide rather than per-file: component/duplicate-name warns on both XML files when two of them declare the same SceneGraph component. Component names are global to the channel, so the second declaration silently overrides the first. The editor reports it as you save; kopytko-lint reports it in CI.
Kopytko imports & test framework
First-class support for the @import / @mock annotation system used by the Kopytko Framework. Import chains are resolved transitively across packages with full IntelliSense at each step.
' @import /components/VideoPlayer.brs
' @import /utils/analytics.brs
' @mock /components/VideoPlayer.brs ' test mode only
sub init()
' completions, hover, go-to-definition all work
m.player = createVideoPlayer()
m.player.observeFieldScoped("state", "onStateChanged")
end sub
Test files (*/_tests/**/*.test.brs) get enriched completions for the Kopytko Unit Testing Framework: expect() matchers, mockFunction(), and test globals with documentation.
' Type "it" + Tab for a test case snippet:
it("should initialize correctly", sub()
m.player = VideoPlayer()
expect(m.player.isReady).toBe(true)
end sub)
' Type "expect" for matcher completions:
expect(result).toBe(42)
expect(result).toEqual({ id: 1 })
expect(m.mock).toHaveBeenCalled()Formatting
The extension embeds the kopytko-formatter engine. Format a file with Shift+Alt+F, or enable format-on-save. All 49 formatting options and casing settings are configurable from .vscode/settings.json.
✓ 49 formatting rules
✓ 10 independent casing dimensions
✓ Preserves @import order or sorts alphabetically
✓ Format-on-save support
✓ Respects readOnlyPaths — skips generated files
Format-on-save
GIF showing a BrightScript file being formatted on save — inconsistent whitespace and casing normalized automatically
→ site/public/screenshots/format-on-save.png
Rename symbol
Press F2 on any function name or local variable to rename it across all usages. Function renames propagate workspace-wide through the @import graph. Local variable renames are confined to the declaring scope.
Roku device discovery
Automatic SSDP-based discovery keeps your sidebar populated with every Roku device on the network. Device health is verified via ECP on each scan, and the list updates on network change or sleep/wake.
Device sidebar
Roku Devices sidebar tree showing two discovered devices: one online (green indicator) with model/firmware info, one offline (gray)
→ site/public/screenshots/device-sidebar.png
Integrated debugger
Debugger breakpoint hit
VS Code debugger paused at a breakpoint in a .brs file. Variables panel shows typed locals. Call stack shows SceneGraph threads.
→ site/public/screenshots/debugger-breakpoint-hit.png
Debug variables panel
Variables panel with expandable roAssociativeArray container, showing fields including nested objects with virtual $children and $count
→ site/public/screenshots/debug-variables-panel.png
Implements the Roku Socket Debug Protocol 3.3.0 over TCP port 8081. Deploy your channel with Ctrl+Shift+F5, then debug with F5.
- ✓ Static, conditional, and hit-count breakpoints
- ✓ Exception breakpoints (caught and uncaught)
- ✓ Multi-thread inspection: main, render, and task threads
- ✓ Step over / into / out, and pause
- ✓ Typed variable inspection with expandable containers
- ✓ Virtual variables: $children, $parent, $count on roSGNodes
- ✓ Hover-to-evaluate expressions
- ✓ Debug console REPL with EXECUTE command
- ✓ Compile errors surfaced as VS Code diagnostics
- ✓ Program output via IO channel
- ✓ Protocol-state aware: breakpoints toggled while the channel runs are replayed on the next stop, so the device is never sent a command it would answer with a connection reset
- ✓ Wire-level protocol tracing via kopytko.debug.trace for diagnosing dropped sessions
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "brightscript",
"request": "launch",
"name": "Debug on Roku",
// host and password are OPTIONAL.
// When omitted they are taken from the active device in the sidebar.
// "host": "192.168.1.100",
// "password": "rokudev",
"rootDir": "${workspaceFolder}",
// env selects a .kopytkorc environment key ("dev", "staging", ...).
// Optional — taken from the active device's environment selection if omitted.
// "env": "dev",
"stopOnEntry": false
}
]
}Device Manager
A dedicated sidebar container — drag it to the right-hand Secondary Side Bar to keep it next to your code — with two views: Remote Control (the on-screen remote, then three expanded-by-default disclosures: Saved Text entries, Device actions, then a Secret screens reference) and a RASP automation-script library compatible with Roku's official Remote Tool.
- ✓ Remote laid out like a physical Roku remote — click for keypress, hold ≥1 s for keydown/keyup (scrubbing works)
- ✓ Text field types on the device keyboard — ordered, sequential Lit_ keypresses, UTF-8 safe
- ✓ Keyboard remote mode — arrows/Enter/Escape drive the device from anywhere; status-bar indicator, Ctrl+K exits
- ✓ Device actions — expanded by default, labeled rows for web-admin actions: screenshot, upload, package, rekey, software update, restart, delete channel
- ✓ Secret screens — expanded-by-default reference list (not buttons) for 5 Roku key sequences: enable dev mode, channel info, screenshots & ads, reset & update, clear cache; typed on your physical remote
- ✓ Saved Text entries: text and credentials types with a Send button per field; passwords in the OS keychain, never in settings or the webview
- ✓ Custom labels on Saved Text (type doubles as an implicit label) and Scripts, with Filter and Sort-by-label dropdowns
- ✓ RASP script editor tab — live validation, snippet toolbar, per-step run progress with cancel
- ✓ Record remote — button presses and sent text write themselves into the open script editor, like Roku’s own tool
- ✓ Full RASP runner: press, text, pause, launch, loops, reusable blocks, wait_for_player_state, validate_streaming
- ✓ Import/export .rasp files — scripts round-trip with the Roku Remote Tool
Device Manager sidebar
Secondary sidebar with a Roku-style remote control (D-pad, media keys, text field), expanded Saved Text entries with Send buttons, an expanded Device actions list of labeled rows, an expanded Secret screens reference list, and a script list with run progress
→ site/public/screenshots/device-manager-sidebar.png
Kopytko Diagnostics (Runtime Telemetry)
A bottom-panel recorder that captures live runtime telemetry from the running channel into replayable per-session NDJSON files, charted live with D3. Every chart and table is individually toggleable — data is only polled while something visible needs it.
- ✓ Memory chart — total/anon/file/shared/swap MB with FG/BG limit lines (chanperf)
- ✓ CPU chart — total/user/sys % (chanperf)
- ✓ SceneGraph Nodes chart — stacked by node type, top 8 + Other (sgnodes counts)
- ✓ Objects chart — BrightScript object counts stacked by type via ECP app-object-counts (hidden by default)
- ✓ Texture memory chart with max reference line (r2d2_bitmaps)
- ✓ Node / Object / Rendezvous / Texture tables — the Objects table splits roSGNode rows per component subtype
- ✓ Rendezvous + framework-beacon markers overlaid on every chart
- ✓ App foreground/background shading + live state badge
- ✓ Record any channel sharing the sideloaded dev key, with past-session replay
Kopytko Diagnostics panel
Bottom panel with live D3 charts: Memory, CPU, stacked SceneGraph Nodes and Objects charts, navigator strip, and node/object/rendezvous tables below
→ site/public/screenshots/kopytko-diagnostics-panel.png
Network Inspector
A Charles-style HTTP inspector for a running channel — an intercepting proxy the device's traffic is redirected through, showing every request and response grouped by origin and path with per-request metrics. The clever part: it sees HTTPS backends decrypted without installing any CA on the Roku. The device stays on plaintext HTTP, the proxy bridges to HTTPS upstream, and response bodies are rewritten https:// → http:// so the app keeps talking HTTP.
- ✓ Pure-Node proxy — no external tools, no mitmproxy, no certificates
- ✓ One master toggle (off by default): flip on to start the proxy and apply the OS traffic redirect; flip off to fully revert
- ✓ Fully automatic transparent redirect on all three platforms out of the box — a scoped iptables chain (Linux), pf anchor (macOS), or a bundled WinDivert companion (Windows, zero setup) redirecting all of the device's traffic by source IP under one elevated prompt; teardown never touches your other firewall rules and reverts on close/crash, and the Windows companion also self-terminates if VS Code disappears without saying goodbye
- ✓ Live-editable rewrite rules — built-in https→http and wss→ws plus your own find/replace, scoped by host and path, applied without restart
- ✓ Rewrite excludes — opt specific host+path combinations out of ALL rewriting, even the built-ins, for a response that must reach the device byte-for-byte
- ✓ Per-host upstream scheme (https / http / auto); gzip/deflate/br decoded, Set-Cookie Secure stripped, redirects downgraded
- ✓ Requests grouped by origin, detail pane with headers, bodies and original-vs-rewritten view, filter, and Export HAR
- ✓ A detail pane that remembers your place — only Overview opens by default, the sections you expand stay expanded as you step between requests, and the pane scrolls back to the section you were focused on; right-click any key/value row (headers, query params, cookies, JSON-tree nodes) to copy the key, the value, or both — tree nodes copy their whole subtree as JSON
- ✓ Every capture session is saved to a timestamped folder on disk (manifest + flows.ndjson + full body files), so oversized bodies are never shown cut off — a warning links to the complete, formatted body in a new editor tab, and replays of truncated requests send the full on-disk bytes
- ✓ Per-request timing waterfall — blocked / DNS / connect / TLS / send / wait / receive as a stacked bar and table, keep-alive-aware, with the same real phases written to HAR
- ✓ Workflow tools — copy URL / copy as cURL / copy body, one-click replay through the proxy (with confirmation for state-changing methods), pause recording without dropping the redirect, timestamps, and status/method filter chips
- ✓ Map-local, latency-injection, header and block rules (block aborts a matched request so you can test error handling); image response preview + hex; parsed query-param and cookie views; and a search across every captured request that jumps straight to the flow
- ✓ Streaming pass-through — Server-Sent Events and chunked no-length responses forward chunk-by-chunk instead of buffering, so a live stream never hangs the device
- ✓ In-flight rows — a request appears the instant it arrives with a live elapsed-time counter and inspectable request headers, then updates in place when it completes, so long-polls, streams, slow endpoints and breakpoint-paused requests are visible while they run
- ✓ Compare any two captured requests — a line-level diff of summary, headers and bodies to spot exactly what changed between them
- ✓ Breakpoints — pause a matched request or response mid-flight, edit its method/status/headers/body live, then continue or abort (with an auto-continue timeout so nothing hangs)
- ✓ Built to stay fast under load — frame-batched incremental list updates, a dual entry-count + byte-budget memory cap, and pooled upstream connections (no per-request TLS handshake to origins)
The dev machine must be the device's gateway (macOS Internet Sharing, a Linux gateway, or Windows ICS). On 64-bit Windows there's nothing to install — the extension bundles a working WinDivert driver and drives it via an elevated companion process (window hidden, everything logged for auditability). kopytko.network.winDivertDir exists only to override that with a different build, or on architectures the bundled driver doesn't cover — it's machine-scoped, so it can't be pushed onto a team via a shared workspace settings.json.
Network Inspector panel
Editor tab with a capture toggle, a request list grouped by origin with method/status/duration/size columns, a detail pane showing request and response headers and JSON body, and a rewrite-rules editor
→ site/public/screenshots/network-inspector-panel.png
Kopytko Console
An interactive terminal for the Roku debug consoles, in its own bottom-panel tab beside Terminal and Problems. It replaces reaching for an external telnet client — which Windows doesn't ship at all — with a real terminal that knows which commands each port accepts and colours the output by severity, so the crash stands out from the wall of log lines. Works identically on Windows, macOS and Linux.
- ✓ Both interactive ports, connected at once — 8085 (BrightScript runtime: live print output, beacons, and the BrightScript Debugger> prompt when execution stops) and 8080 (SceneGraph debug server: chanperf, sgnodes, sgperf, r2d2_bitmaps, free); the dropdown switches the view, not the connection
- ✓ Follows the device you picked in the Roku Devices sidebar — no second device selector to fall out of sync, just the active device and a dot showing the console connection
- ✓ Completion for every documented command with descriptions, argument hints and subcommands — an empty line lists the whole port catalog, so you never have to guess a first letter, and Tab walks sgnodes all|roots|counts, sgperf start|clear|report|stop and the rest with no round-trip to the device
- ✓ Commands seen only in the device's own help output are labelled undocumented, and destructive ones (genkey, remove_plugin) need an explicit confirmation before they are sent
- ✓ Severity colouring drawn from the theme's own terminal palette — errors, warnings, framework beacons, debugger frames and XML replies each read differently, in light, dark and high-contrast alike
- ✓ Clickable pkg:/components/Foo.brs(40) references that open the file at the line, resolving through Kopytko packages in node_modules too
- ✓ Live filter — plain text or /regex/ — plus severity chips, narrowing what you see without touching what is captured
- ✓ Per-port command history, save-buffer-to-file, and optional append-as-it-arrives logging so a crash still leaves a complete record on disk
- ✓ Device output is sanitised before display, so an escape sequence in a print statement can't repaint your terminal
Only the two ports that carry an interactive command surface on current Roku OS are offered — 8089–8093 were retired in Roku OS 7.5+. Port 8085 accepts a single consumer at a time, so the console tells you when another telnet or debug session is holding it rather than retrying in silence; it also replays the channel's log from launch when you connect, and (verified on firmware 15.2.4) only accepts typed commands once execution has stopped at a debugger prompt.
Kopytko Console panel
Bottom panel tab with a port dropdown, connect button, active-device label with a green status dot, and a filter box above a terminal showing colour-coded Roku log output — red errors, yellow warnings, cyan beacons — with a completion popup listing bt, bsc, bscs and brkd with descriptions
→ site/public/screenshots/kopytko-console-panel.png
Deep Linking
Exercise your channel's deep links without hand-crafting curl commands. An editor-tab panel lists every channel installed on the active device — icons included — and lets you fire a contentId plus any key-value parameters at it via ECP Launch (POST /launch, starts or relaunches the channel) or Input (POST /input, delivered to the running channel as an roInput event — no relaunch).
- ✓ Channel grid with icons from the device (ECP /query/apps + /query/icon)
- ✓ contentId field + free-form key-value parameters, with mediaType value suggestions
- ✓ Launch and Input send modes with clear semantics for each
- ✓ Named parameter sets — save, reuse, edit, and delete; persisted per workspace
- ✓ Custom labels on saved sets, plus Filter (multi-select) and Sort-by-label dropdowns
- ✓ Follows the active device; warns when a preset’s channel is not installed
Deep Linking panel
Editor tab with a channel grid (icons), a contentId + parameters form with Launch and Send Input buttons, and a saved parameter sets list
→ site/public/screenshots/deep-linking-panel.png
SceneGraph Tree
An editor-tab panel that fetches a live SceneGraph node collection from the active Roku device — All (/query/sgnodes/all), Roots (/query/sgnodes/roots), or UI (/query/app-ui, the rendered tree of the foreground app) — and shows it as a formatted, syntax-colored XML document (default) or a D3 icicle chart.
- ✓ Three node collections: every created node, only un-parented roots, or exactly what the screen is rendering
- ✓ XML view: formatted and colorized, with a right-click Copy XML / Copy Selection menu and a Ctrl/Cmd+F find widget
- ✓ Chart view: click a node to focus its subtree, double-click to go back up, hover for an attribute tooltip
- ✓ The chosen view sticks while you switch collections; Refresh re-queries the device without reopening the tab
- ✓ Edit mode (UI + XML): edit the document in place and push field changes to the running app through an injected RALE TrackerTask — validated as you type, applied with Ctrl+Enter, every target verified before writing
SceneGraph Tree
Editor tab showing a formatted XML view of the live SceneGraph node tree, with All/Roots/UI collection buttons and an XML/Chart view toggle in the toolbar
→ site/public/screenshots/scenegraph-tree.png
Kopytko Perfetto (App Tracing)
Live Roku app tracing — right inside VS Code. Click Start and the extension deploys your channel with run_as_process=1, connects to the device's Perfetto WebSocket stream, and feeds the native binary trace into an embedded ui.perfetto.dev viewer. The trace refreshes live every few seconds.
No more record → export → import → guess. See flame charts, counter tracks, heap graphs, and rendezvous spans updating as you navigate your app. Sessions are saved as .perfetto-trace files and can be replayed later.
- Requires Roku firmware 15.2+
- Mutual exclusion with the Diagnostics panel — only one panel holds the device at a time
- Heap snapshot capture with one click
- Past-session replay from the dropdown
Using it
- Click Start to deploy and begin tracing
- Watch flame charts, counters, and rendezvous spans stream live in the embedded viewer
- Take a heap snapshot at any point with one click
- Click Stop to save the session as a
.perfetto-tracefile - Replay any past session from the dropdown
Roku Pay Web Services
Call Roku Pay's cloud API (apipub.roku.com) from an editor tab — no device needed. Every transaction-service endpoint (validate, cancel, refund, billing cycle, service credit) plus the four subscription-recovery test transitions (in-grace, on-hold, passively cancel, recover), each rendered as a typed form with a live URL preview and a full response viewer.
- ✓ Named credential profiles — the partner API key lives in the OS keychain, never in settings, logs, or the webview
- ✓ Endpoint-driven forms: typed fields (text, number, checkbox, date), required markers, endpoint descriptions and rate-limit warnings
- ✓ Accept header switch — pretty-printed JSON or raw XML responses, with embedded /Date(ms)/ tokens humanized to readable timestamps
- ✓ Full response viewer: status, duration, headers, request and response bodies
- ✓ Persistent request/response history (last 200) with per-entry delete and clear-all — the API key is always masked as ****
- ✓ Subscription-recovery testing per Roku’s guide: walk a test subscription through in-grace → on-hold → cancel → recover
Roku Pay Web Services panel
Editor tab with a credential profile picker, an endpoint dropdown with a generated form and masked URL preview, a response viewer with status and JSON body, and a request history list
→ site/public/screenshots/roku-pay-web-services-panel.png
.kopytkorc configuration
The extension reads .kopytkorc for per-project environment definitions. The JSON schema is validated inline — hover over keys to see descriptions, and you'll get diagnostics for unknown or mistyped fields.
Each environment key maps to a manifest path and optional plugin list. The active environment is selected per device from the sidebar context menu, or set via the env field in launch.json.
// .kopytkorc
{
"baseManifest": "app/manifest",
"sourceDir": "app",
"archivePath": "/dist/my_app_${args.env}.zip",
"environments": {
"dev": {
"manifest": "app/manifests/dev.manifest",
"plugins": ["dev-tools-plugin"]
},
"staging": {
"manifest": "app/manifests/staging.manifest"
},
"production": {
"manifest": "app/manifests/prod.manifest",
"plugins": ["minify-plugin"]
}
}
}Commands
| Command | Keybinding | Description |
|---|---|---|
| Upload to device | Ctrl+Shift+F5 | Build and upload the channel package to the active Roku |
| Debug device | F5 | Deploy with remotedebug=1 and start the debug session |
| Refresh devices | — | Trigger a new SSDP scan |
| Select device | — | Set the active Roku from the discovered list |
| Unset active device | — | Clear the active device selection |
| Add device | — | Add a device manually by IP address |
| Set password | — | Store device password in the OS keychain |
| Clear password | — | Remove the stored password for a device |
| Copy IP address | — | Copy the device IP address to the clipboard |
| Copy to clipboard | — | Copy selected text or device info to the clipboard |
| Set environment | — | Choose a .kopytkorc environment for a device |
| Open device portal | — | Open the Roku web dashboard in a browser |
| Read registry | — | View device registry sections in a virtual document |
| Remove device | — | Remove a manually-added or stale device from the list |
| Open Console | — | Open the Kopytko Console panel — an interactive terminal for the Roku debug consoles |
| Connect Console | — | Connect the console to the selected device and port |
| Disconnect Console | — | Close the console connection for the selected port |
| Clear Console | — | Empty the console buffer without disconnecting |
| Save Console Output | — | Write the console buffer to a .log file in the workspace |
| Select Console Port | — | Switch between port 8085 (BrightScript runtime) and 8080 (SceneGraph debug server) |
| Show Console Commands | — | Browse every documented command for the selected port as a searchable list |
| Open SceneGraph Tree | — | Fetch a node collection (sgnodes all/roots or app-ui) and show it as formatted XML or an icicle chart; Edit mode pushes field changes to the running app via RALE TrackerTask |
| Open Deep Linking | — | Launch channels with contentId / roInput deep-link parameters |
| Open Device Manager | — | Reveal the Remote Control view (remote, device actions, saved text) |
| Open Roku Pay Web Services | — | Call Roku Pay cloud endpoints with saved credential profiles and a request history |
| Toggle Remote Control Keyboard Mode | Ctrl+K (exit) | Drive the Roku with the keyboard — arrows, Enter, Escape, media keys |
| New Device Script | — | Open the RASP automation-script editor tab |
| Add / Remove favorite | — | Persist a device across restarts |
| Start / Stop Diagnostics Session | — | Start or stop a runtime telemetry recording session |
| Open Kopytko Perfetto | — | Open the Perfetto app-tracing tab |
| Start / Stop Perfetto Tracing Session | — | Deploy and begin, or end, a live Perfetto trace |
| Capture Heap Snapshot (Perfetto) | — | Trigger a heap snapshot during an active Perfetto session |
| Open Network Inspector | — | Open the HTTP capture tab — requests grouped by origin with rewrite rules |
| Toggle Network Capture | — | Start/stop the capture proxy and the OS traffic redirect |
| Clear Network Capture | — | Empty the captured request list |
Settings reference
| Setting | Type | Default | Description |
|---|---|---|---|
kopytko.languageServer.enabled | boolean | true | Enable the Kopytko language server. |
kopytko.languageServer.trace | enum | "off" | Trace LSP communication for debugging. |
kopytko.debug.trace | enum | "off" | Trace the Roku debug protocol (port 8081) to the Kopytko output channel. Use when a debug session disconnects or the channel freezes on resume. |
kopytko.imports.resolveModules | boolean | true | Resolve @import annotations from installed Kopytko NPM modules. |
kopytko.imports.sourceDir | string | "app" | Root source directory for resolving internal @import paths (matches .kopytkorc sourceDir). |
kopytko.imports.generatedPaths | array | [] | Glob patterns for `@import` paths that are generated during the build process and do not exist as source files on disk (e.g. by a Kopytko plugin or code-generation tool). Unresolved imports matching these patterns are shown as informational hints instead of warnings, so the developer is aware of the situation without it being flagged as an error. **Supported wildcards:** `*` matches any characters except `/`; `**` matches any characters including `/`. **Examples:** - `/components/generated/**` - `**/auto-generated/*.brs` |
kopytko.imports.generatedModules | array | [] | Declarations for `@import` paths that are generated during the build process and whose function names should be known to the language server. Each entry has a `path` glob pattern and a `functions` list. When a file imports a path matching `path`, the listed functions are added to the known function scope — so they are not flagged as undefined — and the import itself is shown as an informational hint rather than an unresolved warning. Use this when a plugin or code-generation tool creates `.brs` files that are not present as source files on disk but will exist at build time. **Example:** ```json [ { "path": "/components/generated/PluginApi.brs", "functions": ["PluginInit", "PluginGetData"] } ] ``` |
kopytko.imports.siblingPatterns | array | [] | Groups of file name patterns whose `.brs` files share import scope for the `import/unused` diagnostic check. Files in the same group are always loaded together by the same XML component, so an `@import` in one file is considered used if any file in the group references one of its exported functions. **Pattern syntax:** each pattern may contain a single `*` wildcard; files with the same wildcard value in the same directory are considered siblings. **Example** — Kopytko component/template pairs: ```json [ ["*.component.brs", "*.template.brs"], ["*.view.brs", "*.template.brs"] ] ``` With this config, if `Foo.component.brs` imports `helperFn` and only `Foo.template.brs` calls it, the import is not flagged as unused. |
kopytko.readOnlyPaths | array | [] | Glob patterns for files that the extension should treat as read-only. Acts as a shared fallback — applies to both formatting and linting unless overridden by `kopytko.format.readOnlyPaths` or `kopytko.lint.readOnlyPaths`. **Supported wildcards:** `*` matches any characters except `/`; `**` matches any characters including `/`. **Examples:** ```json [ "**/node_modules/**", "**/generated/**", "**/vendor/*.brs" ] ``` |
kopytko.format.readOnlyPaths | array | [] | Glob patterns for files that the formatter should skip. When set, overrides `kopytko.readOnlyPaths` for formatting. When empty, falls back to `kopytko.readOnlyPaths`. |
kopytko.lint.readOnlyPaths | array | [] | Glob patterns for files that the linter should skip. When set, overrides `kopytko.readOnlyPaths` for linting. When empty, falls back to `kopytko.readOnlyPaths`. |
kopytko.lint.rules.type/missing-return-type | enum | "warning" | Warn when a function is missing an 'as Type' return type annotation. |
kopytko.lint.rules.type/missing-param-type | enum | "warning" | Warn when a function parameter is missing an 'as Type' annotation. |
kopytko.lint.rules.component/duplicate-name | enum | "warning" | Warn when two XML files declare the same SceneGraph component name. Component names are global to the channel, so the declaration that loads last silently wins. Build pipelines that copy the source tree into an output directory will report every component as a duplicate — exclude the copy with `kopytko.lint.readOnlyPaths` (e.g. `**/out/**`). |
kopytko.casing.builtin | enum | "preserve" | Casing applied to BrightScript built-in function names when a completion is inserted. |
kopytko.casing.keyword | enum | "preserve" | Casing applied to BrightScript keywords when a completion is inserted. Acts as fallback for type, literal, and operator casing when those are not set. |
kopytko.casing.type | enum | "preserve" | Casing for BrightScript type names (`boolean`, `integer`, `string`, `void`, …). Falls back to `kopytko.casing.keyword` when not set. |
kopytko.casing.literal | enum | "preserve" | Casing for literal values (`true`, `false`, `invalid`). Falls back to `kopytko.casing.keyword` when not set. |
kopytko.casing.logicOperator | enum | "preserve" | Casing for logic operators (`and`, `or`, `not`). Falls back to `kopytko.casing.keyword` when not set. |
kopytko.casing.mathOperator | enum | "preserve" | Casing for math operators (`mod`). Falls back to `kopytko.casing.keyword` when not set. |
kopytko.casing.method | enum | "preserve" | Casing applied to component method names when a completion is inserted. |
kopytko.casing.userFunction | enum | "preserve" | Casing applied to user-defined function/sub names in completions and formatting. |
kopytko.casing.userMethod | enum | "preserve" | Casing applied to user-defined associative array method names in completions. |
kopytko.casing.exact | object | {} | Per-identifier casing overrides applied after all other casing rules. Keys are identifier names (case-insensitive), values are the exact output string. **Example:** ```json { "invalid": "Invalid", "getglobalaa": "GetGlobalAA" } ``` |
kopytko.format.indentSize | integer | 4 | Number of spaces per indentation level used by the BrightScript formatter. |
kopytko.format.useTabs | boolean | false | Use tabs instead of spaces for indentation. |
kopytko.format.lineEnding | enum | "auto" | Line ending style. 'auto' preserves the document's existing endings. |
kopytko.format.trimTrailingWhitespace | boolean | true | Remove trailing whitespace from lines. |
kopytko.format.insertFinalNewline | boolean | true | Ensure the file ends with a newline. |
kopytko.format.maxEmptyLines | integer | 2 | Maximum consecutive blank lines allowed. 0 = no limit. |
kopytko.format.emptyLinesBetweenFunctions | integer | 1 | Number of blank lines to enforce between top-level function/sub declarations. |
kopytko.format.emptyLinesBetweenMethods | integer | 1 | Number of blank lines between AA method definitions inside a builder function. |
kopytko.format.emptyLinesAtBlockBoundaries | enum | "preserve" | Blank lines at the start/end of blocks: 'strip' removes them, 'enforce' adds one. |
kopytko.format.endKeywordStyle | enum | "preserve" | Compound end-keyword style: `spaced` = `end if`, `compact` = `endif`. Applies to endif, endfunction, endsub, endwhile, endfor, endtry. |
kopytko.format.thenStyle | enum | "preserve" | Controls 'then' on if-lines. |
kopytko.format.functionVsSubForVoid | enum | "preserve" | Controls function vs sub for void procedures. ```brightscript ' "sub": converts function foo() as Void → sub foo() ' "function": converts sub foo() → function foo() as Void ' "allow-void": preserves both sub foo() and function foo() as Void ``` |
kopytko.format.spaceBeforeNamedFunctionParens | boolean | false | Space before `(` in named function/sub definitions. ```brightscript ' true: function doWork () ' false: function doWork() ``` |
kopytko.format.spaceBeforeAnonymousFunctionParens | boolean | false | Space before `(` in anonymous function expressions. ```brightscript ' true: m.handler = function () ' false: m.handler = function() ``` |
kopytko.format.spaceBeforeCallParens | boolean | false | Space before `(` in function calls. ```brightscript ' true: doWork () ' false: doWork() ``` |
kopytko.format.spaceInsideParens | enum | "never" | Spaces inside '()' in calls and definitions. |
kopytko.format.paramAlignmentStyle | enum | "preserve" | Multi-line parameter alignment style. |
kopytko.format.maxLineLength | integer | 120 | Max line length before the formatter wraps long strings. `0` = no limit. |
kopytko.format.wrapLongStrings | enum | "preserve" | How to break long string literals. - `plus`: break with `+` concatenation - `array-join`: break with `[..., ...].join("")` |
kopytko.format.associativeArrayBracketSpacing | boolean | true | Spaces inside `{}`: `{ key: value }` (true) vs `{key: value}` (false). |
kopytko.format.associativeArrayCommaSpacing | enum | "preserve" | Spaces around commas separating key-value pairs in inline associative arrays `{}`. Only applies to commas on the same line as `{` and `}` — multi-line AAs are not affected. |
kopytko.format.trailingComma | enum | "never" | Trailing comma after the last item in multi-line arrays and associative arrays. |
kopytko.format.arrayCommaStyle | enum | "preserve" | Comma separators in multi-line arrays. BrightScript allows omitting commas when items are on separate lines. ```brightscript ' "always": arr = [ 1, 2, 3, ] ' "never": arr = [ 1 2 3 ] ``` |
kopytko.format.associativeArrayCommaStyle | enum | "preserve" | Comma separators in multi-line associative arrays. BrightScript allows omitting commas when entries are on separate lines. ```brightscript ' "always": obj = { name: "foo", value: 42, } ' "never": obj = { name: "foo" value: 42 } ``` |
kopytko.format.associativeArraySingleLineThreshold | integer | 0 | Max number of keys before forcing an AA to multi-line. 0 = no limit. |
kopytko.format.arraySplitOpenBracket | boolean | false | When `true`, splits `[{` onto separate lines in multi-item arrays for better readability. ```brightscript ' true: return [ { name: "Component", }, otherItem() ] ' false: return [{ name: "Component", }, otherItem() ] ``` |
kopytko.format.spaceAroundOperators | boolean | true | Spaces around binary operators (+, -, *, /, <>, and, or, mod). |
kopytko.format.spaceAroundAssignment | boolean | true | Spaces around = in assignments. |
kopytko.format.unarySpacing | boolean | true | Space after unary 'not'. |
kopytko.format.commentStyle | enum | "preserve" | Normalize comment markers: `'` or `rem`, or preserve existing. |
kopytko.format.spaceAfterCommentMarker | boolean | true | Enforce space after `'` or `rem`: `' comment` vs `'comment`. |
kopytko.format.commentWidth | integer | 0 | Max comment line length. 0 = no limit. |
kopytko.format.sortImports | boolean | false | Sort @import statements alphabetically (module name first, then path). |
kopytko.format.emptyLineAfterImports | boolean | false | Insert blank line after the last @import line, before code starts. |
kopytko.format.emptyLineAfterFunctionOpen | boolean | false | Insert empty line after function/sub opening line. |
kopytko.format.emptyLineBeforeFunctionClose | boolean | false | Insert empty line before end function/sub. |
kopytko.format.emptyLineBeforeReturn | string | boolean | false | Empty line before `return` statements. `not-alone` skips the empty line when `return` is the only statement in its block (e.g. a guard clause like `if x then return`). |
kopytko.format.emptyLineBeforeComment | boolean | false | Enforce empty line before stand-alone comment blocks. |
kopytko.format.parenthesisIfCase | enum | "preserve" | Wrap if-condition in parentheses: 'always' adds them, 'never' removes them. |
kopytko.format.elseOnNewLine | boolean | true | Place 'else' on its own line. |
kopytko.format.forLoopSpacing | boolean | true | Enforce spaces around 'to' and 'step' in for loops. |
kopytko.format.printStatement | enum | "preserve" | Flag or remove print debug statements. |
kopytko.format.lineCommentPosition | enum | "preserve" | Move trailing comments: 'above' puts them on the line above. |
kopytko.format.stringConcatStyle | enum | "preserve" | Normalize string concatenation style. ```brightscript ' "plus": result = "hello" + " " + "world" ' "array-join": result = ["hello", " ", "world"].join("") ``` |
kopytko.format.observeFieldStyle | enum | "preserve" | Enforce `observeFieldScoped` over `observeField`. Using non-scoped `observeField` can cause memory leaks. ```brightscript ' "always-scoped": converts observeField → observeFieldScoped ``` |
kopytko.format.mPrefixStyle | enum | "preserve" | Normalize `m`-prefix field access style. ```brightscript ' "dot": m.fieldName m.top.visible ' "bracket": m["fieldName"] m.top["visible"] ``` |
kopytko.format.alignAssignments | boolean | false | Align `=` signs in consecutive assignment lines. ```brightscript ' true: name = "app" version = "1.0" count = 42 ' false: name = "app" version = "1.0" count = 42 ``` |
kopytko.format.fieldAccessConsistency | enum | "preserve" | Field access consistency on nodes. ```brightscript ' "dot": prefers dot notation m.top.visible = true value = m.top.id ' "method": prefers getField/setField m.top.setField("visible", true) value = m.top.getField("id") ``` |
kopytko.format.verifySyntax | boolean | true | Verify that formatted output re-parses to an equivalent AST before applying it, catching formatter bugs. Disable only to debug or skip that safety check. |
kopytko.deviceDiscovery.enabled | boolean | true | Enable automatic Roku device discovery via SSDP. |
kopytko.deviceDiscovery.scanTimeout | number | 5000 | Timeout in milliseconds for active SSDP device scans. |
kopytko.deviceDiscovery.showNotifications | boolean | true | Show notification messages when devices come online or go offline. |
kopytko.console.defaultPort | enum | 8085 | Debug console port the Kopytko Console selects when it opens. |
kopytko.console.autoConnect | boolean | false | Connect to the active device automatically when the Kopytko Console panel opens. |
kopytko.console.maxLines | number | 20000 | Maximum number of output lines kept per console port. Older lines are dropped from the buffer. |
kopytko.console.reconnect | boolean | true | Reconnect automatically (with backoff) when the console connection drops. |
kopytko.console.colorize | boolean | true | Colour console output by severity — errors, warnings, beacons, rendezvous logs, and debugger frames. |
kopytko.console.logToFile | boolean | false | Append every console line to a log file as it arrives, in addition to the in-memory buffer. |
kopytko.console.outputDir | string | "debug" | Folder (relative to the workspace root, or absolute) where console logs and saved output are written. |
kopytko.console.historySize | number | 200 | Number of submitted commands remembered per console port for history recall. |
kopytko.diagnostics.outputDir | string | "debug" | Folder (relative to the workspace root, or absolute) where diagnostics sessions are saved. Each session gets its own timestamped subfolder. |
kopytko.diagnostics.maxLivePoints | number | 3600 | Maximum number of samples kept in memory per stream for the live view. Older points are dropped from the live charts but remain on disk. |
kopytko.diagnostics.debugConsolePort | number | 8080 | TCP port of the Roku SceneGraph debug server used for chanperf / sgnodes / texture metrics. |
kopytko.diagnostics.collectors.memCpu.enabled | boolean | true | Collect per-channel CPU and memory usage (chanperf). |
kopytko.diagnostics.collectors.memCpu.intervalMs | number | 1000 | Polling interval in milliseconds for per-channel CPU/memory. |
kopytko.diagnostics.collectors.nodeCounts.enabled | boolean | true | Collect SceneGraph node counts by type (sgnodes counts). |
kopytko.diagnostics.collectors.nodeCounts.intervalMs | number | 2000 | Polling interval in milliseconds for node counts. |
kopytko.diagnostics.collectors.objectCounts.enabled | boolean | true | Collect BrightScript object counts by type via ECP /query/app-object-counts. Only polled while the Objects chart or table is visible in the panel (both hidden by default). |
kopytko.diagnostics.collectors.objectCounts.intervalMs | number | 2000 | Polling interval in milliseconds for object counts. |
kopytko.diagnostics.collectors.rendezvous.enabled | boolean | true | Collect render-thread rendezvous via ECP. |
kopytko.diagnostics.collectors.rendezvous.intervalMs | number | 1000 | Polling interval in milliseconds for rendezvous events. |
kopytko.diagnostics.collectors.systemMem.enabled | boolean | false | Collect device-wide memory (free). Off by default; per-channel memory is usually more useful. |
kopytko.diagnostics.collectors.systemMem.intervalMs | number | 5000 | Polling interval in milliseconds for device-wide memory. |
kopytko.diagnostics.collectors.textures.enabled | boolean | true | Collect GPU texture/bitmap memory (r2d2_bitmaps). Enabled by default; the Textures chart/table only shows when selected in the Charts/Tables dropdowns. |
kopytko.diagnostics.collectors.textures.intervalMs | number | 5000 | Polling interval in milliseconds for texture memory. |
kopytko.diagnostics.collectors.appState.enabled | boolean | true | Track app foreground/background state via ECP /query/app-state, shown as background shading on the charts. Requires "Control by mobile apps" enabled on the device — degrades to no shading (not an error) when unavailable. |
kopytko.diagnostics.collectors.appState.intervalMs | number | 2000 | Polling interval in milliseconds for app state. |
kopytko.diagnostics.collectors.fwBeacon.enabled | boolean | true | Track framework beacon markers (AppLaunch/AppResume/VODStart Initiate/Complete) via ECP /fwbeacons. Enabled by default, including the Beacons checkbox. |
kopytko.diagnostics.collectors.fwBeacon.intervalMs | number | 1000 | Polling interval in milliseconds for framework beacon events. |
kopytko.diagnostics.defaultVisibleCharts | array | ["memory","cpu","nodes"] | Charts shown by default when the diagnostics panel opens. The corresponding data is only collected while at least one visible chart or table needs it. |
kopytko.diagnostics.defaultVisibleTables | array | ["nodes","rendezvous"] | Tables shown by default when the diagnostics panel opens. |
kopytko.diagnostics.memoryLimits.backgroundMB | number | 100 | Reference line on the Memory chart for Roku's published background-app DRAM guidance. Not device-reported (unlike the foreground limit, which comes from chanperf) — Roku publishes this as a fixed recommendation that may change between OS versions, hence configurable here. |
kopytko.deviceManager.holdThresholdMs | number | 1000 | Remote Control: how long (ms) a button must be held before a press becomes a keydown/keyup hold instead of a single keypress. |
kopytko.deviceManager.runner.pollIntervalMs | number | 500 | Script runner: how often (ms) to poll the device for wait_for_player_state / validate_streaming / launch completion. |
kopytko.deviceManager.runner.waitTimeoutSec | number | 30 | Script runner: how long (seconds) wait_for_player_state and validate_streaming wait for the expected player state before failing the step. |
kopytko.perfetto.ecpPort | number | 8060 | ECP port used for Perfetto tracing control and the WebSocket connection. |
kopytko.perfetto.refreshIntervalMs | number | 3000 | How often (in milliseconds) the live buffer is pushed to the Perfetto UI iframe. |
kopytko.perfetto.startCommand | string | "" | Build and deploy command for Perfetto sessions. Defaults to `npx kopytko start` when empty. |
kopytko.network.proxyPort | number | 8888 | Local port the Network Inspector capture proxy listens on. Device traffic is redirected here. |
kopytko.network.redirectPorts | array | [80] | Device-side ports whose traffic is transparently redirected into the capture proxy (HTTP only). |
kopytko.network.maxEntries | number | 5000 | Maximum number of captured requests kept in memory before the oldest are dropped. |
kopytko.network.maxBufferBytes | number | 52428800 | Approximate memory budget in bytes for the captured-flow buffer (retained bodies + header overhead). Oldest requests are evicted when exceeded. 0 disables the byte cap; kopytko.network.maxEntries still applies. |
kopytko.network.upstreamKeepAlive | boolean | true | Reuse upstream connections (HTTP keep-alive) between the capture proxy and origin servers, avoiding a fresh TCP/TLS handshake per request. The device-side connection is always closed per request. Disable if an origin misbehaves with connection reuse. |
kopytko.network.filterToActiveDevice | boolean | true | Only show traffic originating from the active Roku device's IP (hides the dev machine's own traffic). |
kopytko.network.maxBodyBytes | number | 262144 | Maximum bytes of each request/response body retained in memory for inline display and HAR export. The full body is always forwarded to the device, and is persisted uncapped to the capture session folder (see kopytko.network.outputDir) for the “Open full body in editor” action. |
kopytko.network.outputDir | string | "debug" | Folder (relative to the workspace root, or absolute) where Network Inspector capture sessions are saved — flow metadata plus full request/response bodies. Each capture session gets its own timestamped subfolder. |
kopytko.network.defaultUpstreamScheme | enum | "https" | Scheme the proxy uses to reach an origin when the device called it over HTTP. `https` bridges the plaintext device call to a secure upstream connection. |
kopytko.network.winDivertDir | string | "" | Windows only, and usually unnecessary — this extension bundles a working WinDivert driver for 64-bit Windows. Only set this to override it with a different WinDivert build/version, or on a non-x64 architecture the bundled driver doesn't cover. A local driver path is inherently per-machine, so this can only be set in User Settings, never in a workspace/team settings.json. |
kopytko.network.rewriteRules | array | [] | Body rewrite rules applied to text request/response bodies. Each item: { enabled, direction: 'response'|'request', hostPattern?, pathPattern?, contentTypePattern?, find, replace, isRegex? }. When empty, the built-in `https://` → `http://` and `wss://` → `ws://` response rules are used. |
kopytko.network.rewriteExcludeRules | array | [] | Opt specific host+path combinations out of ALL body rewriting (every enabled rewrite rule, both directions) — for a response that must reach the device byte-for-byte even though a broad rule like the built-in https→http one would otherwise match it. Each item: { enabled?, hostPattern?, pathPattern? }. |
kopytko.network.upstreamSchemes | array | [] | Per-host upstream scheme overrides. Each item: { hostPattern, scheme: 'https'|'http'|'auto' }. |
kopytko.network.mapLocalRules | array | [] | Serve a local file or inline body instead of contacting the upstream when host/path match. Each item: { enabled?, hostPattern, pathPattern?, filePath?, body?, contentType?, status? }. filePath wins over body; a rule with neither is ignored. |
kopytko.network.latencyRules | array | [] | Delay matched responses by delayMs before sending them to the device — simulate slow backends. Each item: { enabled?, hostPattern, pathPattern?, delayMs }. |
kopytko.network.headerRules | array | [] | Add/set/remove request or response headers for matched hosts. Each item: { enabled?, direction: 'request'|'response', hostPattern?, op: 'set'|'add'|'remove', name, value? }. Proxy-managed headers (content-length, transfer-encoding, connection, host) cannot be changed. |
kopytko.network.blockRules | array | [] | Abort matched requests before they reach the upstream — the device sees a connection reset, for testing the channel's network-error handling. Each item: { enabled?, hostPattern, pathPattern? }. |
kopytko.network.breakpointRules | array | [] | Pause matched requests and/or responses so you can edit them live before continuing. Each item: { enabled?, hostPattern, pathPattern?, onRequest?, onResponse? }. |
kopytko.network.breakpointTimeoutMs | number | 30000 | How long a breakpoint pause waits for you to act before auto-continuing the request/response unmodified, so a forgotten breakpoint can't hang the device. |