mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fix(agents): recognize package scopes and route source work
This commit is contained in:
1 parent
d6a35cfa31
commit
231ec0a513
4 files changed
+48
-2
No files matched your search
@@ -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.
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.',
|
||||
}),
|
||||
[]
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user