Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d3e81de
fix(completion): a !reference tag anywhere in the file breaks all inp…
X-Guardian Sep 8, 2026
9538d21
chore(deps): bump js-yaml from 4.2.0 to 4.3.2 (#264)
dependabot[bot] Sep 8, 2026
0999324
chore(deps-dev): bump fast-uri from 3.1.5 to 3.1.7 (#265)
dependabot[bot] Sep 8, 2026
ecd1aeb
chore(deps-dev): bump the dev-dependencies group with 9 updates (#266)
dependabot[bot] Sep 8, 2026
574d38d
chore(release): 0.17.0 [skip ci]
github-actions[bot] Sep 8, 2026
cabacbc
chore(deps-dev): bump mocha from 11.8.0 to 12.0.0 (#267)
dependabot[bot] Sep 8, 2026
ea9b26a
chore(release): 0.17.1 [skip ci]
github-actions[bot] Sep 8, 2026
42d310c
fix(parser): tolerate any local YAML tag, not just sequence !referenc…
X-Guardian Sep 8, 2026
aaf85b3
chore(release): 0.17.2 [skip ci]
github-actions[bot] Sep 8, 2026
8bd3ed0
fix(parser): merge YAML merge keys (`<<:`) as GitLab does (#274)
X-Guardian Sep 8, 2026
48e72c1
chore(release): 0.17.3 [skip ci]
github-actions[bot] Sep 8, 2026
dfed502
fix(providers): match cached component templatePath regardless of ref…
Cid-oe Sep 9, 2026
a83832c
chore(release): 0.17.4 [skip ci]
github-actions[bot] Sep 9, 2026
e3cac4f
fix(details): make Refresh Versions work in the browser-opened detail…
X-Guardian Sep 10, 2026
70e144c
chore(release): 0.17.5 [skip ci]
github-actions[bot] Sep 10, 2026
b020d2d
refactor(webview): serve loading-view CSS from linted external file u…
X-Guardian Sep 11, 2026
20bef37
chore(release): 0.17.6 [skip ci]
github-actions[bot] Sep 11, 2026
1688b3a
chore(ci): bump huntridge-labs/argus/.github/workflows/reusable-secur…
dependabot[bot] Sep 15, 2026
fc76bf3
chore(release): 0.17.7 [skip ci]
github-actions[bot] Sep 15, 2026
8dda61e
chore(ci): point Dependabot at beta (#296)
eFAILution Sep 15, 2026
d67019f
chore(release): 0.17.8 [skip ci]
github-actions[bot] Sep 15, 2026
dff64be
chore(deps-dev): bump the dev-dependencies group with 6 updates (#297)
eFAILution Sep 15, 2026
0e5baeb
chore(release): 0.17.9 [skip ci]
github-actions[bot] Sep 15, 2026
324dc3d
fix(webview): restore version switching in the component browser and …
X-Guardian Sep 15, 2026
4ba468a
chore(release): 0.17.10 [skip ci]
github-actions[bot] Sep 15, 2026
8a93745
fix(ai): repair dangling component references in architecture.yaml (#…
eFAILution Sep 15, 2026
7ebd81a
chore(ai): regenerate .ai/index.yaml
eFAILution Sep 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions .ai/architecture.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ components:
validationProvider.ts: Input validation with Quick Fixes
componentBrowserProvider.ts: Component browser webview UI
componentDetector.ts: Detect GitLab CI component usage in YAML
componentHtmlRenderer.ts: Render component docs as HTML
hoverContentBuilder.ts: Build the hover popup's markdown body (vscode-free, unit-tested)
depends_on:
- services
- utils
- types
- templates
- webview
services:
location: src/services/
purpose: Business logic and data management
Expand Down Expand Up @@ -77,14 +77,17 @@ components:
depends_on:
- types
- utils
templates:
location: src/templates/
purpose: HTML template generation for webview UIs
webview:
location: src/webview/
purpose: Helpers and assets for the webview documents rendered by the providers
files:
detachedComponent.ts: Detached component view template
helpers/htmlBuilder.ts: HTML construction helper
helpers/styleBuilder.ts: CSS style helper
index.ts: Public exports
webviewHtml.ts: Nonce, Content-Security-Policy and asset-URI helpers for webview documents
inlineMarkdown.ts: HTML escaping and inline-Markdown rendering (vscode-free, unit-tested)
scriptData.ts: Safe JSON serialization for embedding data in a script block (vscode-free, unit-tested)
clientInlineMarkdown.ts: Source text for the browser-side twin of renderInlineMarkdown (vscode-free, unit-tested)
styles/: Stylesheets built to out/webview/styles/ and loaded via a CSP'd link
notes: Assets under styles/ (and client/ as scripts are extracted) are built by the webview esbuild
context and resolved at runtime through assetUri; they are not bundled into out/extension.js.
depends_on:
- types
constants:
Expand Down Expand Up @@ -213,8 +216,8 @@ data_flow:
component: componentService
action: Fetch component details (cache-first)
- step: 4
component: componentHtmlRenderer
action: Render documentation as HTML using templates/helpers
component: hoverContentBuilder
action: Build the hover markdown body (a MarkdownString, not HTML)
- step: 5
component: hoverProvider
action: Display hover card
Expand Down
36 changes: 36 additions & 0 deletions .ai/decisions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,42 @@ decisions:
- .release-it/vscode-version.js
- package.json (release:main, release:beta scripts)
title: Release It
DEPENDABOT_TARGETS_BETA:
date: '2026-09-14'
status: accepted
context: Dependabot defaulted to the repository default branch (main), which is the stable
release line. Bumps opened there duplicated what had already landed on beta, and a merge
to main auto-cuts a stable release.
decision: 'Set target-branch: "beta" on every ecosystem in .github/dependabot.yml (npm,
github-actions, pip)'
rationale:
- A push to main runs release-it with .release-it.json and cuts a stable GitHub release, so
a routine dependency bump merged there ships a release out of band
- main trails beta by everything not yet released, so bumps against main duplicate versions
beta already carries (e.g. #291/#292/#295 re-proposed bumps merged weeks earlier)
- Retargeting such a PR to beta conflicts on package-lock.json, since beta has moved
- Updates reach main the same way every other change does, through the beta -> main release PR
implementation:
config: '.github/dependabot.yml, target-branch: "beta" on each of the three package-ecosystem
entries'
flow: dependabot -> beta -> release PR -> main
alternatives_considered:
- name: leave_targeting_main
rejected_because: Ships stable releases from dependency bumps and produces duplicate PRs
- name: retarget_each_pr_by_hand
rejected_because: Lockfile conflicts on every npm PR; recurring manual work
consequences:
positive:
- Dependency updates follow the same path as feature work
- No stable release cut by a dependency bump
- No duplicate bumps against a stale branch
negative:
- A security fix reaches main only when the next release PR merges
references:
- .github/dependabot.yml
- .release-it.json
- .release-it.beta.json
title: Dependabot Targets Beta
MODULAR_SERVICE_SPLIT:
date: '2026'
status: accepted
Expand Down
3 changes: 2 additions & 1 deletion .ai/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ keys:
- providers
- scripts
- services
- templates
- types
- utils
- webview
workflows:
- add_configuration_option
- add_new_command
Expand All @@ -43,6 +43,7 @@ keys:
- BATCH_API_REQUESTS
- CACHE_COMPONENTS
- CENTRALIZED_ERROR_HANDLING
- DEPENDABOT_TARGETS_BETA
- EXTENSION_HOST_TEST_LAYER
- FILE_SIZE_POLICY
- MOCHA_OVER_JEST
Expand Down
1 change: 1 addition & 0 deletions .ai/workflows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ common_commands:
debug: F5 (in VS Code)
package: npm run package
lint: npm run lint
lint_ci: npm run lint:ci (eslint + stylelint with GitHub annotation formatters; run by CI)
git_workflow:
branch_naming: feature/description or fix/description
commit_format: 'type(scope): description'
Expand Down
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@
# in osv-scanner.toml that the Argus scan honors. Do NOT add a dev-dep `ignore:` here to suppress
# those alerts -- `ignore:` also stops the version-update PRs below, which are how dev tools stay
# current and how a transitive fix (e.g. serialize-javascript >= 7.0.5) actually lands.
#
# Every ecosystem below sets `target-branch: "beta"`. Without it Dependabot opens against the default
# branch (`main`), which is the STABLE release line: a push there auto-cuts a stable GitHub release, so a
# routine dependency bump would ship one out of band. `main` also trails `beta` by whatever has not been
# released yet, so bumps opened against it duplicate what already landed on `beta` and conflict on the
# lockfile when retargeted. Everything reaches `main` through the beta -> main release PR instead.
version: 2
updates:
# Maintain npm dependencies
- package-ecosystem: "npm"
directory: "/"
target-branch: "beta"
schedule:
interval: "weekly"
day: "monday"
Expand Down Expand Up @@ -68,6 +75,7 @@ updates:
# Maintain GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "beta"
schedule:
interval: "weekly"
day: "monday"
Expand All @@ -85,6 +93,7 @@ updates:
# Maintain pre-commit hooks
- package-ecosystem: "pip"
directory: "/"
target-branch: "beta"
schedule:
interval: "weekly"
day: "monday"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
run: npm ci

- name: Run lint
run: npm run lint
run: npm run lint:ci

- name: Run compile
run: node esbuild.js
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/security-hardening.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ permissions:
jobs:
argus-hardening:
name: Argus Reusable Hardening
# Pinned to the commit for Argus v1.11.0 for supply-chain safety.
uses: huntridge-labs/argus/.github/workflows/reusable-security-hardening.yml@9b444d8975f4a2253cc53e09a7c5837e5b509d24
# SHA-pinned rather than tag-pinned for supply-chain safety: a tag can be moved, a commit cannot.
# Dependabot bumps this SHA and names the tag it resolves to in the PR title, so read the version there
# rather than trusting a hand-written one here (this comment claimed v1.11.0 through three bumps past it).
uses: huntridge-labs/argus/.github/workflows/reusable-security-hardening.yml@cc7ef8e7de85340368bf74e7c617e1a8f08ff5bb
with:
scanners: codeql,gitleaks,osv,dependency-review
enable_code_security: true
Expand Down
147 changes: 119 additions & 28 deletions esbuild.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
const esbuild = require('esbuild');
const fs = require('fs');

const production = process.argv.includes('--production');
const watch = process.argv.includes('--watch');

/**
* Emits the `[watch] build started`/`finished` markers VS Code's background problem matcher keys on.
*
* Only one context may emit them: the matcher treats the first `finished` as "task ready", so a second pair from a
* parallel build would mark the task ready while that build is still running. {@link errorReporterPlugin} covers the
* other contexts.
*
* @type {import('esbuild').Plugin}
*/
const esbuildProblemMatcherPlugin = {
Expand All @@ -14,42 +21,126 @@ const esbuildProblemMatcherPlugin = {
console.log('[watch] build started');
});
build.onEnd((result) => {
result.errors.forEach(({ text, location }) => {
console.error(`✘ [ERROR] ${text}`);
console.error(` ${location.file}:${location.line}:${location.column}:`);
});
reportErrors(result);
console.log('[watch] build finished');
});
},
};

async function main() {
const ctx = await esbuild.context({
entryPoints: ['src/extension.ts'],
bundle: true,
format: 'cjs',
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: 'node',
outfile: 'out/extension.js',
external: ['vscode'],
logLevel: 'silent',
// Additional optimizations
treeShaking: true,
metafile: production,
// Drop console logs in production for smaller bundle
drop: production ? ['console', 'debugger'] : [],
plugins: [
/* add to the end of plugins array */
esbuildProblemMatcherPlugin,
],
/**
* Reports build errors without emitting watch markers, for contexts that build alongside the marker-emitting one.
*
* @param {string} label Which build the errors came from, since the output is interleaved.
* @returns {import('esbuild').Plugin}
*/
const errorReporterPlugin = (label) => ({
name: `esbuild-error-reporter-${label}`,

setup(build) {
build.onEnd((result) => reportErrors(result, label));
},
});

/** @param {import('esbuild').BuildResult} result @param {string} [label] */
function reportErrors(result, label) {
const prefix = label ? `✘ [ERROR] [${label}] ` : '✘ [ERROR] ';
result.errors.forEach(({ text, location }) => {
console.error(`${prefix}${text}`);
if (location) {
console.error(` ${location.file}:${location.line}:${location.column}:`);
}
});
}

/**
* Build config for the Node-side extension bundle.
*/
const extensionConfig = {
entryPoints: ['src/extension.ts'],
bundle: true,
format: 'cjs',
minify: production,
sourcemap: !production,
sourcesContent: false,
platform: 'node',
outfile: 'out/extension.js',
external: ['vscode'],
logLevel: 'silent',
// Additional optimizations
treeShaking: true,
metafile: production,
// Drop console logs in production for smaller bundle
drop: production ? ['console', 'debugger'] : [],
plugins: [
/* add to the end of plugins array */
esbuildProblemMatcherPlugin,
],
};

/**
* Every webview asset to build, discovered rather than listed.
*
* A stylesheet missing from a hand-maintained list still lints and typechecks, the build still exits 0, and the
* `<link>` only 404s once the extension is packaged. Discovery keeps the build in step with the directory.
*
* Only the top level of each directory is collected, so shared modules imported by a client script are bundled into
* it rather than becoming entry points of their own. Directories that don't exist yet (`client/`, until scripts are
* extracted) contribute nothing.
*
* @returns {string[]} Paths of every asset entry point, relative to the repo root.
*/
function webviewEntryPoints() {
const assetDirs = [
{ dir: 'src/webview/styles', ext: '.css' },
{ dir: 'src/webview/client', ext: '.ts' },
];

return assetDirs.flatMap(({ dir, ext }) => {
if (!fs.existsSync(dir)) {
return [];
}
return fs
.readdirSync(dir, { withFileTypes: true })
.filter((entry) => entry.isFile() && entry.name.endsWith(ext))
.map((entry) => `${dir}/${entry.name}`);
});
}

/**
* Build config for webview assets (styles, and client scripts as they are
* added). These run in the browser-like webview context, not Node, so they
* build separately and emit under out/webview for asWebviewUri loading.
*/
const webviewConfig = {
entryPoints: webviewEntryPoints(),
bundle: true,
minify: production,
sourcemap: !production,
platform: 'browser',
// Pin the syntax level: webview assets run in the Electron renderer, not Node, so they must not inherit
// esbuild's `esnext` default.
target: ['es2020'],
format: 'iife',
outdir: 'out/webview',
// Preserve the src/webview/* folder structure (styles/, client/) under the
// output dir so asset URIs resolve to the same sub-path as the source.
outbase: 'src/webview',
logLevel: 'silent',
plugins: [errorReporterPlugin('webview')],
};

async function main() {
// Clear stale output: esbuild never removes files, so a renamed or deleted asset would leave a copy behind that
// still resolves through asWebviewUri — masking a broken reference until the extension is packaged.
fs.rmSync(webviewConfig.outdir, { recursive: true, force: true });

const ctx = await esbuild.context(extensionConfig);
const webviewCtx = await esbuild.context(webviewConfig);
if (watch) {
await ctx.watch();
await Promise.all([ctx.watch(), webviewCtx.watch()]);
} else {
await ctx.rebuild();
await ctx.dispose();
await Promise.all([ctx.rebuild(), webviewCtx.rebuild()]);
await Promise.all([ctx.dispose(), webviewCtx.dispose()]);
}
}

Expand Down
Loading