Quack: Quick QAQuack lets you define custom QA Bills: small JavaScript or TypeScript rules that run against the active document directly inside VS Code. Open any file, then click Check Bills in the Quack sidebar (or run See CHANGELOG.md for version history. Quick start
The Quack sidebarWhen a file is active, the sidebar shows:
Commands
All commands are available in the Command Palette ( AI agent toolsQuack registers three VS Code language model tools that any LM-backed agent -- including GitHub Copilot and Claude -- can invoke when you ask it to run your QA rules. No additional configuration is required; the tools are registered automatically when the extension activates.
Example promptsThe agent decides when to invoke these tools based on context. Prompts like the following will typically trigger them:
How checks and fixes workWhen an agent calls Configuring Bills (
|
| Property | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Stable unique identifier. Used to track enabled/disabled state. |
title |
string |
Yes | Display name shown in the sidebar. |
description |
string |
Short description of what the bill checks. Shown below the title (and as a tooltip in compact mode). | |
category |
string |
Group label used to visually group bills together in the sidebar. Bills with the same category are rendered under a shared collapsible heading. Bills without a category appear in the Uncategorized group. | |
order |
number |
Sort position within the sidebar. Bills with an order value are sorted numerically and appear before bills without one. Bills without an order are sorted alphabetically by title. |
|
priority |
number \| string |
Fix order priority for Fix All. Lower values are fixed first. Accepts any integer, "Infinity", or "-Infinity". Bills without a priority are fixed last. |
|
author |
string |
Author of the bill in Full Name <email@address.com> format. |
|
repository |
string |
URL to the Git repository where this bill is maintained. | |
version |
string |
Minimum Quack version required to run this bill (semver, e.g. "0.6.0"). If omitted, the bill is assumed compatible. Incompatible bills are greyed out and cannot be toggled or checked. |
|
scope |
string \| string[] |
File extension(s) or glob pattern(s) this bill applies to. Extensions are plain strings such as "html", "html,htm", or ["html","htm"]. Globs are any entry containing / or *, matched against the workspace-relative file path (e.g. "src/**/*.html"). When both extensions and globs are supplied the filter is generous -- a match on either passes. When only globs are supplied the filter is specific -- the path must match at least one glob. If omitted, the bill applies to all file types. |
|
prepare |
string \| string[] |
JavaScript or TypeScript code that runs before the check phase. All preparations run in parallel. Must return an object with at least one key — the merged results are available in check as a top-level context variable. See Prepare. |
|
check |
string \| string[] |
Yes | JavaScript or TypeScript to evaluate the document. Can be inline code, an array of lines, or a path to a .js/.ts file. Supports export default. |
fix |
string \| string[] |
JavaScript or TypeScript to fix the document. Can be inline code, an array of lines, or a path to a .js/.ts file. Supports export default. |
|
granular |
boolean |
Set to true when the fix script reads this.target to operate on only the focused occurrence. Enables the Fix Selected and Ignore Selected labels when navigating between occurrences with the <</>> arrows. Without this, both buttons show their normal labels and always operate on all occurrences. |
|
browser |
string \| string[] |
JavaScript or TypeScript that runs in the browser context each time the preview page loads. Has access to window and all browser APIs. Returns the same format as check. Results are not shown in the sidebar -- browser bills run silently in the background. Can be inline code, an array of lines, or a path to a .js/.ts file. See Browser Preview. |
|
configuration |
object |
A VS Code-style settings schema declaring the bill's configurable properties. When present, a Configure icon appears on the bill card. See Bill Configuration. | |
sideEffects |
array |
Named action buttons shown in the sidebar's Side Effects section. See Side Effects. |
Writing check
check runs when the user clicks Check Bills (or Quack: Check Bills). All enabled bills run in parallel.
All bill code fields (check, fix, prepare, conditions, effect) support TypeScript and export default. You can write bills in .ts files or use TypeScript syntax in inline code — Quack transpiles it automatically via esbuild. You can also use export default to return a value instead of a bare return statement.
Execution context
Inside check and fix, this is a context object with the following properties:
| Property | Value |
|---|---|
this.body |
The full document source as a raw string |
this.bill |
The bill object (id, title, description, etc.) |
this.config |
Resolved configuration for this bill — an object of key/value pairs merged from the bill's configuration defaults and the user's workspace settings |
this.filename |
Absolute path to the active document |
this.match(regex) |
Finds all regex matches in the document and returns { match, groups, line, column, length, start, end }[] (see below) |
context |
The merged object from all bills' prepare results. Empty {} if no bills define prepare. See Prepare |
progress(value) |
Report loading progress on the bill card — pass a number (0–100) for a determinate bar, true for indeterminate, or falsy to clear (see below) |
vscode |
The full VS Code extension API object — use for notifications, clipboard, commands, workspace access, etc. |
this.toString() |
Returns this.body — allows this to coerce to a string in template literals and concatenation |
require(id) |
CommonJS require — fs, path, and crypto are always available; fs and path are sandboxed to the active document's directory. Other modules resolve only from the bill's own directory |
Return value
check must return one of the following:
| Return value | Result |
|---|---|
{ result: 'pass' } |
Bill passed |
{ result: 'pass', message: '...' } |
Bill passed, with a message |
{ result: 'warn', message: '...' } |
Warning: bill is highlighted amber, document is not failed |
{ result: 'fail', message: '...' } |
Bill failed: document is marked FAIL |
true |
Shorthand for pass |
false |
Shorthand for fail |
(nothing / undefined) |
Quack shows a VS Code warning alert (treat this as a bug in your bill) |
Any uncaught exception is caught by Quack and surfaced as an error result (treated as a failure).
selections
Include a selections array in the returned object to indicate exactly where in the document the problem was found. When present, a Jump To link appears in the bill's sidebar card. Clicking it applies all selections at once, so multiple occurrences can be highlighted simultaneously.
Each entry in selections is an object. You can specify positions using character indexes (start/end) or line/column coordinates — start takes priority when present. Entries can also carry a message and a type to drive grouped navigation in the sidebar (see below).
// Using line/column/length
return { result: 'fail', message: 'Missing lang attribute.', selections: [{ line: 1, column: 1 }] };
// Using character indexes (from this.match results, which include start and end)
const hits = this.match(/<font[\s>]/i);
return { result: 'fail', message: `${hits.length} <font> tags.`, selections: hits };
// Multiple occurrences with line/column
return { result: 'fail', message: '3 <font> tags found.', selections: [
{ line: 4, column: 3, length: 6 },
{ line: 9, column: 1, length: 6 },
{ line: 22, column: 7, length: 6 },
] };
Selection behaviour — character index mode:
start |
end |
Cursor behaviour |
|---|---|---|
| y | y | Selects from character start to character end |
| y | — | Places cursor at character start (no selection) |
Selection behaviour — line/column mode (used when start is not present):
line |
column |
length |
Cursor behaviour |
|---|---|---|---|
| y | — | — | Selects the entire line |
| y | — | 0 |
Places cursor at the start of the line (no selection) |
| y | y | — or 0 |
Places cursor at the column (no selection) |
| y | y | > 0 |
Selects length characters from the column |
Per-selection messages and grouped navigation:
Any selection entry can include a message string and an optional type ("warn" or "fail"). When at least one selection carries a message, the sidebar switches to grouped navigation mode: selections are grouped by their message string, and each group is displayed as a labelled block with its own prev/next navigation:
Missing alt attribute
« Select 3 »
Deprecated <font> tag
« Select 2 »
The message is coloured according to type ("fail" is red, "warn" is amber). When no selections carry a message, the sidebar shows the standard single "Select N" link.
const hits = this.match(/<img(?![^>]*\salt=)[^>]*/i);
const deprecated = this.match(/<font[\s>]/i);
return {
result: 'fail',
selections: [
...hits.map(h => ({ ...h, message: 'Missing alt attribute', type: 'fail' })),
...deprecated.map(d => ({ ...d, message: 'Deprecated <font> tag', type: 'warn' })),
],
};
canFix
Return canFix: false to suppress the Fix Issue button even when the bill has fix. This is useful when the fix is only safe under certain conditions that the check can detect:
const { parse } = require('node-html-parser');
const items = parse(this.body).querySelectorAll('td[bgcolor]');
if (items.length === 0) return { result: 'pass' };
// Only offer to auto-fix if all occurrences use the same colour
const colours = new Set(items.map(el => el.getAttribute('bgcolor')));
return {
result: 'fail',
message: `${items.length} bgcolor attribute(s) found.`,
canFix: colours.size === 1, // fix is unsafe when colours differ
};
When canFix is omitted or true, the Fix Issue button is shown normally (subject to the bill having fix and the result being fail, warn, or error).
this.match(regex)
this.match searches the document for all matches of a regular expression and returns their positions. The regex does not need to be global; this.match always finds all matches.
Returns an array of match objects:
| Property | Type | Description |
|---|---|---|
match |
string |
The full matched string |
groups |
string[] |
Captured groups (same as m.slice(1)) |
line |
number |
Line number of the match |
column |
number |
Column number of the match |
length |
number |
Character length of the match |
start |
number |
Start index of the match |
end |
number |
End index of the match |
Example: fail on all <font> tags and select every occurrence:
const hits = this.match(/<font[\s>]/i);
if (hits.length > 0) {
return {
result: 'fail',
message: `<font> tag found (${hits.length} total).`,
selections: hits,
};
}
return { result: 'pass' };
progress(value)
Report loading progress on the bill's sidebar card. A 2px accent-coloured bar appears at the bottom of the card. Useful for bills that perform slow operations (network requests, large file scans, etc.).
| Call | Effect |
|---|---|
progress(50) |
Determinate bar at 50% width |
progress(33.7) |
Accepts floats for fine-grained progress |
progress(true) |
Indeterminate animated bar |
progress(100) |
Bar fills to 100%, holds briefly, then fades out |
progress(0) / progress(false) / progress(null) |
Clears the bar immediately |
Progress is automatically cleared when the bill's result is returned. If the bar is at 100% when the result arrives, it fades out gracefully before the result is displayed.
const items = parse(this.body).querySelectorAll('img');
for (let i = 0; i < items.length; i++) {
progress((i / items.length) * 100);
// ... check each image ...
}
progress(100);
return { result: 'pass' };
require(id)
Bills can load Node modules from their own directory using require. This is useful for DOM parsing, utility libraries, or shared helper files.
require is sandboxed in two ways:
fs— all file-path arguments are validated against the active document's directory. Any path that resolves outside it throws an access-denied error.path—path.resolve()is re-rooted to the active document's directory, so relative paths resolve there rather than againstprocess.cwd().- Other modules — only modules installed in the bill's own directory can be loaded. Any resolution that escapes that directory throws an error.
// Load node-html-parser from the bill's directory (npm install node-html-parser in that directory)
const { parse } = require('node-html-parser');
const lang = parse(this.body).querySelector('html')?.getAttribute('lang');
return { result: lang ? 'pass' : 'fail', message: 'The <html> element must have a lang attribute.' };
Inline example
"check": "const hits = this.match(/<html(?![^>]*\\slang=)[^>]*>/i);\nreturn { result: hits.length === 0 ? 'pass' : 'fail', message: 'The <html> element must have a lang attribute.' };"
As an alternative to escaped newlines, check and fix can be an array of strings: each item is a line of code, joined before execution:
"check": [
"const hits = this.match(/<html(?![^>]*\\slang=)[^>]*>/i);",
"return { result: hits.length === 0 ? 'pass' : 'fail', message: 'The <html> element must have a lang attribute.' };"
]
File example
Point check at a .js or .ts file in your workspace (relative path, no semicolons):
"check": "./qa/checks/lang-attr.js"
Contents of ./qa/checks/lang-attr.js:
const hits = this.match(/<html(?![^>]*\slang=)[^>]*>/i);
return {
result: hits.length === 0 ? 'pass' : 'fail',
message: 'The <html> element must have a lang attribute (e.g. lang="en").'
};
TypeScript example
"check": "./qa/checks/lang-attr.ts"
Contents of ./qa/checks/lang-attr.ts:
const hits: { line: number; column: number; length: number }[] = this.match(/<html(?![^>]*\slang=)[^>]*>/i);
export default {
result: hits.length === 0 ? 'pass' : 'fail',
message: 'The <html> element must have a lang attribute.',
selections: hits,
};
export default works in both .js and .ts files and is equivalent to a bare return.
Writing fix
fix is optional. It runs when the user clicks Fix Issue on a failed or warned bill, or when the user runs Quack: Fix All. The result is written directly back to the open document.
Quack: Fix All applies the fix for every enabled bill that has fix (and where canFix is not false), running them sequentially so each fix sees the output of the previous one.
By default, Quack re-checks bills after a fix is applied: fixing a single bill re-checks all previously checked bills (since a fix may affect other bills' results), while Fix All re-checks every enabled bill. This keeps results up to date without a manual re-check. Disable this with quack.checkAfterFix: false.
Execution context
Same as check: this is the context object, require is available, with one additional property:
| Property | Value |
|---|---|
this.target |
When the user clicks Fix Selected after navigating to a specific occurrence with the <</>> arrows, this is the SelectionTarget for that occurrence ({ start, end, line, column, length }). null when the user clicked Fix or Fix N (fix all). Use this to limit the fix to a single occurrence |
If your fix script does not check this.target, it will fix all occurrences regardless of whether the user clicked Fix or Fix Selected — which is the correct default behaviour for most bills.
To signal that your fix supports per-occurrence targeting, set "granular": true in the bill schema. This causes the sidebar to show Fix Selected and Ignore Selected (instead of Fix and Ignore) when the user has navigated to a specific occurrence. Bills without granular: true always show Fix and Ignore even in navigation mode.
Return value
fix must return the modified document string.
Example
// Inline fix: insert lang="en" if the attribute is missing
return this.body.replace(/<html(?![^>]*\slang=)/i, '<html lang="en"');
Prepare
Bills can define a prepare field — code that runs before the check phase. All preparations run in parallel, and their return values are merged into a single context object available to every check script. This is useful for expensive shared computation (e.g. DOM parsing) that multiple bills need.
Rules
preparemust return a plain object with at least one key. An empty object or non-object return is a fatal error — Quack aborts the check cycle and no bills are checked.- If two or more bills return the same key from their
prepare, a fatal error occurs (duplicate key conflict). - Preparations do not run for fixes or side effects — only for checks.
- The merged object is available in
checkas a top-levelcontextvariable (not onthis).
Execution context
prepare receives the same this context as check (this.body, this.bill, this.config, this.filename, this.match, require, vscode), but does not receive progress or context.
Example
A bill that parses the DOM once and shares it:
{
"id": "dom-parser",
"title": "DOM Parser",
"scope": "html",
"prepare": [
"const { parse } = require('node-html-parser');",
"return { dom: parse(this.body) };"
],
"check": "return true;"
}
Other bills can then use context.dom in their checks without re-parsing:
{
"id": "lang-attr",
"title": "HTML lang attribute",
"scope": "html",
"check": [
"const lang = context.dom?.querySelector('html')?.getAttribute('lang');",
"return { result: lang ? 'pass' : 'fail', message: 'Missing lang attribute.' };"
]
}
Result states
| State | Border colour | Effect on document |
|---|---|---|
pass |
Green | Bill passed. |
warn |
Amber | Concern flagged. Document is not failed. |
fail |
Red | Bill failed. Document is marked FAIL. |
error |
Red | Exception thrown in check. Treated as a failure. |
If any bill fails, the overall document status is Fail. If there are only warnings and no failures, the status is Warning. Otherwise the status is Pass.
Bill Imports (quack.billImports)
Instead of (or in addition to) defining bills inline in settings.json, you can use quack.billImports to load .json bill files by path, glob pattern, or directory. This is useful for sharing bills across projects or keeping them under version control independently.
"quack.billImports": [
"./qa/bills/lang-attr.json",
"./qa/bills/*.json",
"./qa/shared-bills",
"/absolute/path/to/bills/**/*.json"
]
Each entry can be:
- A path to a single file — loads exactly that bill.
- A glob pattern — loads all matched
.jsonfiles (e.g../qa/bills/*.json,./qa/**/*.json). - A directory path — loads all
.jsonfiles within the directory recursively.
Relative paths resolve from the workspace root. All matched bills are merged and loaded alongside any quack.bills entries. Quack watches the base directory of each pattern for changes: adding, editing, or deleting a JSON file refreshes the sidebar automatically.
Bill file format
Each .json file is a single bill. The structure is identical to an entry in quack.bills:
{
"id": "lang-attr",
"title": "HTML lang attribute",
"description": "The <html> element must declare a language.",
"check": "lang-attr.check.js",
"fix": "lang-attr.fix.js"
}
The id field is optional. If omitted, the filename without the .json extension is used as the ID (e.g. lang-attr.json -> id lang-attr).
File resolution
When check, fix, or prepare is a relative .js or .ts path in an imported bill, it resolves relative to the directory containing that bill file. This means you can keep bill logic alongside the bill definition:
qa/bills/
+-- lang-attr.json
+-- lang-attr.check.ts
+-- lang-attr.fix.ts
+-- title-tag.json
+-- title-tag.check.js
File paths in bills defined via quack.bills (in settings.json) continue to resolve relative to the workspace root.
Enabling and disabling bills
Each bill has a toggle in the sidebar. Disabled bills are skipped when running Check Bills. The enabled/disabled state is stored per-workspace in .vscode/settings.json under quack.billsDisabled and persists across sessions. This setting is not shown in the Settings UI and is managed entirely by the sidebar toggles.
Bill Configuration
Bills can declare configurable properties using a configuration object that follows VS Code's settings schema format. When a bill has configuration, a gear icon appears in the bottom-right corner of its card in the sidebar. Clicking the icon opens .vscode/settings.json (creating it if needed) and pre-populates the bill's configuration section with default values so the user can see what is available and start editing immediately.
Declaring configuration
Add a configuration object to the bill. Each key is a setting name; the value is a schema descriptor:
{
"id": "lang-attr",
"title": "HTML lang attribute",
"configuration": {
"lang": {
"type": "string",
"default": "en",
"description": "Expected value for the lang attribute on <html> (e.g. \"en\", \"fr\")."
}
},
"check": [
"const hits = this.match(/<html(?![^>]*\\slang=)[^>]*>/i);",
"if (hits.length === 0) return { result: 'pass' };",
"return { result: 'fail', message: `lang attribute missing — expected lang=\"${this.config.lang}\".` };"
],
"fix": [
"return this.body.replace(/<html(?![^>]*\\slang=)/i, `<html lang=\"${this.config.lang}\"`);"
]
}
Configuration schema properties
Each property in configuration can use the following fields:
| Field | Type | Description |
|---|---|---|
type |
string |
Data type: "string", "number", "boolean", "array", or "object" |
default |
any | Default value used when the user has not overridden the setting |
required |
boolean |
When true, the user must supply a value in quack.billConfig before the bill will run. Bills with unsatisfied required properties are greyed out and excluded from checks and fixes |
description |
string |
Short plain-text description shown in the settings file |
markdownDescription |
string |
Markdown description (use instead of description for richer text) |
enum |
any[] |
Allowed values |
enumDescriptions |
string[] |
Human-readable labels for each enum value |
minimum |
number |
Minimum value (numeric properties) |
maximum |
number |
Maximum value (numeric properties) |
Using this.config in check and fix
Configuration is available as this.config — a plain object of key/value pairs. Values are taken from the user's workspace settings if present, falling back to the schema defaults.
// this.config.lang resolves to "en" unless the user has overridden it
const hits = this.match(/<html(?![^>]*\slang=)[^>]*>/i);
if (hits.length === 0) return { result: 'pass' };
return { result: 'fail', message: `lang attribute missing — expected lang="${this.config.lang}".` };
Where values are stored
User-supplied values live in .vscode/settings.json under quack.billConfig:
{
"quack.billConfig": {
"lang-attr": {
"lang": "fr"
}
}
}
This is workspace-scoped, so each project can have its own overrides independently of others.
Side Effects
Bills can define side effects — named action buttons that appear in a dedicated collapsible "Side Effects" section above the Bills list. A bill can provide side effects alongside checks and fixes, or only side effects. The section includes a search bar and a category filter (funnel icon) for filtering by category. Side effects are sorted by category, then by name.
Each side effect is an object in the bill's sideEffects array:
| Property | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Display name for the button |
icon |
string |
Codicon icon name (e.g. "file-code", "refresh") |
|
category |
string |
Category for filtering and search. If omitted, inherits the parent bill's category | |
scope |
string \| string[] |
File extension(s) or glob pattern(s) this side effect applies to. Follows the same rules as the bill-level scope field. Use "browser" to make this a browser-context side effect (see below). If omitted, the side effect appears for all file types |
|
conditions |
string \| string[] |
JavaScript or TypeScript condition code. Return true to enable the button, false to disable it. Evaluated after each check cycle. If omitted, the button is always enabled |
|
effect |
string \| string[] |
JavaScript or TypeScript code to execute when the button is clicked. If a string is returned, the document content is replaced. Supports export default. Required for non-browser side effects |
|
browser |
string \| string[] |
JavaScript or TypeScript code to run in the browser context when this side effect is triggered. Has access to window and all browser APIs. Required when scope is "browser". See Browser Preview |
Execution context
Side effect code receives the same execution context as check/fix — the this context object, require, progress, and vscode.
Return value
effect can return:
| Return value | Effect |
|---|---|
string |
Replaces the document content |
{ body: string } |
Replaces the document content |
{ selections: Array } |
Creates selections in the editor (same format as check selections) |
{ body: string, selections: Array } |
Replaces the document, then creates selections in the updated content |
| (nothing) | No document changes |
When both body and selections are returned, the body is applied first so that selection positions refer to the new content.
Example
{
"id": "html-tools",
"title": "HTML Tools",
"check": "return true;",
"sideEffects": [
{
"name": "Minify HTML",
"icon": "file-binary",
"effect": [
"return this.body.replace(/\\n\\s*/g, '');"
]
},
{
"name": "Copy to Clipboard",
"icon": "clippy",
"conditions": "return this.body.length > 0;",
"effect": [
"await vscode.env.clipboard.writeText(this.body);",
"vscode.window.showInformationMessage('Copied to clipboard!');"
]
}
]
}
The Side Effects section can be hidden entirely with quack.showSideEffects: false. If no bills define side effects, the section does not appear.
Browser Preview
Click the Preview button in the Quack sidebar header (or run Quack: Open Browser Panel) to open a live preview of the active document in a VS Code panel. The document is served from a local HTTP server. The panel updates as you type (debounced 300 ms), serving the unsaved content directly from memory. When the file is saved, the memory buffer is cleared and the panel reloads from disk.
When the Browser Preview panel is focused, the sidebar will clear its bill list if the current file has no browser bills defined.
Browser bills
Add a browser field to any bill to run JavaScript or TypeScript inside the preview page on each load. Browser bill code runs in the live browser context and has access to window, document, and all other browser APIs.
Browser bills return the same format as check:
{
"id": "layout-check",
"title": "Layout check",
"browser": [
"const main = document.querySelector('main');",
"return { result: main ? 'pass' : 'fail', message: 'Page must contain a <main> element.' };"
]
}
Results are not displayed in the sidebar -- browser bills run silently in the background on every page load.
Browser-context side effects
Side effects with scope: "browser" target the browser context instead of the editor. They use a browser property (not effect) for their code, which runs inside the preview window. Browser side effects appear in the sidebar's Side Effects section only while the Browser Preview panel is open.
{
"id": "browser-tools",
"title": "Browser Tools",
"sideEffects": [
{
"name": "Log performance",
"icon": "pulse",
"scope": "browser",
"browser": [
"const entries = window.performance.getEntriesByType('resource');",
"console.table(entries.map(e => ({ name: e.name, duration: e.duration })));"
]
}
]
}
Context menu
Right-clicking any element in the Browser Preview panel opens a context menu. The available items depend on the element type:
| Item | Available on | Action |
|---|---|---|
| Copy Text | All elements | Copies the element's innerText to the clipboard |
| Copy HTML | All elements | Copies the element's innerHTML to the clipboard |
| Copy Image URL | Images (or children of images) | Copies the image src to the clipboard |
| Copy URL | Links (or children of links) | Copies the link href to the clipboard |
| Copy Source | Video/audio elements | Copies the media src to the clipboard |
| Find in Code | Elements with source data | Selects the element's source range in the active editor |
| Inspect | All elements | Highlights the element with a 3-second blue outline, logs it to the browser console, and opens VS Code developer tools for the webview |
Find in Code is only available when Quack can determine the element's position in the source file. It is supported for static HTML files served by Quack's built-in server. PHP and other dynamic modes do not currently support it.
CMD/CTRL+click
Hold Cmd (Mac) or Ctrl (Windows/Linux) and click any link in the preview to open it in the system's default browser instead of navigating the preview.
Browser dialog APIs
Browser bill code runs after the page has fully loaded, so window.load listeners will not fire. Use top-level code instead.
As alert, prompt, and confirm are not available in the VS Code webview sandbox, Quack replaces them with VS Code equivalents:
| Call | VS Code equivalent |
|---|---|
alert(message) |
Info notification (fire-and-forget) |
await prompt(message, default) |
Input box -- returns the entered string, or null if cancelled |
await confirm(message) |
Modal message -- returns true (OK) or false (Cancel) |
Because prompt and confirm are asynchronous, bill code that uses them must await their return values:
{
"id": "confirm-example",
"title": "Confirm example",
"sideEffects": [
{
"name": "Rename heading",
"scope": "browser",
"browser": [
"const h1 = document.querySelector('h1');",
"const text = await prompt('New heading text', h1?.textContent ?? '');",
"if (text && h1) h1.textContent = text;"
]
}
]
}
Settings reference
| Setting | Type | Default | Description |
|---|---|---|---|
quack.bills |
array |
[] |
Bills defined inline in settings.json. |
quack.billImports |
array |
[] |
Glob patterns, file paths, or directory paths pointing to .json bill files. Bill code can reference .js or .ts files. |
quack.workspaceBills |
boolean |
false |
When enabled, automatically loads .json bill files from the .quack directory at the workspace root (top-level only). Script paths within each bill resolve relative to the .quack directory. |
quack.billsDisabled |
array |
[] |
IDs of bills that are currently disabled. Written to .vscode/settings.json (workspace scope). Managed by the sidebar toggles -- not shown in the Settings UI. |
quack.autoCheck |
boolean |
false |
Re-run all enabled bills automatically after each edit (debounced 500 ms). |
quack.checkAfterFix |
boolean |
true |
Re-check bills automatically after applying a fix. Fix on a single bill re-checks all previously checked bills; Fix All re-checks every enabled bill. |
quack.showSideEffects |
boolean |
true |
Show the Side Effects section in the sidebar. |
quack.compactMode |
boolean |
false |
Condense bill cards: hides descriptions and reduces padding. Bill descriptions are still visible as tooltips. |
quack.fileExtensions |
array |
[] |
File extensions Quack activates on (e.g. "html", ".js"). Leave empty to activate on all file types. |