Skip to content

build-skill-references.ts: import regex is blind to multi-line imports, under-reporting skill schema deps #3139

Description

@os-zhuang

Found while fixing generated-skill drift in #3138 — out of scope there (it is pre-existing and identical before/after regeneration, so it does not affect that diff), filed per Prime Directive #10.

Problem

packages/spec/scripts/build-skill-references.ts resolves the transitive schema closure with a single-line regex:

const re = /^import\s+.*\s+from\s+['"](\.[^'"]+)['"]/gm;

. does not match newlines, so a multi-line import is never seen:

import {
  FooSchema,
} from './foo.zod';        // ← invisible to the resolver

The dep is silently omitted from the skill's "Transitive dependencies" section. No warning — the file simply never enters the queue.

Impact

18 non-test multi-line local imports in packages/spec/src are currently invisible, e.g.:

api/feed-api.zod.ts       -> ../data/feed.zod
api/feed-api.zod.ts       -> ../data/subscription.zod
api/protocol.zod.ts       -> ./analytics.zod
api/protocol.zod.ts       -> ../kernel/package-registry.zod
automation/bpmn-mapping.ts -> ./control-flow.zod
integration/connector/*.zod.ts -> ../connector.zod   (×4)

(217 including .test.ts, which are irrelevant here.)

The effect is under-reporting, not a broken pointer: skills/*/references/_index.md ships to third parties via npx skills add, and an agent following the index gets an incomplete picture of the schema graph. Whether any of the 18 is reachable from a SKILL_MAP entry's closure is worth checking — that determines real-world impact.

Second, related blind spot

extractLocalImports appends .ts only when the specifier does not already end in .ts:

if (!resolved.endsWith('.ts')) resolved += '.ts';

So a .js-suffixed ESM specifier resolves to foo.js.ts, never exists, and is dropped — e.g. contracts/data-engine.ts -> ../data/index.js and conversions/apply.ts -> ./types.js.

Suggested fix

Replace the regex with a real import scan (TS AST, or at minimum a multi-line-tolerant pattern plus .js.ts specifier normalization). Note this will legitimately grow the "Transitive dependencies" section of several indexes — a gate now guards those files (#3138), so the regen must land with the fix.

Why this is safe to do now

#3138 added check:skill-refs, proven to fail on stale/missing/leftover output, so any change to the resolver will surface as drift rather than rot silently.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions