Sign In

@double-coding/flow2spec-core

Package Overview
Dependencies
Maintainers
2
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@double-coding/flow2spec-core - npm Package Compare versions

Comparing version
3.3.1
to
3.4.0
+327
index.d.ts
export type Flow2SpecLocale = "zh-CN" | "en-US";
export type Flow2SpecHost = "dsh" | "cursor" | "claude" | "codex";
export type JsonPrimitive = string | number | boolean | null;
export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };
export interface Flow2SpecProjectConfig {
subAgent?: boolean;
switchAgentVerification?: boolean;
intentRecognition?: boolean;
locale?: Flow2SpecLocale;
changeTracking?: {
feat?: boolean;
fix?: boolean;
implement?: boolean;
[key: string]: boolean | undefined;
};
updateCheck?: {
enabled?: boolean;
[key: string]: JsonValue | undefined;
};
collaboration?: {
enabled?: boolean;
developerId?: string;
[key: string]: JsonValue | undefined;
};
[key: string]: unknown;
}
export interface CreateFlow2SpecOptions {
cwd?: string;
signal?: AbortSignal;
onProgress?: (event: unknown) => void;
}
export interface ProjectInitOptions {
mode?: "native-host" | "project-adapter" | string;
integrations?: Flow2SpecHost[] | string[];
locale?: Flow2SpecLocale;
overwriteKnowledge?: boolean;
configValues?: Partial<Flow2SpecProjectConfig>;
[key: string]: unknown;
}
export interface ProjectInitResult {
ids: string[];
mode: string;
overwriteKnowledge: boolean;
locale: Flow2SpecLocale;
projectConfig: Flow2SpecProjectConfig;
[key: string]: unknown;
}
export interface ProjectInspection {
cwd: string;
config: Flow2SpecProjectConfig;
}
export interface RoutingRule {
task?: string;
matcherId?: string;
matcherPath?: string;
topics?: string[];
[key: string]: JsonValue | undefined;
}
export interface RoutingCandidate {
rule: RoutingRule | null;
score: number;
order?: number;
confidence: "high" | "medium" | "low";
matchedPhrases: string[];
topics: string[];
fallback?: boolean;
}
export interface RoutingMatchInput {
request?: string;
query?: string;
task?: string;
}
export interface RoutingMatchResult {
request: string;
task: string | null;
primary: RoutingCandidate;
alternatives: RoutingCandidate[];
candidates: RoutingCandidate[];
manifestVersion: string;
topics?: string[];
}
export interface RoutingMissingContext {
kind: "topic" | "context";
id?: string;
path: string | null;
}
export interface RoutingVerification {
ok: boolean;
missing: RoutingMissingContext[];
confidence: "high" | "medium" | "low";
fallback: boolean;
}
export interface RoutingContextFile {
topic: string;
path: string;
content: string;
truncated: boolean;
}
export interface RoutingContext {
files: RoutingContextFile[];
lineCount: number;
truncated: boolean;
}
export interface KnowledgeValidation {
ok: boolean;
issues: string[];
warnings: string[];
topicCount: number;
}
export type KnowledgeDeltaChangeType =
| "createTopic"
| "appendBody"
| "replaceBody"
| "updateFrontmatter";
export interface KnowledgeDeltaChange {
type: KnowledgeDeltaChangeType | string;
targetTopic: string;
content?: string;
frontmatter?: Record<string, JsonValue>;
[key: string]: unknown;
}
export interface KnowledgeDelta {
taskId: string;
developerId: string;
baseRevisions: Record<string, number>;
changes: KnowledgeDeltaChange[];
notes?: string;
}
export interface KnowledgePlanResult {
delta: KnowledgeDelta;
plan: unknown[];
conflicts: unknown[];
mergeable: boolean;
[key: string]: unknown;
}
export interface KnowledgeApplyResult {
dryRun: boolean;
changedFiles: string[];
plan: unknown[];
[key: string]: unknown;
}
export interface DeveloperContext {
developerId: string | null;
source: "config" | "git-email" | "git-email-hash" | "git-name" | "git-name-hash" | "legacy";
legacy: boolean;
taskRoot: string;
enabled: boolean;
warnings: string[];
}
export type DoctorCheckStatus = "pass" | "warning" | "error";
export interface DoctorCheck {
id: string;
label: string;
status: DoctorCheckStatus;
message: string;
repair: string | null;
details?: unknown;
}
export interface DoctorReport {
ok: boolean;
package: { name: string; version: string };
cwd: string;
summary: { passed: number; warnings: number; errors: number };
checks: DoctorCheck[];
}
export interface CapabilityDefinition {
id: string;
api: string;
since: string;
}
export interface CapabilityManifest {
schema: "flow2spec.capabilities.v1" | string;
protocolVersion: number;
package: string;
capabilities: CapabilityDefinition[];
}
export interface HostResourceOptions {
host: Flow2SpecHost;
locale?: Flow2SpecLocale;
projectConfig?: Flow2SpecProjectConfig;
}
export interface Flow2SpecTextResource {
relativePath: string;
content: string;
mediaType: "text/markdown";
}
export interface Flow2SpecSkillResource {
name: string;
description: string;
content: string;
relativePath: string;
resources: readonly Flow2SpecTextResource[];
}
export type UpdateCheckStatus =
| "disabled"
| "skipped"
| "current"
| "upgrade-available"
| "unavailable";
export interface UpdateCheckOptions {
packageName?: string;
force?: boolean;
signal?: AbortSignal;
timeout?: number;
}
export interface UpdateCheckResult {
status: UpdateCheckStatus;
checked: boolean;
fromCache: boolean;
packageName: string;
manifestVersion: string | null;
latestVersion: string | null;
needsUpgrade: boolean;
notice: string;
checkedAt: number | null;
reason: string | null;
}
export interface Flow2SpecApi {
context: {
cwd: string;
signal?: AbortSignal;
onProgress: (event: unknown) => void;
};
project: {
init(options?: ProjectInitOptions): Promise<ProjectInitResult>;
inspect(): ProjectInspection;
};
config: {
load(): Flow2SpecProjectConfig;
missingFields(): unknown[];
};
routing: {
graph(): unknown;
state(): { graph: unknown; validation: KnowledgeValidation };
match(input?: RoutingMatchInput): RoutingMatchResult;
expand(result: RoutingMatchResult): RoutingMatchResult & { topics: string[] };
verify(
result: RoutingMatchResult,
options?: { requiredContext?: string[] },
): RoutingVerification;
loadContext(
result: RoutingMatchResult,
options?: { maxFiles?: number; maxLines?: number },
): RoutingContext;
};
knowledge: {
status(options?: Record<string, unknown>): unknown;
check(options?: { strict?: boolean; strictRevision?: boolean }): KnowledgeValidation;
plan(options?: { delta?: KnowledgeDelta; deltaFile?: string }): KnowledgePlanResult;
apply(options?: {
delta?: KnowledgeDelta;
deltaFile?: string;
dryRun?: boolean;
planHash?: string;
[key: string]: unknown;
}): KnowledgeApplyResult;
build(options?: Record<string, unknown>): unknown;
};
collaboration: {
resolveDeveloper(options?: Record<string, unknown>): DeveloperContext;
};
doctor: {
run(options?: Record<string, unknown>): DoctorReport;
};
resources: {
root: string;
capabilities(): CapabilityManifest;
listSkills(locale?: Flow2SpecLocale): string[];
listRules(locale?: Flow2SpecLocale): string[];
listHooks(locale?: Flow2SpecLocale): string[];
read(relativePath: string, locale?: Flow2SpecLocale): string;
skillCatalog(options: HostResourceOptions): Flow2SpecSkillResource[];
unifiedEntry(options: HostResourceOptions): string;
};
update: {
check(options?: UpdateCheckOptions): Promise<UpdateCheckResult>;
};
}
export class Flow2SpecError extends Error {
constructor(
code: string,
message: string,
details?: Record<string, unknown>,
options?: { recoverable?: boolean },
);
code: string;
details: Record<string, unknown>;
recoverable: boolean;
}
export function createFlow2Spec(options?: CreateFlow2SpecOptions): Flow2SpecApi;
export function getCapabilities(): CapabilityManifest;
export const resourcesRoot: string;
export const legacy: Record<string, unknown>;
"use strict";
const fs = require("fs");
const path = require("path");
const { normalizeLocale, DEFAULT_LOCALE } = require("./flow2specConfig");
const SUPPORTED_HOSTS = new Set(["dsh", "cursor", "claude", "codex"]);
const HOST_PATHS = {
dsh: { rules: "rules", skills: "skills", ruleExtension: ".md" },
cursor: { rules: ".cursor/rules", skills: ".cursor/skills", ruleExtension: ".mdc" },
claude: { rules: ".claude/rules", skills: ".claude/skills", ruleExtension: ".md" },
codex: { rules: ".codex/topics", skills: ".codex/skills", ruleExtension: ".md" },
};
function resourceError(code, message, details = {}) {
const error = new Error(message);
error.code = code;
error.details = details;
return error;
}
function normalizeOptions(options = {}) {
const host = String(options.host || "").trim().toLowerCase();
if (!SUPPORTED_HOSTS.has(host)) {
throw resourceError(
"F2S_INVALID_ARGUMENT",
`unsupported resource host: ${options.host || "<empty>"}`,
{ field: "host", supported: Array.from(SUPPORTED_HOSTS) },
);
}
return {
host,
locale: normalizeLocale(options.locale, DEFAULT_LOCALE),
projectConfig: options.projectConfig,
};
}
function templatesDir(templatesRoot, locale) {
return path.join(templatesRoot, locale);
}
function parseSkillDocument(raw, relativePath) {
const match = String(raw).match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
if (!match) {
throw resourceError("F2S_RESOURCE_INVALID", `skill frontmatter missing: ${relativePath}`, {
relativePath,
});
}
const frontmatter = {};
for (const line of match[1].split(/\r?\n/)) {
const separator = line.indexOf(":");
if (separator < 0) continue;
const key = line.slice(0, separator).trim();
let value = line.slice(separator + 1).trim();
if (
value.length >= 2 &&
((value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'")))
) {
value = value.slice(1, -1);
}
frontmatter[key] = value;
}
if (!frontmatter.name || !frontmatter.description) {
throw resourceError(
"F2S_RESOURCE_INVALID",
`skill name or description missing: ${relativePath}`,
{ relativePath },
);
}
return {
name: frontmatter.name,
description: frontmatter.description,
body: raw.slice(match[0].length),
};
}
function ruleReferenceIds(content) {
const ids = new Set(["f2s-flow2spec-unified-entry", "f2s-config-check"]);
const pattern = /(?:(?:\.codex|\.dsh)\/topics\/|(?:\.cursor|\.claude)\/rules\/|rules\/)(f2s-[a-zA-Z0-9-]+)/g;
let match;
while ((match = pattern.exec(content))) ids.add(match[1]);
return Array.from(ids).sort();
}
function hostRulePath(profile, ruleId) {
return `${profile.rules}/${ruleId}${profile.ruleExtension}`;
}
function hostSkillPath(profile, skillName) {
return `${profile.skills}/${skillName}/SKILL.md`;
}
function nativeSkillName(name, host) {
if (host !== "dsh") return name;
return String(name)
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
.toLowerCase();
}
function adaptNativeSkillNames(content, host) {
if (host !== "dsh") return String(content);
return String(content).replace(/\bf2s-[a-zA-Z0-9-]*[A-Z][a-zA-Z0-9-]*\b/g, (name) =>
nativeSkillName(name, host),
);
}
function adaptHostPaths(content, host) {
const profile = HOST_PATHS[host];
const adapted = String(content)
.replace(
/(?:(?:\.codex|\.dsh)\/topics\/|(?:\.cursor|\.claude)\/rules\/|rules\/)(f2s-[a-zA-Z0-9-]+)(?:\.(?:mdc?|\*))?/g,
(_, ruleId) => hostRulePath(profile, ruleId),
)
.replace(
/(?:(?:\.codex|\.dsh|\.cursor|\.claude)\/skills\/|skills\/)(f2s-[a-zA-Z0-9-]+)\/SKILL\.md/g,
(_, skillName) => hostSkillPath(profile, skillName),
);
if (host !== "dsh") return adapted;
// Native DSH resources are supplied by the provider, not copied into another
// client's project directory. Keep any remaining ancillary paths provider-relative.
return adaptNativeSkillNames(
adapted.replace(/\.(?:codex|cursor|claude|dsh)\//g, ""),
host,
);
}
function replaceSection(content, startHeading, endHeading, replacement) {
const start = content.indexOf(startHeading);
if (start < 0) return content;
const end = content.indexOf(endHeading, start + startHeading.length);
if (end < 0) return content;
return `${content.slice(0, start)}${replacement.trim()}\n\n${content.slice(end)}`;
}
function adaptNativeDshEntry(content, locale) {
if (locale === "en-US") {
return replaceSection(
content,
"## Knowledge Base Version Check",
"## Topic Authoring Pointer",
`## Knowledge Base Version Check
The native host calls Core \`update.check()\` on session start. The API respects the project \`updateCheck.enabled\` switch and the daily \`.Knowledge/update-check.json\` cache. A notice is displayed when the published Core is newer than the project knowledge version. This check only detects and reports updates; it does not replace the configuration-read or knowledge-routing gates.`,
);
}
return replaceSection(
content,
"## 知识库版本自检",
"## 主题创作",
`## 知识库版本自检
原生宿主在会话启动时调用 Core \`update.check()\`。该 API 服从项目的 \`updateCheck.enabled\` 开关并复用每日 \`.Knowledge/update-check.json\` 缓存;Core 发布版本高于项目知识版本时,由宿主展示升级提示。版本检查只负责检测与提醒,不替代配置前置读取和知识路由门禁。`,
);
}
function configSummary(projectConfig, locale) {
if (!projectConfig || typeof projectConfig !== "object") return "";
const known = {
subAgent: projectConfig.subAgent,
switchAgentVerification: projectConfig.switchAgentVerification,
intentRecognition: projectConfig.intentRecognition,
locale: projectConfig.locale,
changeTracking: projectConfig.changeTracking,
updateCheck: projectConfig.updateCheck,
collaboration: projectConfig.collaboration,
};
for (const key of Object.keys(known)) {
if (known[key] === undefined) delete known[key];
}
if (Object.keys(known).length === 0) return "";
const heading = locale === "en-US" ? "## Current Project Configuration" : "## 当前项目配置";
const note =
locale === "en-US"
? "This snapshot is contextual only. Read `flow2spec.config.json` again before running any `f2s-*` skill."
: "此摘要只提供当前上下文。执行任何 `f2s-*` 技能前仍须重新读取 `flow2spec.config.json`。";
return `${heading}\n\n${note}\n\n\`\`\`json\n${JSON.stringify(known, null, 2)}\n\`\`\`\n\n`;
}
function readRule(templatesRoot, locale, ruleId) {
const relativePath = path.posix.join("rules", `${ruleId}.md`);
const absolutePath = path.join(templatesDir(templatesRoot, locale), ...relativePath.split("/"));
if (!fs.existsSync(absolutePath)) return null;
return { relativePath, content: fs.readFileSync(absolutePath, "utf8") };
}
function skillCatalog(templatesRoot, options = {}) {
const { host, locale } = normalizeOptions(options);
const profile = HOST_PATHS[host];
const skillsRoot = path.join(templatesDir(templatesRoot, locale), "skills");
if (!fs.existsSync(skillsRoot)) return [];
return fs
.readdirSync(skillsRoot, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.sort((left, right) => left.name.localeCompare(right.name))
.map((entry) => {
const sourceRelativePath = path.posix.join("skills", entry.name, "SKILL.md");
const sourcePath = path.join(skillsRoot, entry.name, "SKILL.md");
if (!fs.existsSync(sourcePath)) {
throw resourceError("F2S_RESOURCE_MISSING", `skill resource missing: ${sourceRelativePath}`, {
relativePath: sourceRelativePath,
locale,
});
}
const raw = fs.readFileSync(sourcePath, "utf8");
const parsed = parseSkillDocument(raw, sourceRelativePath);
const skillName = nativeSkillName(parsed.name, host);
const resources = ruleReferenceIds(raw)
.map((ruleId) => readRule(templatesRoot, locale, ruleId))
.filter(Boolean)
.map((resource) => ({
relativePath: hostRulePath(profile, path.basename(resource.relativePath, ".md")),
content: adaptHostPaths(resource.content, host),
mediaType: "text/markdown",
}));
return {
name: skillName,
description: adaptNativeSkillNames(parsed.description, host),
content: adaptHostPaths(parsed.body, host),
relativePath: hostSkillPath(profile, skillName),
resources,
};
});
}
function unifiedEntry(templatesRoot, options = {}) {
const { host, locale, projectConfig } = normalizeOptions(options);
const entry = readRule(templatesRoot, locale, "f2s-flow2spec-unified-entry");
if (!entry) {
throw resourceError("F2S_RESOURCE_MISSING", "unified entry resource is missing", { locale });
}
let content = adaptHostPaths(entry.content, host);
if (host === "dsh") content = adaptNativeDshEntry(content, locale);
return `${configSummary(projectConfig, locale)}${content}`;
}
module.exports = {
SUPPORTED_HOSTS,
adaptHostPaths,
skillCatalog,
unifiedEntry,
};
"use strict";
const fs = require("fs");
const path = require("path");
const { execFile } = require("child_process");
const DEFAULT_PACKAGE_NAME = "@double-coding/flow2spec-core";
const KNOWLEDGE_ROOT = ".Knowledge";
const CACHE_FILENAME = "update-check.json";
function parseVersion(version) {
return String(version || "")
.replace(/^v/, "")
.split(/[.-]/)
.slice(0, 3)
.map((part) => {
const number = Number.parseInt(part, 10);
return Number.isFinite(number) ? number : 0;
});
}
function compareVersions(left, right) {
const a = parseVersion(left);
const b = parseVersion(right);
for (let index = 0; index < 3; index += 1) {
const difference = (a[index] || 0) - (b[index] || 0);
if (difference !== 0) return difference;
}
return 0;
}
function abortError() {
const error = new Error("update check aborted");
error.code = "F2S_ABORTED";
error.details = { operation: "update.check" };
return error;
}
function assertNotAborted(signal) {
if (signal?.aborted) throw abortError();
}
function readJson(filePath) {
try {
return JSON.parse(fs.readFileSync(filePath, "utf8"));
} catch (_) {
return null;
}
}
function sameLocalDay(timestamp, now) {
const checkedAt = Number(timestamp || 0);
return checkedAt > 0 && new Date(checkedAt).toDateString() === new Date(now).toDateString();
}
function projectName(cwd) {
const pkg = readJson(path.join(cwd, "package.json"));
return pkg?.name ? String(pkg.name) : path.basename(cwd);
}
function buildNotice({ cwd, locale, manifestVersion, latestVersion }) {
if (locale === "en-US") {
return `[flow2spec] The project "${projectName(cwd)}" knowledge version is v${manifestVersion}; Core v${latestVersion} is available. Run the f2s-kb-upgrade skill to align templates and routing.`;
}
return `[flow2spec] 当前项目「${projectName(cwd)}」知识版本为 v${manifestVersion},Core 最新版本为 v${latestVersion}。可执行 f2s-kb-upgrade skill 对齐模板与路由。`;
}
function queryLatestVersion(packageName, options = {}) {
const npmExecutable = process.platform === "win32" ? "npm.cmd" : "npm";
return new Promise((resolve, reject) => {
execFile(
npmExecutable,
["view", packageName, "version", "--registry=https://registry.npmjs.org"],
{
encoding: "utf8",
timeout: options.timeout || 5000,
maxBuffer: 1024 * 1024,
signal: options.signal,
windowsHide: true,
},
(error, stdout) => {
if (options.signal?.aborted || error?.name === "AbortError" || error?.code === "ABORT_ERR") {
reject(abortError());
return;
}
if (error) {
reject(error);
return;
}
resolve(String(stdout || "").trim());
},
);
});
}
function result(status, values = {}) {
return {
status,
checked: false,
fromCache: false,
packageName: values.packageName || DEFAULT_PACKAGE_NAME,
manifestVersion: values.manifestVersion || null,
latestVersion: values.latestVersion || null,
needsUpgrade: status === "upgrade-available",
notice: values.notice || "",
checkedAt: values.checkedAt || null,
reason: values.reason || null,
...values,
};
}
async function checkUpdate(cwd, config, options = {}) {
assertNotAborted(options.signal);
const packageName = options.packageName || DEFAULT_PACKAGE_NAME;
const locale = config?.locale === "en-US" ? "en-US" : "zh-CN";
if (config?.updateCheck?.enabled === false) {
return result("disabled", { packageName, reason: "config-disabled" });
}
if (!options.force && (process.env.CI || process.env.CONTINUOUS_INTEGRATION)) {
return result("skipped", { packageName, reason: "continuous-integration" });
}
const knowledgeDir = path.join(cwd, KNOWLEDGE_ROOT);
const manifestPath = path.join(knowledgeDir, "manifest-routing.json");
const cachePath = path.join(knowledgeDir, CACHE_FILENAME);
const manifestVersion = readJson(manifestPath)?.version || null;
if (!manifestVersion) {
return result("skipped", { packageName, reason: "manifest-missing" });
}
const now = Date.now();
const cache = readJson(cachePath);
const cachePackageMatches = cache?.packageName
? cache.packageName === packageName
: packageName === DEFAULT_PACKAGE_NAME;
if (!options.force && cache && cachePackageMatches && sameLocalDay(cache.checkedAt, now)) {
const latestVersion = cache.latestVersion || cache.latestNpm || null;
if (latestVersion && compareVersions(manifestVersion, latestVersion) >= 0) {
try {
fs.rmSync(cachePath, { force: true });
} catch (_) {}
return result("current", {
checked: true,
fromCache: true,
packageName,
manifestVersion,
latestVersion,
checkedAt: Number(cache.checkedAt),
});
}
const needsUpgrade =
Boolean(latestVersion) &&
(cache.needsUpgrade === true || compareVersions(manifestVersion, latestVersion) < 0);
return result(needsUpgrade ? "upgrade-available" : "current", {
checked: true,
fromCache: true,
packageName,
manifestVersion,
latestVersion,
needsUpgrade,
notice: needsUpgrade
? buildNotice({ cwd, locale, manifestVersion, latestVersion })
: "",
checkedAt: Number(cache.checkedAt),
});
}
let latestVersion;
try {
latestVersion = await queryLatestVersion(packageName, {
signal: options.signal,
timeout: options.timeout,
});
} catch (error) {
if (error?.code === "F2S_ABORTED") throw error;
return result("unavailable", {
packageName,
manifestVersion,
reason: "registry-unavailable",
});
}
assertNotAborted(options.signal);
if (!latestVersion) {
return result("unavailable", {
packageName,
manifestVersion,
reason: "empty-registry-version",
});
}
const needsUpgrade = compareVersions(manifestVersion, latestVersion) < 0;
const notice = needsUpgrade
? buildNotice({ cwd, locale, manifestVersion, latestVersion })
: "";
const checkedAt = Date.now();
try {
fs.mkdirSync(knowledgeDir, { recursive: true });
fs.writeFileSync(
cachePath,
`${JSON.stringify(
{
packageName,
latestVersion,
latestNpm: latestVersion,
manifestVersion,
needsUpgrade,
notice,
checkedAt,
},
null,
2,
)}\n`,
"utf8",
);
} catch (_) {}
return result(needsUpgrade ? "upgrade-available" : "current", {
checked: true,
packageName,
manifestVersion,
latestVersion,
needsUpgrade,
notice,
checkedAt,
});
}
module.exports = {
DEFAULT_PACKAGE_NAME,
compareVersions,
checkUpdate,
};
+5
-2
{
"schema": "flow2spec.capabilities.v1",
"protocolVersion": 1,
"protocolVersion": 2,
"package": "@double-coding/flow2spec-core",

@@ -25,4 +25,7 @@ "capabilities": [

{ "id": "resources.rules", "api": "resources.listRules", "since": "3.3.0" },
{ "id": "resources.read", "api": "resources.read", "since": "3.3.0" }
{ "id": "resources.read", "api": "resources.read", "since": "3.3.0" },
{ "id": "resources.skill-catalog", "api": "resources.skillCatalog", "since": "3.4.0" },
{ "id": "resources.unified-entry", "api": "resources.unifiedEntry", "since": "3.4.0" },
{ "id": "update.check", "api": "update.check", "since": "3.4.0" }
]
}

@@ -16,2 +16,4 @@ "use strict";

const routing = require("./lib/routing");
const hostResources = require("./lib/resources");
const updateCheck = require("./lib/updateCheck");
const capabilities = require("./capabilities.json");

@@ -78,2 +80,26 @@

function toFlow2SpecError(error) {
if (error instanceof Flow2SpecError) return error;
if (error && typeof error.code === "string" && error.code.startsWith("F2S_")) {
return new Flow2SpecError(error.code, error.message || String(error), error.details || {});
}
return error;
}
function callCore(operation) {
try {
return operation();
} catch (error) {
throw toFlow2SpecError(error);
}
}
async function callCoreAsync(operation) {
try {
return await operation();
} catch (error) {
throw toFlow2SpecError(error);
}
}
function createFlow2Spec(options = {}) {

@@ -117,4 +143,4 @@ const cwd = assertCwd(options.cwd || process.cwd());

},
apply: ({ deltaFile, ...applyOptions } = {}) =>
knowledgeEngine.applyKnowledgeDelta(cwd, deltaFile, applyOptions),
apply: ({ delta, deltaFile, ...applyOptions } = {}) =>
knowledgeEngine.applyKnowledgeDelta(cwd, delta || deltaFile, applyOptions),
build: (buildOptions = {}) => knowledgeEngine.buildKnowledgeGraph(cwd, buildOptions),

@@ -139,3 +165,13 @@ },

read: (relativePath, locale = "zh-CN") => readResource(relativePath, locale),
skillCatalog: (resourceOptions = {}) =>
callCore(() => hostResources.skillCatalog(path.join(__dirname, "templates"), resourceOptions)),
unifiedEntry: (resourceOptions = {}) =>
callCore(() => hostResources.unifiedEntry(path.join(__dirname, "templates"), resourceOptions)),
},
update: {
check: (checkOptions = {}) =>
callCoreAsync(() =>
updateCheck.checkUpdate(cwd, config.loadFlow2specConfig(cwd), checkOptions),
),
},
};

@@ -142,0 +178,0 @@ }

{
"name": "@double-coding/flow2spec-core",
"version": "3.3.1",
"version": "3.4.0",
"description": "Flow2Spec Core APIs, knowledge engine, project initialization and shared resources",

@@ -12,4 +12,6 @@ "homepage": "https://github.com/double-coding-lab/Flow2Spec#readme",

"main": "./index.js",
"types": "./index.d.ts",
"files": [
"index.js",
"index.d.ts",
"lib",

@@ -16,0 +18,0 @@ "templates",

@@ -6,1 +6,23 @@ # @double-coding/flow2spec-core

普通用户通常不需要单独安装此包;安装 `@double-coding/flow2spec` 或 Flow2Spec 原生插件时会自动带上对应版本的 Core。
## 原生插件 API
```js
const { createFlow2Spec } = require("@double-coding/flow2spec-core");
const flow2spec = createFlow2Spec({ cwd: process.cwd() });
const skills = flow2spec.resources.skillCatalog({ host: "dsh", locale: "zh-CN" });
const entry = flow2spec.resources.unifiedEntry({
host: "dsh",
locale: "zh-CN",
projectConfig: flow2spec.config.load(),
});
const update = await flow2spec.update.check();
```
- `resources.skillCatalog()` 返回带正文和关联规则资源的结构化 Skill 清单。Core 根据宿主适配规则与 Skill 路径,插件不需要重写其他客户端目录。
- `resources.unifiedEntry()` 返回宿主适配后的统一入口,可附带当前项目配置摘要。
- `update.check()` 复用 `.Knowledge/update-check.json` 的每日缓存与版本比较语义;网络不可用时返回 `unavailable`,不会阻断宿主。
- `capabilities.json` 的 `protocolVersion` 用于插件启动时执行能力兼容校验。
包通过 `index.d.ts` 导出完整公共契约类型。普通 CLI 用户继续使用 `npx @double-coding/flow2spec init`,无需直接调用这些 API。