fix(coding): remediate ML-07 plugin authority

This commit is contained in:
2026-08-27 20:40:58 +08:00
parent 9407c67df2
commit cf13aa7eba
29 changed files with 1008 additions and 214 deletions

View File

@@ -1,9 +1,8 @@
import { readFile, readdir } from 'node:fs/promises';
import path from 'node:path';
import { DATA_SERVICE_PLUGIN_DEFINITION } from '../../shared/coding-plugins';
import {
BUNDLED_CODING_SKILL_IDS,
type BundledCodingSkillId,
type CodingSkillId,
} from '../../shared/coding-skills';
import type {
ProductCodingCommand,
@@ -29,7 +28,9 @@ const COMMAND_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/;
export interface ProductCodingPluginSkillSource {
id: string;
pluginId?: string;
directory: string;
available?: boolean;
/** Package-relative Skill entry; defaults to `SKILL.md`. */
entryPath?: string;
}
@@ -47,20 +48,6 @@ function frontmatterScalar(content: string, key: string): string | undefined {
return value || undefined;
}
function defaultPluginSkillSources(bundledSkillsDir: string): ProductCodingPluginSkillSource[] {
const skillId = DATA_SERVICE_PLUGIN_DEFINITION.skills[0]?.id ?? 'data-service';
return [{
id: skillId,
directory: path.join(
path.dirname(path.resolve(bundledSkillsDir)),
'coding-plugins',
'data-service',
'skills',
'data-service',
),
}];
}
function selectedSkillIds(
value: readonly string[],
allIds: readonly string[],
@@ -75,11 +62,8 @@ function selectedSkillIds(
return selected;
}
function productSkillId(id: string): BundledCodingSkillId {
// ProductCodingSkill predates package-owned Skill ids. The registry is
// the trusted projection boundary, so the runtime value may contain a
// package Skill while the shared contract is migrated by the caller.
return id as BundledCodingSkillId;
function productSkillId(id: string): CodingSkillId {
return id;
}
async function listSkillEntries(
@@ -103,7 +87,7 @@ async function listSkillEntries(
export async function listProductCodingSkills(
bundledSkillsDir: string,
selectedIds: readonly string[] = [],
pluginSkillSources: readonly ProductCodingPluginSkillSource[] = defaultPluginSkillSources(bundledSkillsDir),
pluginSkillSources: readonly ProductCodingPluginSkillSource[] = [],
): Promise<ProductCodingSkill[]> {
const pluginIds = pluginSkillSources.map(({ id }) => id);
const allIds = [...BUNDLED_CODING_SKILL_IDS, ...pluginIds];
@@ -118,7 +102,8 @@ export async function listProductCodingSkills(
})),
...pluginSkillSources,
];
return await Promise.all(sources.map(async ({ id, directory, entryPath }) => {
const visibleSources = sources.filter(({ id, available }) => available !== false || selected.has(id));
return await Promise.all(visibleSources.map(async ({ id, directory, entryPath, available = true }) => {
const location = path.resolve(directory);
const content = await readFile(path.join(location, entryPath ?? 'SKILL.md'), 'utf8');
return {
@@ -126,6 +111,8 @@ export async function listProductCodingSkills(
name: frontmatterScalar(content, 'name') ?? id,
description: frontmatterScalar(content, 'description') ?? '',
selected: selected.has(id),
available,
effective: available && selected.has(id),
location,
content,
entries: await listSkillEntries(location),
@@ -153,7 +140,7 @@ export function buildProductCodingCommandCatalog(
}
for (const skill of skills) {
const key = skill.id.toLocaleLowerCase();
if (!skill.selected || used.has(key)) continue;
if (!skill.effective || used.has(key)) continue;
used.add(key);
commands.push({
name: skill.id,