From 231ec0a513a4bb669e178b19d7348347fac3c23a Mon Sep 17 00:00:00 2001 From: 4gray Date: Sun, 20 Sep 2026 21:11:28 +0200 Subject: [PATCH] fix(agents): recognize package scopes and route source work --- docs/development/agent-workflow.md | 2 ++ docs/maintenance/agent-context-map.md | 2 +- tools/skills/validate-agent-guidance.mjs | 28 ++++++++++++++++++- tools/skills/validate-agent-guidance.test.mjs | 18 ++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/docs/development/agent-workflow.md b/docs/development/agent-workflow.md index 753ea009a..526f61b07 100644 --- a/docs/development/agent-workflow.md +++ b/docs/development/agent-workflow.md @@ -53,6 +53,8 @@ as Markdown links, including decoded attributes and fragment validation. Image references check file existence without interpreting image fragments as Markdown headings; document links keep anchor checks even when sharing a target. Inline guidance imports are rejected after punctuation as well as whitespace. +Scopes declared by package dependencies or TypeScript path aliases are recognized +as package/alias mentions rather than additional file imports. The import scan includes visible HTML text and excludes code and non-rendered containers. Image source sets use `parse-srcset` to check each candidate URL. Root-relative literals never suppress source-relative definition checks. diff --git a/docs/maintenance/agent-context-map.md b/docs/maintenance/agent-context-map.md index ab14e114d..e6ccb1076 100644 --- a/docs/maintenance/agent-context-map.md +++ b/docs/maintenance/agent-context-map.md @@ -22,7 +22,7 @@ are not prerequisites for reading repository contracts. | Area / code ownership | Required contracts | Repository skill | | --- | --- | --- | | SQLite schema/startup; `libs/shared/database`; Electron DB events/workers/operations | [DB worker](../architecture/sqlite-db-worker.md), [migration ownership and tests](../../libs/shared/database/README.md) | [SQLite worker](../../.codex/skills/iptvnator-sqlite-db-worker/SKILL.md) | -| M3U import/state/player, XMLTV, source lifecycle, startup readiness and OS file opening; `libs/m3u-state`, `libs/playlist`, `libs/epg` | [M3U module](../architecture/m3u-playlist-module.md) | Read the contract directly | +| M3U import/state/player, XMLTV, source lifecycle, startup readiness and OS file opening; `libs/m3u-state`, `libs/playlist`, `libs/epg` | [M3U module](../architecture/m3u-playlist-module.md), [adding sources across layers](../development/agent-workflow.md#adding-behavior-across-layers) | Read both contracts when adding a source | | Xtream API/store/data sources and routed views; `libs/portal/xtream` | [Xtream compatibility](../architecture/xtream-portal-compatibility.md), [category management](../architecture/category-management.md), [detail navigation](../architecture/portal-detail-navigation.md) | [Xtream](../../.codex/skills/xtream-electron/SKILL.md) | | Stalker/Ministra protocol, identity, sessions and routed views; `libs/portal/stalker` | [Stalker portal](../architecture/stalker-portal.md), [Stalker EPG](../architecture/stalker-epg.md) for EPG work, [store API baseline](../architecture/stalker-store-api-baseline.md) for store API changes | [Stalker](../../.codex/skills/stalker-portal/SKILL.md) | | Browser runtime, HTTP proxies, redirects and backend networking; `apps/web-backend`, `libs/shared/host-health` | [PWA/self-hosting](../architecture/pwa-self-hosted.md), [connectivity guard](../architecture/host-connectivity-guard.md), [Electron security](../architecture/electron-security.md) for desktop boundary changes | Read the affected runtime contract | diff --git a/tools/skills/validate-agent-guidance.mjs b/tools/skills/validate-agent-guidance.mjs index be5842102..16c63dad4 100644 --- a/tools/skills/validate-agent-guidance.mjs +++ b/tools/skills/validate-agent-guidance.mjs @@ -67,9 +67,35 @@ async function validateReference( } } +async function packageScopes(rootDir) { + async function readJson(path) { + try { + return JSON.parse(await readFile(resolve(rootDir, path), 'utf8')); + } catch (error) { + if (error.code === 'ENOENT') return {}; + throw error; + } + } + const manifest = await readJson('package.json'); + const config = await readJson('tsconfig.base.json'); + const names = [ + ...Object.keys(manifest.dependencies ?? {}), + ...Object.keys(manifest.devDependencies ?? {}), + ...Object.keys(manifest.optionalDependencies ?? {}), + ...Object.keys(manifest.peerDependencies ?? {}), + ...Object.keys(config.compilerOptions?.paths ?? {}), + ]; + return new Set( + names + .filter((name) => /^@[^/]+\//u.test(name)) + .map((name) => name.slice(1, name.indexOf('/'))) + ); +} + export async function validateAgentGuidance({ rootDir }) { rootDir = await realpath(rootDir); const diagnostics = []; + const scopes = await packageScopes(rootDir); for (const source of SURFACES) { let markdown; try { @@ -103,7 +129,7 @@ export async function validateAgentGuidance({ rootDir }) { .map((match) => match[1]) .filter( (token) => - !token.startsWith('iptvnator/') && + !scopes.has(token.split('/')[0]) && (/[./\\]/u.test(token) || /^(?:LICENSE|Makefile|Dockerfile|AGENTS|CLAUDE)(?:$|[.,;)])/u.test( token diff --git a/tools/skills/validate-agent-guidance.test.mjs b/tools/skills/validate-agent-guidance.test.mjs index 6cce18b55..6dfeea6c0 100644 --- a/tools/skills/validate-agent-guidance.test.mjs +++ b/tools/skills/validate-agent-guidance.test.mjs @@ -12,6 +12,9 @@ async function fixture(t, overrides = {}) { t.after(() => rm(rootDir, { recursive: true, force: true })); for (const [path, body] of Object.entries({ 'AGENTS.md': '# Guidance\n', + 'tsconfig.base.json': JSON.stringify({ + compilerOptions: { paths: { '@iptvnator/*': ['libs/*'] } }, + }), 'CLAUDE.md': '@AGENTS.md\n', [map]: '# Context\n', [migration]: '# Migration\n', @@ -704,3 +707,18 @@ test('non-rendered HTML text and code do not introduce imports', async (t) => { [] ); }); + +test('declared scoped dependencies and aliases are not inline imports', async (t) => { + assert.deepEqual( + await diagnostics(t, { + 'package.json': JSON.stringify({ + dependencies: { '@angular/core': '*', '@nx/devkit': '*' }, + }), + 'tsconfig.base.json': JSON.stringify({ + compilerOptions: { paths: { '@custom/*': ['libs/*'] } }, + }), + 'AGENTS.md': 'Use @angular/core, @nx/* and @custom/services.', + }), + [] + ); +});