AKML SQL — Configuration Guide Updated

Last updated

The configuration file is stored at:

%AppData%\AKML SQL\config.json

It is created automatically on first run with all defaults. The file is written atomically (temp-file + rename) to prevent corruption.


Full Schema

{
  "configVersion": 1,
  "autoUpdateEnabled": true,
  "telemetryEnabled": true,
  "logMinimumLevel": "Debug",
  "lastUpdateCheck": null,
  "installId": "00000000-0000-0000-0000-000000000000",
  "nativeIntelliSensePrompted": false,
  "disabledNativeIntelliSense": false,
  "installedTargets": [],

  "intelliSense": {
    "enabled": true,
    "autoTrigger": true,
    "triggerDelayMs": 0,
    "afterDot": true,
    "maxSuggestions": 50,
    "fuzzyMatch": true,
    "showDataTypes": true,
    "showNullability": true,
    "showPkFk": true,
    "autoAlias": true,
    "joinAssist": true,
    "keywordCase": "Upper",
    "disableNativeIntelliSense": true
  },

  "cache": {
    "autoRefresh": true,
    "refreshIntervalSeconds": 300,
    "detectDdl": true,
    "maxDatabases": 10,
    "lazyLoadColumns": true,
    "persistToDisk": true,
    "persistPath": ""
  },

  "formatter": {
    "enabled": true,
    "activeProfile": "Khamis Style",
    "formatOnPaste": false,
    "formatOnSave": false,
    "formatOnDelimiter": false,
    "shortcutKey": "Ctrl+K, Y",
    "showProfileInStatusBar": true,
    "confirmBulkFormat": true,
    "createBackups": true,
    "respectNoformat": true,
    "handleParseErrors": true,
    "semanticValidation": true
  },

  "snippets": {
    "enabled": true,
    "showInCompletion": true,
    "triggerKey": "Tab",
    "formatOnExpand": true,
    "personalFolder": "",
    "teamFolder": "",
    "contextFilter": true,
    "surroundShortcut": "Ctrl+K, Ctrl+S",
    "trackUsage": true
  },

  "codeAnalysis": {
    "enabled": true,
    "runOnType": true,
    "runOnSave": true,
    "autoFixOnFormat": false,
    "squiggleStyle": "underline",
    "showInErrorList": true
  },

  "refactoring": {
    "previewBeforeApply": true,
    "createBackups": true,
    "formatAfterRefactor": true,
    "renameScope": "currentScript",
    "includeCommentsInRename": true,
    "includeStringLiteralsInRename": false
  },

  "postgreSql": {
    "enabled": true
  }
}

Top-Level Settings

Key Type Default Description
configVersion int 1 Schema version (for future migrations)
autoUpdateEnabled bool true Check for updates automatically: at IDE startup (every 24 h) and through the AKML SQL\Update Check scheduled task (daily and at sign-in), which also downloads, verifies and announces them. Nothing is installed without the user's click.
telemetryEnabled bool true Send anonymous error reports: Error-level log entries only (telemetryMinimumLevel), with user, machine, domain, server and database names, profile paths, connection-string values, emails and IPs replaced by placeholders before sending (TelemetryScrubber). Set by the installer's options page; on by default.
logMinimumLevel string "Debug" Serilog minimum level: Verbose, Debug, Information, Warning, Error, Fatal
lastUpdateCheck string? null ISO 8601 timestamp of the last update check
installId string (GUID) Anonymous installation identifier
nativeIntelliSensePrompted bool false Whether the native IntelliSense conflict dialog was shown
disabledNativeIntelliSense bool false Whether AKML SQL disabled the native SSMS IntelliSense via registry

intelliSense Section

Key Type Default Description
enabled bool true Master switch for all IntelliSense features
autoTrigger bool true Show completion list automatically while typing
triggerDelayMs int 0 Milliseconds after the last keystroke before typing opens the suggestions; 0 opens them at once. A dot always opens them at once
triggerDelayVersion int? (written on first load) Spec 040 marker: a config without it had the old, never-used default 100, which becomes 0 once
afterDot bool true Auto-trigger after typing . (table.column completion)
maxSuggestions int 50 Maximum items in the completion list
fuzzyMatch bool true Enable fuzzy/substring matching (not just prefix)
showDataTypes bool true Show column data types in completion details
showNullability bool true Show NOT NULL / NULL in column details
showPkFk bool true Show PK/FK indicators in column details
autoAlias bool true Suggest automatic table aliases
joinAssist bool true Suggest JOIN conditions based on FK relationships
keywordCase string "Upper" Keyword casing in completions: Upper, Lower, PascalCase, AsIs
disableNativeIntelliSense bool true Whether to disable SSMS native IntelliSense to avoid conflicts

cache Section

Key Type Default Description
autoRefresh bool true Periodically check for schema changes
refreshIntervalSeconds int 300 How often to check (used by shell; engine uses 60s for periodic refresh)
detectDdl bool true Trigger cache refresh when DDL (CREATE/ALTER/DROP) is executed
maxDatabases int 10 Maximum number of databases to keep in memory; LRU eviction applies
lazyLoadColumns bool true Load columns/FKs in Phase B (background) rather than blocking Phase A
persistToDisk bool true Persist schema cache to disk across sessions
persistPath string "" Override cache directory; empty = %LocalAppData%\AKML SQL\cache

formatter Section

Key Type Default Description
enabled bool true Master switch for the formatter
activeProfile string "Khamis Style" Name of the active formatting profile
formatOnPaste bool false Auto-format SQL when pasting into the editor
formatOnSave bool false Auto-format when saving a file
formatOnDelimiter bool false Auto-format when typing ; or GO
shortcutKey string "Ctrl+K, Y" Keyboard shortcut for Format Document
showProfileInStatusBar bool true Show active profile name in the VS status bar
confirmBulkFormat bool true Ask for confirmation before bulk-formatting multiple files
createBackups bool true Create .bak backup files before bulk format
respectNoformat bool true Honor -- noformat / -- endnoformat region comments
handleParseErrors bool true Skip files with parse errors in bulk format instead of aborting
semanticValidation bool true Run semantic round-trip validation after formatting
teamStyleFolder string "" A shared folder of team formatting styles (local or UNC path; empty = off). Its styles are listed under TEAM STYLES and are read-only when the folder can't be written to. A folder that can't be reached is skipped after 2 seconds.
formatSqlActions object (below) What Format Document and Format Selection do, whatever the style says

formatSqlActions (Options › Format › When you run Format SQL, AKML SQL will:):

Key Type Default Description
applyLayout bool true Lay out line breaks and indentation
applyCasing bool true Change keyword, function and data type case
semicolons string "style" style (the active style's own setting), insert, remove or leave statement-terminating semicolons
squareBrackets string "style" style (the active style's own setting), add, remove or leave square brackets around names. Only names (objects, columns, aliases, types) are bracketed, never keywords or built-in functions; a result that would not parse is not applied
expandWildcards bool false Replace SELECT * with the column list (needs a connection)
qualifyObjectNames bool false Add the schema to table names (needs a connection)

tabs Section

Tab colouring by environment (Options › Queries › Color). Only the environment keys are listed here.

Key Type Default Description
coloringEnabled bool true Colour query tabs by environment
gradientColors bool false Lighter top, base colour bottom
environments array PRODUCTION #FF4444, STAGING #FFB800, DEV #44BB44, AZURE #4488FF Named environments: { "name": "...", "color": "#RRGGBB" }. Older configs are given environments from their rules' label and colour when they load.
coloringRules[].pattern string Server or server-group pattern; * is a wildcard
coloringRules[].databaseName string "" When set, the rule matches that server and database
coloringRules[].environment string The environment the rule picks; its label and color are written from that environment when the Color page saves
coloringRules[].order int Evaluation order (0 first), renumbered when rules are moved

snippets Section

Key Type Default Description
enabled bool true Master switch for snippet features
showInCompletion bool true Include snippets in the IntelliSense completion list
triggerKey string "Tab" Key that expands a typed shortcode
formatOnExpand bool true Format the expanded snippet body
personalFolder string "" Override path for personal snippets; empty = %AppData%\AKML SQL\snippets\personal
teamFolder string "" Optional path for shared team snippets
contextFilter bool true Only show snippets appropriate for the current SQL clause
surroundShortcut string "Ctrl+K, Ctrl+S" Shortcut to show surround-with snippet picker
trackUsage bool true Track snippet usage counts

codeAnalysis Section

Key Type Default Description
enabled bool true Master switch for static code analysis
runOnType bool true Analyze after each keystroke (debounced)
runOnSave bool true Analyze when saving a file
autoFixOnFormat bool false Apply auto-fix actions when running Format Document
squiggleStyle string "underline" Squiggle rendering style (underline, dotted, solid)
showInErrorList bool true Show analysis issues in the VS Error List window

refactoring Section

Key Type Default Description
previewBeforeApply bool true Show a diff preview before applying any refactoring
createBackups bool true Create backup files before applying file-level refactoring
formatAfterRefactor bool true Run the formatter on modified text after applying refactoring
renameScope string "currentScript" Scope for Safe Rename: currentScript or projectDirectory
includeCommentsInRename bool true Update object names found inside SQL comments
includeStringLiteralsInRename bool false Update object names found inside string literals

postgreSql Section

Spec 042. PostgreSQL support in the engine and the web edition is one switchable unit.

Key Type Default Description
enabled bool true Read when the engine starts. When false the engine does not advertise the dialect.postgresql.v1 capability, registers no PostgreSQL-only handler, and refuses a PostgreSQL connection, connection test or database list with PostgreSQL support is switched off in this engine (postgreSql.enabled).; the web edition shows the PostgreSQL choice disabled with the reason. No session is ever of that kind, so every handler sees SQL Server work only, as before spec 042.

Remembered PostgreSQL passwords (the opt-in "Remember password" per saved connection) are kept by the engine in sql-credentials.json, the same DPAPI-protected store as SQL Server logins, under the server key pg://{host}:{port} and the user name. On the web engine service (which runs as LocalSystem) this means any administrator of that machine can read them.


ai Section

Key Type Default Description
enabled bool false Master switch for AI assistance features. Spec 037: derived on every load — true exactly when at least one agent in agents is usable; the value on disk is rewritten by normalisation
provider string "" Canonical provider id: anthropic, openai, azure, gemini, kimi, ollama, lmstudio, custom. Legacy spellings (AzureOpenAI, LMStudio) are normalised on load (spec 036). Spec 037: this and the other flat connection fields below are a derived mirror of the active agent — see Multiple agents
model string "" Model identifier (e.g. gpt-4o, claude-sonnet-4-6, kimi-latest)
apiKey string "" DPAPI-wrapped at rest (dpapi:<base64>, spec 036 FR-008) — the Options page wraps on save via ApiKeyProtector (entropy AkmlSql-ApiKey-v1). Legacy plaintext values still read correctly and are upgraded on the next save. The key is never written to logs
endpoint string "" Service endpoint; required for azure, defaulted for kimi (https://api.moonshot.ai/v1; use https://api.moonshot.cn/v1 for the mainland-China service) and ollama
maxTokens int 4096 Maximum response tokens
temperature double 0.2 Sampling temperature
timeout int 30 Request timeout in seconds; also bounds the Options "Test connection" check
retries int 2 Provider retry count
schemaContextMaxObjects int 500 Spec 036 FR-026 — the explicit schema-context budget. Every schema-aware AI request receives the full object inventory at name level up to this count; prompt-named objects are promoted to full detail additionally. When a database exceeds the budget the context is marked truncated and both the model and the user are told so
privacyMode string "schemaOnly" schemaOnly (metadata only) / full (includes query text) / anonymous (identifiers hashed — the assistant cannot see real object names; the chat panel says so and names this setting) / offline / disabled
offlineProvider / offlineModel / offlineEndpoint string "" Optional offline fallback profile
privacyConsentRequired bool true When true, cloud providers require the in-product consent before use

Schema information sent to a provider never includes table data rows — metadata only (FR-032). The chat panel and all AI commands bind to the active editor's connection; when no editor is connected the assistant says so instead of answering from an empty schema (FR-028).

Multiple agents (spec 037)

ai.agents is the truth for which provider serves AI requests. The flat provider/model/apiKey/endpoint/maxTokens/temperature/timeout/retries above are a derived mirror of the active agent, rewritten by AiAgentResolver.MirrorActiveAgent on every ConfigManager.Load (inside Normalize) and every ConfigManager.Save. The mirror keeps the file downgrade-safe: an older build reads the flat fields and ignores ai.agents, so it uses the same provider the new build would. When agents is empty the flat fields are the whole configuration and are left untouched — that is the pre-migration shape the V14 migration rescues on the next load.

Key Type Default Description
agents array [] 0–20 agent objects (V12); list order is display order
activeAgentId string "" id of the active agent. A dangling id is repaired on load to the first usable agent, or "" when there is none (V13)
featureAgents object {} Which agent serves each feature: chat, textToSql, explain, fix, optimize, indexSuggestions, ghostText — each an agent id, or "" = follow the active agent. An assignment naming a missing or disabled agent is cleared on load (V16)
fallbackOrder array [] Agent ids tried in order when the selected agent fails; the offline provider (offlineProvider above) is still tried last. Dangling, duplicate and self references are pruned on load (V17)

Each agents[] entry:

Key Type Default Description
id string (generated) 32 lowercase hex (Guid.NewGuid().ToString("N")), assigned at creation and immutable. Every reference — activeAgentId, featureAgents.*, fallbackOrder[] — is by id, never by name
name string — 1–40 chars after trim, unique case/trim-insensitively across the list
provider string — Canonical provider id, same vocabulary as the flat provider above
model string — Model identifier (free text)
apiKey string "" Same dpapi: wrapping as the flat key. Migration copies the flat value verbatim — never unwrapped, never re-wrapped
endpoint string "" Absolute URL; required for azure and custom
maxTokens int 4096 256–32768
temperature double 0.2 0.0–2.0
timeout int 30 5–300 seconds
retries int 2 0–5
enabled bool true Disabled agents are hidden from the chat picker and treated as absent by assignments and the fallback order
createdUtc string — ISO 8601 UTC creation time; informational only
health object? null Last recorded health check; null = never tested. Advisory only — it never gates a request

health carries status (unknown / ready / needsKey / failed — an unrecognised value loads as unknown, V20), checkedUtc, latencyMs (round trip of the last successful check) and message (≤ 500 chars, never contains a key).

Load-time migration and repairs all live in AiAgentResolver.Normalize, are idempotent, and never throw: V14 turns an empty agents list with a non-empty flat provider into exactly one agent named for the provider's display name, enabled, never health-tested, and active, copying the key verbatim; V15 drops malformed entries (empty or duplicate id) with a log warning naming the index; V21 drops entries beyond the 20th. An agent counts as usable when it is enabled, names a canonical provider, has a model, and carries every field its provider requires (key for the cloud providers, endpoint for azure/custom).


Per-Project Settings (.casettings)

Individual rule overrides can be placed in a .casettings JSON file anywhere in the project directory hierarchy. The engine searches from the current file's directory upward.

{
  "rules": {
    "PE001": { "severity": "Warning", "enabled": true },
    "SE001": { "severity": "Error",   "enabled": true },
    "ST001": { "enabled": false }
  },
  "globalSuppressions": [
    { "ruleId": "NM002", "reason": "Legacy naming convention" }
  ]
}

Rule severity values

"None" | "Info" | "Warning" | "Error"

Inline suppressions

-- akml-disable PE001
SELECT * FROM dbo.Orders   -- suppressed
-- akml-enable PE001

Or single-line:

SELECT * FROM dbo.Orders  -- akml-disable-line PE001

Omit the -- akml-enable and the suppression runs to the end of the file, which is how the "Disable … in this script" quick fix works. Rule ids are comma-separated (-- akml-disable PE001, BP004); omitting them entirely suppresses every rule; trailing text is treated as a note. The directives are case-insensitive, work inside /* … */, and the original -- noqa: / -- noqa-begin / -- noqa-end forms are still honoured.

Session-only suppressions

The quick-fix menu also offers Disable … for this session, which is held in the engine process and written nowhere. It ends when the IDE closes, or from the Restore button in Manage Code Analysis Rules. Because it is not persisted there is no config.json key for it.


Persistence Markers (Spec 020)

Spec 020 (SQL Prompt visual parity) introduced two state files alongside config.json:

File Purpose
%AppData%/AKML SQL/themeMigration.v1.json First-launch marker written by ThemeMigrationManager (FR-030). Records migratedAt timestamp, whether legacyColorOverrides were detected in config.json, and the migration schema version. Idempotent — presence of the file short-circuits future runs.
%AppData%/AKML SQL/editor/preview-sample.sql User-pasted custom sample SQL for the Format Styles editor's live preview pane (T069). Atomic temp-file + rename writes. If absent, the editor falls back to its built-in DefaultSampleSql constant.

Both files are written defensively — failures are caught and logged at Debug level; they never block extension startup or editor interaction.

Formatting Profiles (.akmlstyle)

Profiles are stored in:

%AppData%\AKML SQL\profiles\{name}.akmlstyle

Built-in profiles (read-only) are embedded in the extension. The profile format is a JSON file with a metadata block plus formatting option sections. See formatting.md for the full profile schema.


Web edition: editor settings (Settings › Editor)

The web edition keeps its editor options in the browser (IndexedDB database AkmlSqlWeb, store analysisSettings, key editorSettings), not in config.json. Settings › Export carries them in the editor section.

Field Default Values
FontFamily (export: fontFamily) Default Default (the theme's Consolas stack), CascadiaMono, Consolas, SystemMonospace; an unknown value reads as Default (spec 043)
FontSize 13 9 to 32 pixels
WordWrap off
TabSize 4 1 to 8

The font and the size apply to every surface that shows SQL: the editor, its completion list and hover help, SQL History's preview, the Format styles preview, snippet bodies and AI answers. Line spacing is the theme token --akml-type-editor-line-height (1.25, from docs/theme-tokens.json), not a setting.


Log Configuration

Logs are written to:

%AppData%\AKML SQL\logs\akmlsql-YYYYMMDD.log
  • Rolling interval: daily
  • Max file size: 5 MB (rolls on size limit)
  • Retained files: 10
  • Format: {Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}
  • One file per process: the file sink holds its file exclusively, so when the SSMS extension, the engine and the updater run at the same time, each one after the first rolls to the next free name — akmlsql-YYYYMMDD_001.log, _002, … After a size or day roll a process can also append to a file another process wrote earlier and has closed. Every time a process opens a log file it first writes AKML SQL {Version} log file opened by {Process} (pid {Pid}) (ProcessHeaderHooks), which is how View Logs labels the Source of each entry; its default view merges all of the newest day's files, newest first.

To change the log level without editing JSON directly, set logMinimumLevel in config.json:

{ "logMinimumLevel": "Information" }

Site settings (spec 038)

The product site (src/AkmlSql.Site) has its own owner-editable settings, stored in a site_settings table inside the same analytics.db the metrics use — not in appsettings.json. Writing to the deployed config file would restart the application on every settings change, turning a one-second toggle into a cold start.

Edit them at /admin/settings. Changes take effect on the next public request; no deploy, no restart.

Key Values Default Effect
release_visibility LatestOnly, LatestN, All LatestN How much release history /download advertises
release_visibility_count 1–50 3 Number shown when the mode is LatestN
identifiable_retention_days 1–3650 365 Days before ip and visitor_id are erased in place

Out-of-range values are rejected with a message naming the bound, never silently clamped — a clamp leaves the owner believing they saved something they did not.

If the settings table cannot be read at all, the site serves the documented defaults and reports the failure in the logs, on /health (settingsLoaded: false) and in the portal. It is never fatal: the download page must not break for a reason unrelated to releases.

Visibility governs advertising, not reachability. A release hidden from the page is still downloadable by a link published earlier, and still counted. /dl/{file} has no reference to the settings store at all, which is asserted structurally by a test.

After spec 038 the site stores, only for visitors who explicitly accept:

  • the full client IP address, and
  • a persistent first-party identifier in the akml.vid cookie.

For everyone else — declined, or not yet answered — both columns stay NULL and only the per-day salted hash, the truncated network prefix (/24 or /48) and the derived country are written, exactly as before. The gate is enforced in AnalyticsStore, not at the call site, so no caller can bypass it.

Cookie Purpose Lifetime Flags
akml.consent Remembers the choice; set whichever way the visitor answers 365 days HttpOnly, Secure, SameSite=Lax
akml.vid Persistent individual id; only set on acceptance 365 days HttpOnly, Secure, SameSite=Lax
akml.admin Owner's portal sign-in session; never set for visitors 8 hours HttpOnly, Secure, SameSite=Lax

The consent request is shown to every visitor with no recorded choice, regardless of country, and is non-blocking — a visitor can ignore it and complete the whole download path. Silence leaves the choice unresolved and is never treated as consent; an explicit dismissal records a refusal.

Two retention boundaries apply:

  • identifiable_retention_days (site setting, default 365) — ip and visitor_id are nulled in place, keeping the row, so country and version totals for old periods still reconcile.
  • Analytics:RetentionDays (appsettings.json, default 400) — whole rows deleted.

Both run in the post-start maintenance service, never inline at startup.

Geo database

Country is resolved at write time from GeoLite2-Country.mmdb. Without that file every country reads "Unknown", and installing it later cannot backfill existing rows. Obtain a MaxMind licence key and run scripts/update-geoip.ps1.