@double-coding/flow2spec-core
Advanced tools
@@ -8,3 +8,5 @@ { | ||
| { "id": "project.inspect", "api": "project.inspect", "since": "3.3.0" }, | ||
| { "id": "project.agents", "api": "project.agents", "since": "3.5.0" }, | ||
| { "id": "config.load", "api": "config.load", "since": "3.3.0" }, | ||
| { "id": "config.supported-locales", "api": "config.supportedLocales", "since": "3.5.0" }, | ||
| { "id": "routing.graph", "api": "routing.graph", "since": "3.3.0" }, | ||
@@ -11,0 +13,0 @@ { "id": "routing.state", "api": "routing.state", "since": "3.3.0" }, |
+20
-0
@@ -58,2 +58,7 @@ export type Flow2SpecLocale = "zh-CN" | "en-US"; | ||
| export interface Flow2SpecAgent { | ||
| root: string; | ||
| label: string; | ||
| } | ||
| export interface RoutingRule { | ||
@@ -204,2 +209,8 @@ task?: string; | ||
| export interface Flow2SpecVersions { | ||
| coreVersion: string; | ||
| templateVersion: string; | ||
| protocolVersion: number; | ||
| } | ||
| export interface HostResourceOptions { | ||
@@ -244,4 +255,10 @@ host: Flow2SpecHost; | ||
| packageName: string; | ||
| currentCoreVersion: string; | ||
| currentTemplateVersion: string; | ||
| manifestVersion: string | null; | ||
| latestCoreVersion: string | null; | ||
| latestTemplateVersion: string | null; | ||
| latestVersion: string | null; | ||
| coreUpdateAvailable: boolean; | ||
| templateUpdateAvailable: boolean; | ||
| needsUpgrade: boolean; | ||
@@ -262,2 +279,3 @@ notice: string; | ||
| inspect(): ProjectInspection; | ||
| agents(): Record<string, Flow2SpecAgent>; | ||
| }; | ||
@@ -267,2 +285,3 @@ config: { | ||
| missingFields(): unknown[]; | ||
| supportedLocales(): Flow2SpecLocale[]; | ||
| }; | ||
@@ -331,3 +350,4 @@ routing: { | ||
| export function getCapabilities(): CapabilityManifest; | ||
| export function getVersions(): Flow2SpecVersions; | ||
| export const resourcesRoot: string; | ||
| export const legacy: Record<string, unknown>; |
+10
-0
@@ -19,2 +19,3 @@ "use strict"; | ||
| const capabilities = require("./capabilities.json"); | ||
| const packageMetadata = require("./package.json"); | ||
@@ -117,2 +118,5 @@ class Flow2SpecError extends Error { | ||
| inspect: () => ({ cwd, config: config.loadFlow2specConfig(cwd) }), | ||
| agents: () => Object.fromEntries( | ||
| Object.entries(agents.AGENTS).map(([id, metadata]) => [id, { ...metadata }]), | ||
| ), | ||
| }, | ||
@@ -122,2 +126,3 @@ config: { | ||
| missingFields: () => config.getMissingConfigFields(cwd), | ||
| supportedLocales: () => [...config.SUPPORTED_LOCALES], | ||
| }, | ||
@@ -183,2 +188,7 @@ routing: { | ||
| getCapabilities: () => capabilities, | ||
| getVersions: () => ({ | ||
| coreVersion: packageMetadata.version, | ||
| templateVersion: packageMetadata.templateVersion, | ||
| protocolVersion: capabilities.protocolVersion, | ||
| }), | ||
| resourcesRoot: __dirname, | ||
@@ -185,0 +195,0 @@ legacy: { |
@@ -172,12 +172,13 @@ 'use strict'; | ||
| let body = fs.readFileSync(src, 'utf8'); | ||
| if (body.includes('__FLOW2SPEC_PACKAGE_NAME__')) { | ||
| let packageName = '@double-coding/flow2spec'; | ||
| try { | ||
| const packageDir = findPackageJsonDir(templatesDir); | ||
| packageName = JSON.parse( | ||
| fs.readFileSync(path.join(packageDir || path.join(templatesDir, '..'), 'package.json'), 'utf8'), | ||
| ).name || packageName; | ||
| } catch (_) {} | ||
| body = body.replace(/__FLOW2SPEC_PACKAGE_NAME__/g, packageName); | ||
| } | ||
| let packageMetadata = {}; | ||
| try { | ||
| const packageDir = findPackageJsonDir(templatesDir); | ||
| packageMetadata = JSON.parse( | ||
| fs.readFileSync(path.join(packageDir || path.join(templatesDir, '..'), 'package.json'), 'utf8'), | ||
| ); | ||
| } catch (_) {} | ||
| body = body | ||
| .replace(/__FLOW2SPEC_PACKAGE_NAME__/g, packageMetadata.name || '@double-coding/flow2spec-core') | ||
| .replace(/__FLOW2SPEC_CORE_VERSION__/g, packageMetadata.version || '0.0.0') | ||
| .replace(/__FLOW2SPEC_TEMPLATE_VERSION__/g, packageMetadata.templateVersion || '0.0.0'); | ||
| fs.writeFileSync(path.join(hooksDir, scriptName), body, 'utf8'); | ||
@@ -184,0 +185,0 @@ return { written: true }; |
@@ -29,3 +29,3 @@ const fs = require("fs"); | ||
| DeepSeek Harness loads the repository-root \`AGENTS.md\` and discovers project skills from \`./.dsh/skills/\`. Flow2Spec mirrors its long-form rules to \`./.dsh/topics/\` for on-demand reading. Native Cordis plugin integration is outside this initialization adapter.`, | ||
| DeepSeek Harness loads the repository-root \`AGENTS.md\` and discovers project skills from \`./.dsh/skills/\`. Flow2Spec mirrors its long-form rules to \`./.dsh/topics/\` for on-demand reading. Prefer the native Cordis plugin \`@double-coding/flow2spec-deepseek-harness\` when installed; \`flow2spec init dsh\` remains the project-level fallback.`, | ||
| ); | ||
@@ -44,3 +44,3 @@ return body; | ||
| DeepSeek Harness 会加载仓库根 \`AGENTS.md\`,并从 \`./.dsh/skills/\` 发现项目技能。Flow2Spec 将规则长文镜像到 \`./.dsh/topics/\` 供按需读取。原生 Cordis 插件集成不属于本初始化适配范围。`, | ||
| DeepSeek Harness 会加载仓库根 \`AGENTS.md\`,并从 \`./.dsh/skills/\` 发现项目技能。Flow2Spec 将规则长文镜像到 \`./.dsh/topics/\` 供按需读取。已安装时优先使用原生 Cordis 插件 \`@double-coding/flow2spec-deepseek-harness\`;\`flow2spec init dsh\` 仍作为项目级兼容入口。`, | ||
| ); | ||
@@ -47,0 +47,0 @@ } |
+18
-14
@@ -175,8 +175,8 @@ const path = require("path"); | ||
| function readPackageName(templatesDir) { | ||
| function readPackageMetadata(templatesDir) { | ||
| try { | ||
| const packageDir = findPackageJsonDir(templatesDir); | ||
| return readJson(path.join(packageDir || path.join(templatesDir, ".."), "package.json")).name; | ||
| return readJson(path.join(packageDir || path.join(templatesDir, ".."), "package.json")); | ||
| } catch (_) { | ||
| return "@double-coding/flow2spec"; | ||
| return {}; | ||
| } | ||
@@ -203,3 +203,7 @@ } | ||
| let body = fs.readFileSync(src, "utf8"); | ||
| body = body.replace(/__FLOW2SPEC_PACKAGE_NAME__/g, readPackageName(templatesDir)); | ||
| const packageMetadata = readPackageMetadata(templatesDir); | ||
| body = body | ||
| .replace(/__FLOW2SPEC_PACKAGE_NAME__/g, packageMetadata.name || "@double-coding/flow2spec-core") | ||
| .replace(/__FLOW2SPEC_CORE_VERSION__/g, packageMetadata.version || "0.0.0") | ||
| .replace(/__FLOW2SPEC_TEMPLATE_VERSION__/g, packageMetadata.templateVersion || "0.0.0"); | ||
| fs.writeFileSync(path.join(destDir, scriptName), body, "utf8"); | ||
@@ -467,3 +471,3 @@ return { written: true }; | ||
| function buildMergedRouting(templateRouting, existingRouting, pkgVersion, isFirstInit = false) { | ||
| function buildMergedRouting(templateRouting, existingRouting, templateVersion, isFirstInit = false) { | ||
| const mergedTaskRules = unionByKey( | ||
@@ -493,3 +497,3 @@ templateRouting.taskToTopicRules, | ||
| const knownMerged = { | ||
| version: pkgVersion || templateRouting.version || existingRouting.version, | ||
| version: templateVersion || templateRouting.version || existingRouting.version, | ||
| knowledgeRoot: | ||
@@ -738,3 +742,3 @@ existingRouting.knowledgeRoot || templateRouting.knowledgeRoot, | ||
| } | ||
| // 顺便用本包版本号覆盖 manifest.version——reset 路径直接 cp 模板会留下模板里的占位版本号 | ||
| // reset 路径直接复制模板时,用 Core 包内独立模板版本覆盖 manifest.version。 | ||
| let changed = false; | ||
@@ -744,5 +748,5 @@ try { | ||
| if (fs.existsSync(pkgJsonPath)) { | ||
| const pkgVersion = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8")).version; | ||
| if (typeof pkgVersion === "string" && routing.version !== pkgVersion) { | ||
| routing.version = pkgVersion; | ||
| const templateVersion = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8")).templateVersion; | ||
| if (typeof templateVersion === "string" && routing.version !== templateVersion) { | ||
| routing.version = templateVersion; | ||
| changed = true; | ||
@@ -812,10 +816,10 @@ } | ||
| // 读包版本号,用于写入 manifest-routing.json 的 version 字段 | ||
| let pkgVersion; | ||
| // 读取 Core 包内独立模板版本,用于写入 manifest-routing.json.version。 | ||
| let templateVersion; | ||
| try { | ||
| const packageDir = findPackageJsonDir(templatesDir); | ||
| pkgVersion = readJson(path.join(packageDir || path.join(templatesDir, ".."), "package.json")).version; | ||
| templateVersion = readJson(path.join(packageDir || path.join(templatesDir, ".."), "package.json")).templateVersion; | ||
| } catch (_) {} | ||
| const mergedRouting = buildMergedRouting(templateRouting, existingRouting, pkgVersion, !hadRouting); | ||
| const mergedRouting = buildMergedRouting(templateRouting, existingRouting, templateVersion, !hadRouting); | ||
| const mergedMatchers = buildMergedMatchers( | ||
@@ -822,0 +826,0 @@ templateMatchers, |
+2
-2
@@ -144,3 +144,3 @@ "use strict"; | ||
| 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.`, | ||
| 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. It reports Core Version and Template Version independently: Core-only updates refresh the runtime and Hook without entering knowledge upgrade, while Template updates continue through init and the projectRev/pkgRev gate. This check does not replace configuration-read or knowledge-routing gates.`, | ||
| ); | ||
@@ -154,3 +154,3 @@ } | ||
| 原生宿主在会话启动时调用 Core \`update.check()\`。该 API 服从项目的 \`updateCheck.enabled\` 开关并复用每日 \`.Knowledge/update-check.json\` 缓存;Core 发布版本高于项目知识版本时,由宿主展示升级提示。版本检查只负责检测与提醒,不替代配置前置读取和知识路由门禁。`, | ||
| 原生宿主在会话启动时调用 Core \`update.check()\`。该 API 服从项目的 \`updateCheck.enabled\` 开关并复用每日 \`.Knowledge/update-check.json\` 缓存,分别报告 Core Version 与 Template Version:Core-only 更新只刷新运行时与 Hook,Template 更新再进入 init 和 projectRev/pkgRev 门禁。版本检查不替代配置前置读取和知识路由门禁。`, | ||
| ); | ||
@@ -157,0 +157,0 @@ } |
+94
-113
@@ -6,16 +6,12 @@ "use strict"; | ||
| const { execFile } = require("child_process"); | ||
| const packageMetadata = require("../package.json"); | ||
| 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; | ||
| }); | ||
| return String(version || "").replace(/^v/, "").split(/[.-]/).slice(0, 3).map((part) => { | ||
| const number = Number.parseInt(part, 10); | ||
| return Number.isFinite(number) ? number : 0; | ||
| }); | ||
| } | ||
@@ -45,7 +41,3 @@ | ||
| function readJson(filePath) { | ||
| try { | ||
| return JSON.parse(fs.readFileSync(filePath, "utf8")); | ||
| } catch (_) { | ||
| return null; | ||
| } | ||
| try { return JSON.parse(fs.readFileSync(filePath, "utf8")); } catch (_) { return null; } | ||
| } | ||
@@ -59,14 +51,18 @@ | ||
| function projectName(cwd) { | ||
| const pkg = readJson(path.join(cwd, "package.json")); | ||
| return pkg?.name ? String(pkg.name) : path.basename(cwd); | ||
| return readJson(path.join(cwd, "package.json"))?.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.`; | ||
| function buildNotice({ cwd, locale, state }) { | ||
| const summary = `Core v${state.currentCoreVersion} -> v${state.latestCoreVersion}, Template v${state.manifestVersion} -> v${state.latestTemplateVersion}`; | ||
| if (state.templateUpdateAvailable) { | ||
| return locale === "en-US" | ||
| ? `[flow2spec] Project "${projectName(cwd)}" has a template update (${summary}). Update Core, run flow2spec init, then use f2s-kb-upgrade only if projectRev differs from pkgRev.` | ||
| : `[flow2spec] 当前项目「${projectName(cwd)}」有模板更新(${summary})。请更新 Core 后执行 flow2spec init;仅当 projectRev 与 pkgRev 不等时再执行 f2s-kb-upgrade。`; | ||
| } | ||
| return `[flow2spec] 当前项目「${projectName(cwd)}」知识版本为 v${manifestVersion},Core 最新版本为 v${latestVersion}。可执行 f2s-kb-upgrade skill 对齐模板与路由。`; | ||
| return locale === "en-US" | ||
| ? `[flow2spec] Project "${projectName(cwd)}" has a Core-only update (${summary}). Update Core and run one idempotent flow2spec init; do not run f2s-kb-upgrade.` | ||
| : `[flow2spec] 当前项目「${projectName(cwd)}」仅有 Core 程序更新(${summary})。请更新 Core 并执行一次幂等 flow2spec init;无需执行 f2s-kb-upgrade。`; | ||
| } | ||
| function queryLatestVersion(packageName, options = {}) { | ||
| function queryLatestMetadata(packageName, options = {}) { | ||
| const npmExecutable = process.platform === "win32" ? "npm.cmd" : "npm"; | ||
@@ -76,3 +72,3 @@ return new Promise((resolve, reject) => { | ||
| npmExecutable, | ||
| ["view", packageName, "version", "--registry=https://registry.npmjs.org"], | ||
| ["view", packageName, "version", "templateVersion", "--json", "--registry=https://registry.npmjs.org"], | ||
| { | ||
@@ -94,3 +90,12 @@ encoding: "utf8", | ||
| } | ||
| resolve(String(stdout || "").trim()); | ||
| try { | ||
| const metadata = JSON.parse(String(stdout || "").trim()); | ||
| const latestCoreVersion = typeof metadata === "string" ? metadata : metadata.version; | ||
| const latestTemplateVersion = typeof metadata === "string" | ||
| ? metadata | ||
| : metadata.templateVersion || metadata.version; | ||
| resolve({ latestCoreVersion, latestTemplateVersion }); | ||
| } catch (parseError) { | ||
| reject(parseError); | ||
| } | ||
| }, | ||
@@ -101,2 +106,19 @@ ); | ||
| function buildState(manifestVersion, metadata) { | ||
| const currentCoreVersion = packageMetadata.version; | ||
| const currentTemplateVersion = packageMetadata.templateVersion; | ||
| const coreUpdateAvailable = compareVersions(currentCoreVersion, metadata.latestCoreVersion) < 0; | ||
| const templateUpdateAvailable = compareVersions(manifestVersion, metadata.latestTemplateVersion) < 0; | ||
| return { | ||
| currentCoreVersion, | ||
| currentTemplateVersion, | ||
| manifestVersion, | ||
| latestCoreVersion: metadata.latestCoreVersion, | ||
| latestTemplateVersion: metadata.latestTemplateVersion, | ||
| coreUpdateAvailable, | ||
| templateUpdateAvailable, | ||
| needsUpgrade: coreUpdateAvailable || templateUpdateAvailable, | ||
| }; | ||
| } | ||
| function result(status, values = {}) { | ||
@@ -108,4 +130,10 @@ return { | ||
| packageName: values.packageName || DEFAULT_PACKAGE_NAME, | ||
| currentCoreVersion: values.currentCoreVersion || packageMetadata.version, | ||
| currentTemplateVersion: values.currentTemplateVersion || packageMetadata.templateVersion, | ||
| manifestVersion: values.manifestVersion || null, | ||
| latestVersion: values.latestVersion || null, | ||
| latestCoreVersion: values.latestCoreVersion || null, | ||
| latestTemplateVersion: values.latestTemplateVersion || null, | ||
| latestVersion: values.latestCoreVersion || values.latestVersion || null, | ||
| coreUpdateAvailable: false, | ||
| templateUpdateAvailable: false, | ||
| needsUpgrade: status === "upgrade-available", | ||
@@ -130,101 +158,53 @@ notice: values.notice || "", | ||
| const knowledgeDir = path.join(cwd, KNOWLEDGE_ROOT); | ||
| const manifestPath = path.join(knowledgeDir, "manifest-routing.json"); | ||
| const knowledgeDir = path.join(cwd, ".Knowledge"); | ||
| const cachePath = path.join(knowledgeDir, CACHE_FILENAME); | ||
| const manifestVersion = readJson(manifestPath)?.version || null; | ||
| if (!manifestVersion) { | ||
| return result("skipped", { packageName, reason: "manifest-missing" }); | ||
| } | ||
| const manifestVersion = readJson(path.join(knowledgeDir, "manifest-routing.json"))?.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), | ||
| }); | ||
| let metadata; | ||
| let fromCache = false; | ||
| if (!options.force && cache && sameLocalDay(cache.checkedAt, Date.now())) { | ||
| metadata = { | ||
| latestCoreVersion: cache.latestCoreVersion || cache.latestVersion || cache.latestNpm, | ||
| latestTemplateVersion: cache.latestTemplateVersion || cache.latestNpm || cache.latestVersion, | ||
| }; | ||
| fromCache = true; | ||
| } else { | ||
| try { | ||
| metadata = await queryLatestMetadata(packageName, options); | ||
| } catch (error) { | ||
| if (error?.code === "F2S_ABORTED") throw error; | ||
| return result("unavailable", { packageName, manifestVersion, reason: "registry-unavailable" }); | ||
| } | ||
| 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), | ||
| }); | ||
| } | ||
| assertNotAborted(options.signal); | ||
| if (!metadata.latestCoreVersion || !metadata.latestTemplateVersion) { | ||
| return result("unavailable", { packageName, manifestVersion, reason: "empty-registry-version" }); | ||
| } | ||
| 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", | ||
| }); | ||
| const state = buildState(manifestVersion, metadata); | ||
| const notice = state.needsUpgrade ? buildNotice({ cwd, locale, state }) : ""; | ||
| const checkedAt = fromCache ? Number(cache.checkedAt) : Date.now(); | ||
| if (!fromCache) { | ||
| try { | ||
| fs.mkdirSync(knowledgeDir, { recursive: true }); | ||
| fs.writeFileSync(cachePath, `${JSON.stringify({ | ||
| packageName, | ||
| ...state, | ||
| latestNpm: state.latestTemplateVersion, | ||
| notice, | ||
| checkedAt, | ||
| }, null, 2)}\n`, "utf8"); | ||
| } catch (_) {} | ||
| } | ||
| assertNotAborted(options.signal); | ||
| if (!latestVersion) { | ||
| return result("unavailable", { | ||
| packageName, | ||
| manifestVersion, | ||
| reason: "empty-registry-version", | ||
| }); | ||
| if (!state.needsUpgrade) { | ||
| try { fs.rmSync(cachePath, { force: true }); } catch (_) {} | ||
| } | ||
| 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", { | ||
| return result(state.needsUpgrade ? "upgrade-available" : "current", { | ||
| checked: true, | ||
| fromCache, | ||
| packageName, | ||
| manifestVersion, | ||
| latestVersion, | ||
| needsUpgrade, | ||
| ...state, | ||
| latestVersion: state.latestCoreVersion, | ||
| notice, | ||
@@ -239,2 +219,3 @@ checkedAt, | ||
| checkUpdate, | ||
| queryLatestMetadata, | ||
| }; |
+2
-1
| { | ||
| "name": "@double-coding/flow2spec-core", | ||
| "version": "3.4.1", | ||
| "version": "3.5.0", | ||
| "templateVersion": "3.5.0", | ||
| "description": "Flow2Spec Core APIs, knowledge engine, project initialization and shared resources", | ||
@@ -5,0 +6,0 @@ "homepage": "https://github.com/double-coding-lab/Flow2Spec#readme", |
+5
-2
@@ -10,3 +10,3 @@ # @double-coding/flow2spec-core | ||
| ```js | ||
| const { createFlow2Spec } = require("@double-coding/flow2spec-core"); | ||
| const { createFlow2Spec, getVersions } = require("@double-coding/flow2spec-core"); | ||
@@ -21,2 +21,3 @@ const flow2spec = createFlow2Spec({ cwd: process.cwd() }); | ||
| const update = await flow2spec.update.check(); | ||
| const versions = getVersions(); | ||
| ``` | ||
@@ -26,5 +27,7 @@ | ||
| - `resources.unifiedEntry()` 返回宿主适配后的统一入口,可附带当前项目配置摘要。 | ||
| - `update.check()` 复用 `.Knowledge/update-check.json` 的每日缓存与版本比较语义;网络不可用时返回 `unavailable`,不会阻断宿主。 | ||
| - `project.agents()` 返回可用于 `project.init()` 的客户端集成元数据;`config.supportedLocales()` 返回包内模板语言列表。 | ||
| - `getVersions()` 返回独立的 Core、Template 与 Protocol Version。 | ||
| - `update.check()` 复用 `.Knowledge/update-check.json` 的每日缓存,分别返回 Core 与 Template 更新状态;网络不可用时返回 `unavailable`,不会阻断宿主。 | ||
| - `capabilities.json` 的 `protocolVersion` 用于插件启动时执行能力兼容校验。 | ||
| 包通过 `index.d.ts` 导出完整公共契约类型。普通 CLI 用户继续使用 `npx @double-coding/flow2spec init`,无需直接调用这些 API。 |
| #!/usr/bin/env node | ||
| 'use strict'; | ||
| /** | ||
| * flow2spec SessionStart hook — checks for version updates on the first conversation each day. | ||
| * Compares the local knowledge-base manifest-routing.json version with the latest npm version: | ||
| * - Same or local is newer -> exit silently | ||
| * - Behind -> inject one notice into Agent context (suggest running f2s-kb-upgrade) | ||
| * If already checked and no upgrade is needed, stay silent; if already checked and an upgrade is still needed, keep injecting the reminder in each new session. | ||
| * Written by flow2spec init to the corresponding agent's hooks/f2s-update-check.js. | ||
| */ | ||
| const fs = require('fs'); | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
@@ -16,173 +9,149 @@ const { execFileSync } = require('child_process'); | ||
| const MANIFEST_PATH = path.join(process.cwd(), '.Knowledge', 'manifest-routing.json'); | ||
| const CACHE_DIR = path.join(process.cwd(), '.Knowledge'); | ||
| const CACHE_FILE = path.join(CACHE_DIR, 'update-check.json'); | ||
| const CACHE_FILE = path.join(process.cwd(), '.Knowledge', 'update-check.json'); | ||
| const PACKAGE_NAME_PLACEHOLDER = '__FLOW2SPEC_' + 'PACKAGE_NAME__'; | ||
| const PACKAGE_NAME = '__FLOW2SPEC_PACKAGE_NAME__'; | ||
| const GENERATED_CORE_VERSION = '__FLOW2SPEC_CORE_VERSION__'; | ||
| const GENERATED_TEMPLATE_VERSION = '__FLOW2SPEC_TEMPLATE_VERSION__'; | ||
| // ── Cache ─────────────────────────────────────────────────────────────────── | ||
| function readJson(file) { | ||
| try { return JSON.parse(fs.readFileSync(file, 'utf8')); } catch (_) { return null; } | ||
| } | ||
| function readCache() { | ||
| if (!fs.existsSync(CACHE_FILE)) return null; | ||
| try { | ||
| const d = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')); | ||
| if (!d || typeof d !== 'object') return null; | ||
| const checkedAt = Number(d.checkedAt || 0); | ||
| if (!checkedAt) return null; | ||
| if (new Date(checkedAt).toDateString() !== new Date().toDateString()) return null; | ||
| return d; | ||
| } catch (_) { return null; } | ||
| function parseVer(value) { | ||
| return String(value || '').replace(/^v/, '').split(/[.-]/).slice(0, 3).map((part) => { | ||
| const number = Number.parseInt(part, 10); | ||
| return Number.isFinite(number) ? number : 0; | ||
| }); | ||
| } | ||
| function getProjectName() { | ||
| try { | ||
| const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')); | ||
| if (pkg && pkg.name) return String(pkg.name); | ||
| } catch (_) {} | ||
| return path.basename(process.cwd()); | ||
| function cmpVer(left, right) { | ||
| const a = parseVer(left), b = parseVer(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 buildNotice(latestNpm, manifestVersion) { | ||
| const userMsg = `📦 [flow2spec] The current project "${getProjectName()}" knowledge-base version is v${manifestVersion}, which is lower than the latest package version v${latestNpm}. You can run the f2s-kb-upgrade skill to align templates and routing.`; | ||
| return [ | ||
| `[flow2spec/agent-instruction] At the start of this response, you must relay the following upgrade notice to the user verbatim. Do not omit it or skip it after only internal judgment:`, | ||
| ``, | ||
| userMsg, | ||
| ].join('\n'); | ||
| function getProjectName() { | ||
| return readJson(path.join(process.cwd(), 'package.json'))?.name || path.basename(process.cwd()); | ||
| } | ||
| function emitNotice(notice) { | ||
| process.stdout.write( | ||
| JSON.stringify({ | ||
| additional_context: notice, | ||
| hookSpecificOutput: { | ||
| hookEventName: 'SessionStart', | ||
| additionalContext: notice, | ||
| }, | ||
| }) + '\n' | ||
| ); | ||
| function getManifestVersion() { | ||
| return readJson(MANIFEST_PATH)?.version || null; | ||
| } | ||
| function writeCache(latestNpm, manifestVersion) { | ||
| try { | ||
| const needsUpgrade = cmpVer(manifestVersion, latestNpm) < 0; | ||
| fs.writeFileSync( | ||
| CACHE_FILE, | ||
| `${JSON.stringify({ | ||
| latestNpm, | ||
| manifestVersion, | ||
| needsUpgrade, | ||
| notice: needsUpgrade ? buildNotice(latestNpm, manifestVersion) : '', | ||
| checkedAt: Date.now(), | ||
| }, null, 2)}\n`, | ||
| 'utf8' | ||
| ); | ||
| } catch (_) {} | ||
| function getPackageName() { | ||
| return PACKAGE_NAME && PACKAGE_NAME !== PACKAGE_NAME_PLACEHOLDER | ||
| ? PACKAGE_NAME | ||
| : '@double-coding/flow2spec-core'; | ||
| } | ||
| function deleteCache() { | ||
| try { | ||
| if (fs.existsSync(CACHE_FILE)) fs.unlinkSync(CACHE_FILE); | ||
| } catch (_) {} | ||
| function isEnabled() { | ||
| const config = readJson(path.join(process.cwd(), 'flow2spec.config.json')); | ||
| return config?.updateCheck?.enabled !== false; | ||
| } | ||
| // ── Version comparison ─────────────────────────────────────────────────────── | ||
| function parseVer(v) { | ||
| return String(v || '').replace(/^v/, '').split(/[.-]/).slice(0, 3).map((p) => { | ||
| const n = Number.parseInt(p, 10); | ||
| return Number.isFinite(n) ? n : 0; | ||
| }); | ||
| function readCache() { | ||
| const cache = readJson(CACHE_FILE); | ||
| if (!cache?.checkedAt) return null; | ||
| return new Date(cache.checkedAt).toDateString() === new Date().toDateString() ? cache : null; | ||
| } | ||
| /** a < b -> negative; a === b -> 0; a > b -> positive */ | ||
| function cmpVer(a, b) { | ||
| const av = parseVer(a), bv = parseVer(b); | ||
| for (let i = 0; i < 3; i++) { | ||
| const d = (av[i] || 0) - (bv[i] || 0); | ||
| if (d !== 0) return d; | ||
| } | ||
| return 0; | ||
| function queryLatestMetadata(packageName) { | ||
| const output = execFileSync( | ||
| 'npm', | ||
| ['view', packageName, 'version', 'templateVersion', '--json', '--registry=https://registry.npmjs.org'], | ||
| { encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'] }, | ||
| ); | ||
| const metadata = JSON.parse(output); | ||
| const latestCoreVersion = typeof metadata === 'string' ? metadata : metadata.version; | ||
| const latestTemplateVersion = typeof metadata === 'string' | ||
| ? metadata | ||
| : metadata.templateVersion || metadata.version; | ||
| return { latestCoreVersion, latestTemplateVersion }; | ||
| } | ||
| // ── Reads ──────────────────────────────────────────────────────────────────── | ||
| function getManifestVersion() { | ||
| if (!fs.existsSync(MANIFEST_PATH)) return null; | ||
| try { | ||
| return JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8')).version || null; | ||
| } catch (_) { return null; } | ||
| function updateState(metadata, manifestVersion) { | ||
| const coreUpdateAvailable = cmpVer(GENERATED_CORE_VERSION, metadata.latestCoreVersion) < 0; | ||
| const templateUpdateAvailable = cmpVer(manifestVersion, metadata.latestTemplateVersion) < 0; | ||
| return { | ||
| packageName: getPackageName(), | ||
| generatedByCoreVersion: GENERATED_CORE_VERSION, | ||
| generatedTemplateVersion: GENERATED_TEMPLATE_VERSION, | ||
| manifestVersion, | ||
| ...metadata, | ||
| coreUpdateAvailable, | ||
| templateUpdateAvailable, | ||
| needsUpgrade: coreUpdateAvailable || templateUpdateAvailable, | ||
| }; | ||
| } | ||
| function getPackageName() { | ||
| if (PACKAGE_NAME && PACKAGE_NAME !== PACKAGE_NAME_PLACEHOLDER) { | ||
| return PACKAGE_NAME; | ||
| function buildNotice(state) { | ||
| const project = getProjectName(); | ||
| const versionSummary = `Core v${state.generatedByCoreVersion} -> v${state.latestCoreVersion}, Template v${state.manifestVersion} -> v${state.latestTemplateVersion}`; | ||
| if (state.templateUpdateAvailable) { | ||
| return [ | ||
| '[flow2spec/agent-instruction] At the start of this response, relay this update notice to the user:', | ||
| '', | ||
| `📦 [flow2spec] Project "${project}" has a template update (${versionSummary}).`, | ||
| '', | ||
| '[flow2spec/agent-instruction] You may run `flow2spec update --core`, then `flow2spec init <initialized agents>`. After init, read `.Knowledge/manifest-routing.json`: if projectRev equals pkgRev, delete `.Knowledge/update-check.json`; otherwise continue with f2s-kb-upgrade from step 2c.', | ||
| ].join('\n'); | ||
| } | ||
| return '@double-coding/flow2spec'; | ||
| return [ | ||
| '[flow2spec/agent-instruction] At the start of this response, relay this update notice to the user:', | ||
| '', | ||
| `📦 [flow2spec] Project "${project}" has a Core-only update (${versionSummary}); the template version is unchanged.`, | ||
| '', | ||
| '[flow2spec/agent-instruction] You may run `flow2spec update --core`, then one idempotent `flow2spec init <initialized agents>` to refresh the Hook. Do not enter f2s-kb-upgrade when Template Version is unchanged; delete `.Knowledge/update-check.json` afterwards.', | ||
| ].join('\n'); | ||
| } | ||
| function queryNpmLatest(pkgName) { | ||
| return execFileSync('npm', ['view', pkgName, 'version'], { | ||
| encoding: 'utf8', | ||
| timeout: 5000, | ||
| stdio: ['ignore', 'pipe', 'ignore'], | ||
| }).trim(); | ||
| function emitNotice(notice) { | ||
| process.stdout.write(JSON.stringify({ | ||
| additional_context: notice, | ||
| hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: notice }, | ||
| }) + '\n'); | ||
| } | ||
| // ── Configuration switch ───────────────────────────────────────────────────── | ||
| function isEnabled() { | ||
| function writeCache(state) { | ||
| try { | ||
| const cfg = JSON.parse(fs.readFileSync( | ||
| path.join(process.cwd(), 'flow2spec.config.json'), 'utf8' | ||
| )); | ||
| const uc = cfg && cfg.updateCheck; | ||
| if (uc && typeof uc.enabled === 'boolean') return uc.enabled; | ||
| return true; | ||
| } catch (_) { return true; } | ||
| fs.writeFileSync(CACHE_FILE, `${JSON.stringify({ | ||
| ...state, | ||
| latestNpm: state.latestTemplateVersion, | ||
| notice: state.needsUpgrade ? buildNotice(state) : '', | ||
| checkedAt: Date.now(), | ||
| }, null, 2)}\n`, 'utf8'); | ||
| } catch (_) {} | ||
| } | ||
| // ── Main flow ──────────────────────────────────────────────────────────────── | ||
| function deleteCache() { | ||
| try { fs.rmSync(CACHE_FILE, { force: true }); } catch (_) {} | ||
| } | ||
| function main() { | ||
| if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) return; | ||
| if (!isEnabled()) return; | ||
| const cache = readCache(); | ||
| if (cache) { | ||
| // If already checked today, do not query npm again; if the cache still says an upgrade is needed, keep reminding in each new session. | ||
| const needsUpgrade = cache.needsUpgrade === true || | ||
| cmpVer(cache.manifestVersion, cache.latestNpm) < 0; | ||
| if (needsUpgrade) { | ||
| const currentManifestVersion = getManifestVersion(); | ||
| if (currentManifestVersion && cache.latestNpm && | ||
| cmpVer(currentManifestVersion, cache.latestNpm) >= 0) { | ||
| deleteCache(); | ||
| return; | ||
| } | ||
| // SessionStart enters a new session: cache hit and upgrade still needed, so emit directly. | ||
| const notice = buildNotice(cache.latestNpm, cache.manifestVersion); | ||
| emitNotice(notice); | ||
| } | ||
| return; | ||
| } | ||
| if (process.env.CI || process.env.CONTINUOUS_INTEGRATION || !isEnabled()) return; | ||
| const manifestVersion = getManifestVersion(); | ||
| if (!manifestVersion) return; // No knowledge base, skip | ||
| if (!manifestVersion) return; | ||
| let latestNpm; | ||
| try { | ||
| const pkgName = getPackageName(); | ||
| latestNpm = queryNpmLatest(pkgName); | ||
| } catch (_) { | ||
| return; // Network unavailable, exit silently without writing cache (retry next time) | ||
| const cached = readCache(); | ||
| if (cached) { | ||
| const state = updateState({ | ||
| latestCoreVersion: cached.latestCoreVersion, | ||
| latestTemplateVersion: cached.latestTemplateVersion || cached.latestNpm, | ||
| }, manifestVersion); | ||
| if (!state.needsUpgrade) deleteCache(); | ||
| else emitNotice(buildNotice(state)); | ||
| return; | ||
| } | ||
| // Write cache (whether or not upgrade is needed, do not repeat the check today) | ||
| writeCache(latestNpm, manifestVersion); | ||
| if (cmpVer(manifestVersion, latestNpm) >= 0) return; // Already up to date | ||
| const notice = buildNotice(latestNpm, manifestVersion); | ||
| emitNotice(notice); | ||
| let metadata; | ||
| try { metadata = queryLatestMetadata(getPackageName()); } catch (_) { return; } | ||
| if (!metadata.latestCoreVersion || !metadata.latestTemplateVersion) return; | ||
| const state = updateState(metadata, manifestVersion); | ||
| writeCache(state); | ||
| if (state.needsUpgrade) emitNotice(buildNotice(state)); | ||
| } | ||
| main(); |
| { | ||
| "version": "3.4.1", | ||
| "version": "3.5.0", | ||
| "projectRev": 2, | ||
@@ -4,0 +4,0 @@ "knowledgeRoot": ".Knowledge", |
| --- | ||
| id: flow2spec-dsh-adapter | ||
| revision: 0 | ||
| revision: 1 | ||
| summary: "DeepSeek Harness project skill initialization and directory adapter" | ||
@@ -16,2 +16,3 @@ primary: feature | ||
| - A missing root `AGENTS.md` receives a full entry; an existing entry is preserved. | ||
| - Native Cordis plugin work remains a later roadmap item. | ||
| - Native Cordis plugin is available: [Flow2Spec-DeepSeek-Harness](https://github.com/double-coding-lab/Flow2Spec-DeepSeek-Harness), npm package `@double-coding/flow2spec-deepseek-harness`. | ||
| - `flow2spec init dsh` remains the project-level fallback when the native plugin is not installed. |
@@ -100,5 +100,5 @@ --- | ||
| 1. Read `flow2spec.config.json` -> if `updateCheck.enabled` is not `true`, skip and show no notice. | ||
| 2. Read `.Knowledge/update-check.json` -> if the file exists and `checkedAt` is the same local calendar day as today (`new Date(checkedAt).toDateString() === new Date().toDateString()`), do not check npm again. However, if `needsUpgrade=true` or `latestNpm > manifestVersion`, the first user reply in this session must still remind the user to run `f2s-kb-upgrade`; if the current `.Knowledge/manifest-routing.json.version` is already no lower than `latestNpm`, delete that cache and stop reminding. | ||
| 2. Read `.Knowledge/update-check.json` -> if the file exists and `checkedAt` is on the same local calendar day, do not query npm again. When `coreUpdateAvailable=true`, the agent may run `flow2spec update --core`. If `templateUpdateAvailable=false`, run one idempotent `flow2spec init <initialized agents>` to refresh the Hook, delete the cache, and do not enter `f2s-kb-upgrade`. If `templateUpdateAvailable=true`, update Core, run init, then use `projectRev` / `pkgRev` to choose the fast path or full flow. `.Knowledge/manifest-routing.json.version` is Template Version and must not be compared directly with Core Version. | ||
| 3. If neither of the two steps above skipped the check: run the update-check script under the current agent configuration root (Claude: `node .claude/hooks/f2s-update-check.js`; Cursor: `node .cursor/hooks/f2s-update-check.js`; Codex: `node .codex/hooks/f2s-update-check.js`) and parse JSON from stdout: | ||
| - If it contains `hookSpecificOutput.additionalContext`: **tell the user** that content (suggest running the `f2s-kb-upgrade` skill). | ||
| - If it contains `hookSpecificOutput.additionalContext`: **tell the user** that content and follow its separate Core-only or Template-update instructions. | ||
| - If there is no output or parsing fails: stay silent. | ||
@@ -109,3 +109,3 @@ 4. If any step errors, silently skip it and do not affect normal conversation. | ||
| When adding or modifying `.Knowledge/topics/<topic>.md`, adjusting `manifest-routing.topicDependencies`, or deleting / migrating topics, the **authoring-side** guideline uses **`rules/f2s-topic-authoring.*`** as the single source of truth (**Cursor/Claude**: `rules/f2s-topic-authoring.mdc`; **Codex**: `.codex/topics/f2s-topic-authoring.md`). This entry is the **consumption side** (how to route / read / fall back using existing topics), and both coexist; in hard conflicts, this unified entry wins. `f2s-kb-build` / `f2s-kb-add` / `f2s-kb-feat` / `f2s-kb-fix` / `f2s-kb-sync` / `f2s-kb-migrate` / `f2s-kb-rm` must Read that full rule before any topic write. | ||
| When adding or modifying `.Knowledge/topics/<topic>.md`, adjusting `manifest-routing.topicDependencies`, or deleting / migrating topics, the **authoring-side** guideline uses **`rules/f2s-topic-authoring.*`** as the single source of truth (**Cursor/Claude**: `rules/f2s-topic-authoring.mdc`; **Codex**: `.codex/topics/f2s-topic-authoring.md`). This entry is the **consumption side** (how to route / read / fall back using existing topics), and both coexist; in hard conflicts, this unified entry wins. `f2s-kb-build` / `f2s-kb-add` / `f2s-kb-feat` / `f2s-kb-fix` / `f2s-kb-sync` / `f2s-kb-rm` must Read that full rule before any topic write. | ||
@@ -112,0 +112,0 @@ ## Prohibited |
@@ -42,3 +42,2 @@ --- | ||
| | 合并上下文冲突、解决知识库冲突; merge context conflict, resolve KB conflict | `f2s-kb-merge` | | ||
| | 知识库迁移、旧版迁移; knowledge-base migration, legacy migration | `f2s-kb-migrate` | | ||
| | 删除项目上下文; delete project context | `f2s-kb-rm` | | ||
@@ -45,0 +44,0 @@ | 知识库模板升级、知识库升级、一键升级迁移; KB template upgrade, KB upgrade, one-click upgrade migration | `f2s-kb-upgrade` | |
@@ -19,3 +19,3 @@ --- | ||
| - Add a reference to a topic id in `taskToTopicRules[].topics`; | ||
| - Delete or migrate a topic (`f2s-kb-rm` / `f2s-kb-migrate` / `f2s-kb-upgrade`). | ||
| - Delete or migrate a topic (`f2s-kb-rm` / `f2s-kb-upgrade`). | ||
@@ -22,0 +22,0 @@ ## 1. Topic Naming |
| --- | ||
| name: f2s-kb-upgrade | ||
| description: Knowledge-base template upgrade skill (this SKILL only): **V1 flow branch** must run f2s-kb-migrate first, then run flow2spec init inside the workflow; **current repositories (flow branch V2+, including Flow2Spec npm v3.x projects already using .Knowledge)** run init to align manifest-routing + matcher shards (package `manifest-matchers.json` is only an init merge seed and is not written into .Knowledge). Triggers: f2s-kb-upgrade、一键升级迁移、旧项目升级、知识库模板升级、upgrade knowledge base、template upgrade. Note: do not call standalone flow2spec init an "upgrade command"; **V1/V2+ are flow-branch labels inside this skill, not npm package major versions**. | ||
| description: Knowledge-base template upgrade skill (this SKILL only): **current repositories (flow branch V2+, including Flow2Spec npm v3.x projects already using .Knowledge)** run init to align manifest-routing + matcher shards (package `manifest-matchers.json` is only an init merge seed and is not written into .Knowledge); **non-topic version updates** (projectRev == pkgRev after init) can be completed by the agent directly running flow2spec init, without entering this skill's full flow; **legacy layouts (flow branch V1) no longer have built-in migration support** (f2s-kb-migrate has been removed from the package). Triggers: f2s-kb-upgrade、旧项目升级、知识库模板升级、upgrade knowledge base、template upgrade. Note: do not call standalone flow2spec init an "upgrade command"; **V1/V2+ are flow-branch labels inside this skill, not npm package major versions**. | ||
| --- | ||
@@ -15,3 +15,3 @@ | ||
| - **`flow2spec init` does not write business knowledge**: it does not replace `f2s-kb-add`, `f2s-kb-fix`, `f2s-kb-feat`, `f2s-kb-sync`, `f2s-kb-build`, or similar maintenance of `stock-docs` / `req-docs` / `topics` bodies and business routing terms. | ||
| - This skill completes **directory, template placeholder, and routing-structure alignment under the package version**. If the user says "write the new capability into the knowledge base", guide them to **`f2s-kb-sync` / `f2s-kb-add`** etc., not just `f2s-kb-upgrade`. | ||
| - This skill completes **directory, template placeholder, and routing-structure alignment under Template Version**. Core code version and Template Version are independent. If the user says "write the new capability into the knowledge base", guide them to **`f2s-kb-sync` / `f2s-kb-add`** etc., not just `f2s-kb-upgrade`. | ||
| - This skill is responsible for auditing existing `topicMetadata`: `primary` / `tags` are only for governance, audit, inventory, and reading expectations; they do not participate in route matching or execution requirements. Execution requirements still come from `AGENTS.md`, rules, skills, and topic bodies. | ||
@@ -21,3 +21,3 @@ | ||
| **Field location**: root-level integer field `projectRev` in `templates/{zh-CN,en-US}/knowledge/manifest-routing.json` (starts at `1`). | ||
| **Field location**: root-level integer field `projectRev` in the Core package's `knowledge/manifest-routing.json` template (starts at `1`). | ||
@@ -32,11 +32,11 @@ **Field write semantics (read first)**: | ||
| **Must bump (at least `+1` per release)** when any of the following changes: | ||
| - Any body change / addition / deletion / rename of `templates/<locale>/knowledge/topics/<topic>.md`; | ||
| - `includeAny` entries, `id` changes, or addition / deletion of any `templates/<locale>/knowledge/matchers/<id>.json`; | ||
| - Any change in `topicPaths` / `taskToTopicRules` / `topicDependencies` / `fallbackTopic` / `topicMetadata` of `templates/<locale>/knowledge/manifest-routing.json`; | ||
| - Any change in the "topic overview" section or package-level sections of `templates/<locale>/knowledge/index.md`. | ||
| - Any body change / addition / deletion / rename of `knowledge/topics/<topic>.md`; | ||
| - `includeAny` entries, `id` changes, or addition / deletion of any `knowledge/matchers/<id>.json`; | ||
| - Any change in `topicPaths` / `taskToTopicRules` / `topicDependencies` / `fallbackTopic` / `topicMetadata` of `knowledge/manifest-routing.json`; | ||
| - Any change in the "topic overview" section or package-level sections of `knowledge/index.md`. | ||
| **Does NOT require a bump**: | ||
| - Package source (`lib/`, `cli.js`, `scripts/`), `AGENTS.md`, `README*`; | ||
| - `templates/<locale>/flow2spec.config.json` default values; | ||
| - `templates/<locale>/rules/*` / `templates/<locale>/skills/*` rule/skill body changes (unrelated to the topic layer; no need to trigger the full flow). | ||
| - `flow2spec.config.json` default values; | ||
| - `rules/*` / `skills/*` rule/skill body changes (unrelated to the topic layer; no need to trigger the full flow, but Template Version must still be raised explicitly so the Hook distributes the new content). | ||
@@ -50,3 +50,3 @@ **One-line rule**: any topic-layer artifact (topic / matcher / manifest / index) under `templates/<locale>/knowledge/` changed -> must bump; otherwise do not. Missing a bump causes user `f2s-kb-upgrade` to take the fast path and miss the topic changes from the package. | ||
| - **Main agent must control** (must not delegate): | ||
| 1. **Version branching**: **V1** runs `f2s-kb-migrate` first, then enters this skill; **current repositories (V2+)** directly enter the `init` flow (including Flow2Spec **npm v3.x** etc.; as long as step 0 "current repository" conditions are met, use this branch. **Do not** create a separate flow just because the package major version is 3). | ||
| 1. **Version branching**: **V1 (legacy layout)** no longer supports automatic migration — on a hit, stop this skill and tell the user how to proceed (see step 0); **current repositories (V2+)** directly enter the `init` flow (including Flow2Spec **npm v3.x** etc.; as long as step 0 "current repository" conditions are met, use this branch. **Do not** create a separate flow just because the package major version is 3). | ||
| 2. **Re-read after `init`**: re-read `f2s-kb-upgrade/SKILL.md` from disk and compare whether the identifier changed. | ||
@@ -59,12 +59,18 @@ 3. **Rerun the whole skill**: when SKILL changed, rerun from the beginning according to the new literal text until two consecutive rounds show no changes. | ||
| ## Why this coexists with `f2s-kb-migrate` | ||
| ## Non-topic version updates: the agent may run `init` directly (no need to enter this skill) | ||
| | Skill | Problem solved | | ||
| | --- | --- | | ||
| | **`f2s-kb-migrate`** | **Structural move**: `docs-index.md` / `index-doc.md`, `rules/main.md(c)`, business `skills/`, scattered `stock-docs`/`req-docs` -> **migrate into `.Knowledge`**, write `migration-report.md`, and confirm deletion list with the user. It does not run npm package upgrade. | | ||
| | **This skill `f2s-kb-upgrade`** | **Package and template alignment**: run **`flow2spec init`**, merge **`manifest-routing.json`** with **`matchers/*.json`**, refresh each agent's **`rules`/`skills`** (or Codex **`AGENTS.md`**); `init` also copies the current-language **`index.md` -> `.Knowledge/template/index.template.md`** as a comparison snapshot. **`.Knowledge/index.md`** is diff-aligned in step 3b; init **does not** automatically change its body. | | ||
| When the version check reports a Core update with `templateUpdateAvailable=false`, the agent may **update Core and directly run** `flow2spec init <initialized agents>`: | ||
| - **One-click closure for old projects**: **first `f2s-kb-migrate`** -> **then this skill** (`init`). Do not use only `init` as a substitute for full migration. | ||
| - **Projects already using new `.Knowledge`**: **run only this skill**; do not repeat migrate. | ||
| - After `init`, read the project-side `.Knowledge/manifest-routing.json`: if `projectRev` **equals** `pkgRev` (a non-topic version update) -> the update is complete, delete `.Knowledge/update-check.json`, **no need to enter this skill**; | ||
| - If they **differ** (topic-layer changes included) -> enter this skill's full flow (judge from step 2c, without re-running `init`). | ||
| - This path uses the same judgment as this skill's step 2c "fast path"; when the user explicitly asks for a "full flow / reset", still execute this skill in full. | ||
| - `.Knowledge/manifest-routing.json.version` is Template Version; never compare it directly with Core Version. | ||
| ## Legacy layouts (V1) no longer have built-in migration | ||
| `f2s-kb-migrate` has been removed from the package; automatic migration of legacy knowledge organization is no longer supported. When step 0's V1 signals hit, this skill **stops** and tells the user two ways to proceed: | ||
| 1. Use a historical package version that still ships `f2s-kb-migrate` (npm `@double-coding/flow2spec@3.4.x` or earlier) to perform the one-time migration, then come back to the latest version and run this skill; | ||
| 2. Manually move legacy rules, business skills, and scattered `stock-docs` / `req-docs` into the `.Knowledge` shape, then run this skill. | ||
| **Why does each configured client directory have a same-named `SKILL.md`?** | ||
@@ -100,36 +106,21 @@ Each client only loads `skills/` under **its own configuration root**. `flow2spec init` writes the current-language skill content into the selected agent directories. | ||
| ### Step -1: Global flow2spec Version Preflight (Required, Before Everything, Foreground Probe by Main Agent) | ||
| ### Step -1: CLI/Core/Template Version Preflight (Required, Before Everything) | ||
| **Purpose**: prefer using the **already-installed global `flow2spec`** whenever possible. Only trigger a global upgrade when it is **missing** or **out of date**; when it is already at latest, **completely skip** any upgrade action. This also decides the **default form** of the step 2 command (whether to use `flow2spec init` or `npx @latest init`). | ||
| The main agent runs these read-only probes in the foreground: | ||
| **Action**: before entering step 0, the main agent **sequentially runs 3 probes in the foreground** (all read-only, no side effects, seconds to return — no sub-agent needed): | ||
| ```bash | ||
| # 1. Probe whether flow2spec is installed globally on this machine | ||
| flow2spec --version 2>/dev/null || echo __F2S_NOT_INSTALLED__ | ||
| # 2. Query the latest version on npm (may fail on restricted networks — allowed) | ||
| npm view @double-coding/flow2spec version 2>/dev/null || echo __F2S_NPM_UNREACHABLE__ | ||
| # 3. (Backup) if step 1 returned __F2S_NOT_INSTALLED__, confirm npx is available | ||
| command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ | ||
| flow2spec version | ||
| flow2spec update --check | ||
| ``` | ||
| **Three-way decision** (pick one branch based on the results; record it in this-turn context and use it to drive step 2 & step 5 summary): | ||
| Record CLI Version, Core Version, Core Range, Template Version, Protocol Version, and the latest npm Core/Template values: | ||
| | Case | Condition | Action | Default step 2 command | | ||
| | --- | --- | --- | --- | | ||
| | **A. Installed & on latest** | Step 1 returned version `V`, step 2 returned version `L`, and `V === L` | **Skip upgrade entirely** — no sub-agent, no `npm i -g` this turn | **`flow2spec init <agents...>`** (use global CLI) | | ||
| | **B. Installed but behind** | Step 1 returned `V`, step 2 returned `L`, and `V !== L` (`V < L` or semver-unequal) | **Dispatch an independent sub-agent (fire-and-forget)** to run `npm i -g @double-coding/flow2spec@latest` in the background — no wait, no block. Current turn's step 2 still uses `npx @latest` to guarantee this session gets the latest template | **`npx @double-coding/flow2spec@latest init <agents...>`** | | ||
| | **C. Not installed or latest unknown** | Step 1 hit `__F2S_NOT_INSTALLED__`, OR step 2 hit `__F2S_NPM_UNREACHABLE__` and step 1 also didn't return a version | If A doesn't hold and **not installed**: same as B — dispatch a sub-agent to `npm i -g ...@latest`. If step 2 failed but step 1 shows some installed version: treat as B without a way to compare to latest — **do not** dispatch an upgrade, just note "latest unknown, use npx conservatively" | **`npx @double-coding/flow2spec@latest init <agents...>`** | | ||
| | Case | Action | Default step 2 command | | ||
| | --- | --- | --- | | ||
| | **A. Template is current** | If only Core changed, run `flow2spec update --core`, then one idempotent init to refresh the Hook, clear the cache, and **stop this skill**. If Core is also current, stop immediately. | `flow2spec init <agents...>` | | ||
| | **B. Template changed and latest Core is inside Core Range** | Run `flow2spec update --core`, then continue to step 0. A compatible Core update does not require a CLI release or upgrade. | `flow2spec init <agents...>` | | ||
| | **C. Not installed, unknown, or latest Core is outside the range** | Use an explicit latest CLI/Core combination so npx cannot reuse an old Core. If latest Core still is not compatible, upgrade to a CLI that supports it first. | `npx --yes --package <cli-package>@latest --package <core-package>@latest flow2spec init <agents...>` | | ||
| **Orchestration (required)**: | ||
| If the preflight fails, case C is allowed as fallback, but never treat Core Version as Template Version. This step does not mandate a sub-agent or a background global install. | ||
| - **Branch A**: main agent skips all upgrade actions and **does not** dispatch a sub-agent; step 2's default is `flow2spec init`. | ||
| - **Branch B / C**: only when an upgrade is actually needed (missing or behind), dispatch an **independent sub-agent** fire-and-forget to run `npm i -g @double-coding/flow2spec@latest`; **do not wait**, **do not block** the main flow. Success/failure does not enter the SKILL summary conclusion. This sub-agent dispatch is **mandatory** and **not subject to** `flow2spec.config.json.subAgent` (a one-off global npm install is not a business split). | ||
| - **Write permission**: the sub-agent only runs that shell command and **does not** touch any project file (`.Knowledge`, `manifest-routing.json`, `index.md`, etc.). Write-permission constraints remain unchanged. | ||
| - **Probe failure fallback**: if all 3 probes fail (no shell permission, extremely restricted env), treat as branch C and use `npx @latest`; alternatively, skip step -1 entirely and rely on `cli.js`'s `maybeAutoUpdateGlobalInstall()` tail fallback. | ||
| **Relation to cli.js**: | ||
| - `maybeAutoUpdateGlobalInstall()` inside cli.js is the `init` tail fallback. **No conflict with this step**: this step probes/dispatches before the foreground init; cli's fallback runs once more at init's tail. If both succeed it's a no-op; if the first fails the second still has a chance to fix it. | ||
| ### Step 0: Version Judgment and Branching (Required, Before init) | ||
@@ -139,3 +130,3 @@ | ||
| **V1 - Legacy knowledge organization (must migrate before init)** | ||
| **V1 - Legacy knowledge organization (built-in migration no longer available)** | ||
| Hit **any** strong signal: | ||
@@ -146,3 +137,3 @@ | ||
| **Action**: first execute the full **`f2s-kb-migrate`** workflow (including `migration-report` and deletion-list confirmation), **then** enter steps 1-5 to execute `flow2spec init`. | ||
| **Action**: **stop this skill** and tell the user the two ways to proceed per "Legacy layouts (V1) no longer have built-in migration" above (one-time migration with a historical package version / manual move into `.Knowledge`); **do not** continue steps 1-5 on a V1 layout. | ||
@@ -157,3 +148,3 @@ **Current repository (V2+) - Already on `.Knowledge` + new routing (package/shape alignment only)** | ||
| **Action**: directly enter steps 1-5; **no** migrate is needed unless the user explicitly asks to redo migration. | ||
| **Action**: directly enter steps 1-5. | ||
@@ -172,6 +163,6 @@ ### Step 1: Confirm `init` Mode Inside This Skill (Required) | ||
| 1. **Step -1 returned A (installed & on latest)**: use the global CLI directly (**preferred**): | ||
| 1. **Step -1 returned A/B (local CLI/Core is usable)**: use the current CLI: | ||
| - `flow2spec init <agents...>` | ||
| 2. **Step -1 returned B/C (missing / behind / latest unknown)**: fetch npm latest (**guarantees this session gets the latest template**): | ||
| - `npx @double-coding/flow2spec@latest init <agents...>` | ||
| 2. **Step -1 returned C**: explicitly combine latest CLI/Core so this session gets the latest Core and templates: | ||
| - `npx --yes --package <cli-package>@latest --package <core-package>@latest flow2spec init <agents...>` | ||
| 3. For overwrite reset: | ||
@@ -185,3 +176,3 @@ - Append `--reset-knowledge` to the above command. | ||
| > **Helper commands (user self-inspection)**: `flow2spec --version` shows the current global version; `flow2spec update` triggers the CLI's built-in self-update. These do **not** replace this SKILL's full flow — they only keep the global CLI fresh; topic-layer alignment still requires step 2 and beyond. | ||
| > **Helper commands (user self-inspection)**: `flow2spec version` shows the five version dimensions; `flow2spec update --check|--cli|--core` checks updates, updates CLI, or updates a compatible Core. These commands do not replace this skill's full flow after Template Version changes. | ||
@@ -329,3 +320,3 @@ **After step 2 completes**: immediately execute the above **"init and skill self-update"** loop: re-read **`skills/f2s-kb-upgrade/SKILL.md`**. If updated, **rerun from step 2c per the new literal text** (**do not run `init` a second time**; avoid using the old SKILL for subsequent verification). | ||
| - **Step -1 global version preflight**: `A Installed & on latest (upgrade skipped) / B Installed but behind (sub-agent dispatched to run npm i -g in background) / C Missing or latest unknown (dispatched / conservative npx)`; current version=`<V>`, latest=`<L or unknown>` | ||
| - Command run inside this skill: `<actual flow2spec init ... or npx @latest init ...>` | ||
| - Command run inside this skill: `<actual flow2spec init ... or explicit latest CLI/Core combination init ...>` | ||
| - init mode: `incremental` / `overwrite reset (--reset-knowledge)` | ||
@@ -361,4 +352,4 @@ - Result: `success` / `failure` | ||
| 1. **Step -1** was performed: before entering step 0, **3 foreground probes** were run sequentially (`flow2spec --version` / `npm view ... version` / `npx` availability) and one of the A/B/C branches was determined. Only under B/C did an **independent sub-agent** get dispatched to run `npm i -g @double-coding/flow2spec@latest` in the background (fire-and-forget); under A **no upgrade action** was dispatched. Step 2's default command form was chosen accordingly (A → `flow2spec init`, B/C → `npx @latest init`); the summary clearly states the branch and version comparison. | ||
| 2. **Step 0** was performed: V1 did not skip migrate, and **current repositories (V2+)** did not incorrectly run migrate. | ||
| 1. **Step -1** was performed: `flow2spec version` and `flow2spec update --check` recorded CLI/Core/Core Range/Template/Protocol; Core-only updates refreshed Core/Hook and stopped, while Template updates selected the current CLI or an explicit latest CLI/Core combination through A/B/C. | ||
| 2. **Step 0** was performed: on V1 the skill stopped and told the user how to proceed, and **current repositories (V2+)** entered the `init` flow normally. | ||
| 3. **Before step 2** recorded the project-side `projectRev` (`projectRev`), and **after step 2 `init`** re-read `pkgRev` and executed **step 2c** judgment. | ||
@@ -365,0 +356,0 @@ 4. After **step 2 `init`**, **`f2s-kb-upgrade/SKILL.md`** was re-read: on full flow, a change must trigger **a rerun from step 2c per the new literal text** (**no second `init`**); on fast path, the loop can be skipped (see "init and skill self-update" / "fast-path exception"). |
| #!/usr/bin/env node | ||
| 'use strict'; | ||
| /** | ||
| * flow2spec SessionStart hook — 每天第一次对话时检查版本更新。 | ||
| * 比较本地知识库 manifest-routing.json 的 version 与 npm 最新版本: | ||
| * - 一致或本地更新 → 静默退出 | ||
| * - 落后 → 向 Agent 上下文注入一行提示(建议执行 f2s-kb-upgrade) | ||
| * 已检查过且无需升级则静默;已检查且仍需升级时,每个新会话继续注入提醒。 | ||
| * 由 flow2spec init 写入对应 agent 的 hooks/f2s-update-check.js。 | ||
| */ | ||
| const fs = require('fs'); | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
@@ -16,173 +9,149 @@ const { execFileSync } = require('child_process'); | ||
| const MANIFEST_PATH = path.join(process.cwd(), '.Knowledge', 'manifest-routing.json'); | ||
| const CACHE_DIR = path.join(process.cwd(), '.Knowledge'); | ||
| const CACHE_FILE = path.join(CACHE_DIR, 'update-check.json'); | ||
| const CACHE_FILE = path.join(process.cwd(), '.Knowledge', 'update-check.json'); | ||
| const PACKAGE_NAME_PLACEHOLDER = '__FLOW2SPEC_' + 'PACKAGE_NAME__'; | ||
| const PACKAGE_NAME = '__FLOW2SPEC_PACKAGE_NAME__'; | ||
| const GENERATED_CORE_VERSION = '__FLOW2SPEC_CORE_VERSION__'; | ||
| const GENERATED_TEMPLATE_VERSION = '__FLOW2SPEC_TEMPLATE_VERSION__'; | ||
| // ── 缓存 ──────────────────────────────────────────────────────────────────── | ||
| function readJson(file) { | ||
| try { return JSON.parse(fs.readFileSync(file, 'utf8')); } catch (_) { return null; } | ||
| } | ||
| function readCache() { | ||
| if (!fs.existsSync(CACHE_FILE)) return null; | ||
| try { | ||
| const d = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8')); | ||
| if (!d || typeof d !== 'object') return null; | ||
| const checkedAt = Number(d.checkedAt || 0); | ||
| if (!checkedAt) return null; | ||
| if (new Date(checkedAt).toDateString() !== new Date().toDateString()) return null; | ||
| return d; | ||
| } catch (_) { return null; } | ||
| function parseVer(value) { | ||
| return String(value || '').replace(/^v/, '').split(/[.-]/).slice(0, 3).map((part) => { | ||
| const number = Number.parseInt(part, 10); | ||
| return Number.isFinite(number) ? number : 0; | ||
| }); | ||
| } | ||
| function getProjectName() { | ||
| try { | ||
| const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')); | ||
| if (pkg && pkg.name) return String(pkg.name); | ||
| } catch (_) {} | ||
| return path.basename(process.cwd()); | ||
| function cmpVer(left, right) { | ||
| const a = parseVer(left), b = parseVer(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 buildNotice(latestNpm, manifestVersion) { | ||
| const userMsg = `📦 [flow2spec] 当前项目「${getProjectName()}」知识库版本 v${manifestVersion},低于最新包版本 v${latestNpm}。可执行 f2s-kb-upgrade skill 对齐模板与路由。`; | ||
| return [ | ||
| `[flow2spec/agent-instruction] 你必须在本轮回复开头将下列升级提示原文转告用户,不得省略、不得仅在内部判断后跳过:`, | ||
| ``, | ||
| userMsg, | ||
| ].join('\n'); | ||
| function getProjectName() { | ||
| return readJson(path.join(process.cwd(), 'package.json'))?.name || path.basename(process.cwd()); | ||
| } | ||
| function emitNotice(notice) { | ||
| process.stdout.write( | ||
| JSON.stringify({ | ||
| additional_context: notice, | ||
| hookSpecificOutput: { | ||
| hookEventName: 'SessionStart', | ||
| additionalContext: notice, | ||
| }, | ||
| }) + '\n' | ||
| ); | ||
| function getManifestVersion() { | ||
| return readJson(MANIFEST_PATH)?.version || null; | ||
| } | ||
| function writeCache(latestNpm, manifestVersion) { | ||
| try { | ||
| const needsUpgrade = cmpVer(manifestVersion, latestNpm) < 0; | ||
| fs.writeFileSync( | ||
| CACHE_FILE, | ||
| `${JSON.stringify({ | ||
| latestNpm, | ||
| manifestVersion, | ||
| needsUpgrade, | ||
| notice: needsUpgrade ? buildNotice(latestNpm, manifestVersion) : '', | ||
| checkedAt: Date.now(), | ||
| }, null, 2)}\n`, | ||
| 'utf8' | ||
| ); | ||
| } catch (_) {} | ||
| function getPackageName() { | ||
| return PACKAGE_NAME && PACKAGE_NAME !== PACKAGE_NAME_PLACEHOLDER | ||
| ? PACKAGE_NAME | ||
| : '@double-coding/flow2spec-core'; | ||
| } | ||
| function deleteCache() { | ||
| try { | ||
| if (fs.existsSync(CACHE_FILE)) fs.unlinkSync(CACHE_FILE); | ||
| } catch (_) {} | ||
| function isEnabled() { | ||
| const config = readJson(path.join(process.cwd(), 'flow2spec.config.json')); | ||
| return config?.updateCheck?.enabled !== false; | ||
| } | ||
| // ── 版本比较 ───────────────────────────────────────────────────────────────── | ||
| function parseVer(v) { | ||
| return String(v || '').replace(/^v/, '').split(/[.-]/).slice(0, 3).map((p) => { | ||
| const n = Number.parseInt(p, 10); | ||
| return Number.isFinite(n) ? n : 0; | ||
| }); | ||
| function readCache() { | ||
| const cache = readJson(CACHE_FILE); | ||
| if (!cache?.checkedAt) return null; | ||
| return new Date(cache.checkedAt).toDateString() === new Date().toDateString() ? cache : null; | ||
| } | ||
| /** a < b → 负数;a === b → 0;a > b → 正数 */ | ||
| function cmpVer(a, b) { | ||
| const av = parseVer(a), bv = parseVer(b); | ||
| for (let i = 0; i < 3; i++) { | ||
| const d = (av[i] || 0) - (bv[i] || 0); | ||
| if (d !== 0) return d; | ||
| } | ||
| return 0; | ||
| function queryLatestMetadata(packageName) { | ||
| const output = execFileSync( | ||
| 'npm', | ||
| ['view', packageName, 'version', 'templateVersion', '--json', '--registry=https://registry.npmjs.org'], | ||
| { encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'] }, | ||
| ); | ||
| const metadata = JSON.parse(output); | ||
| const latestCoreVersion = typeof metadata === 'string' ? metadata : metadata.version; | ||
| const latestTemplateVersion = typeof metadata === 'string' | ||
| ? metadata | ||
| : metadata.templateVersion || metadata.version; | ||
| return { latestCoreVersion, latestTemplateVersion }; | ||
| } | ||
| // ── 读取 ───────────────────────────────────────────────────────────────────── | ||
| function getManifestVersion() { | ||
| if (!fs.existsSync(MANIFEST_PATH)) return null; | ||
| try { | ||
| return JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8')).version || null; | ||
| } catch (_) { return null; } | ||
| function updateState(metadata, manifestVersion) { | ||
| const coreUpdateAvailable = cmpVer(GENERATED_CORE_VERSION, metadata.latestCoreVersion) < 0; | ||
| const templateUpdateAvailable = cmpVer(manifestVersion, metadata.latestTemplateVersion) < 0; | ||
| return { | ||
| packageName: getPackageName(), | ||
| generatedByCoreVersion: GENERATED_CORE_VERSION, | ||
| generatedTemplateVersion: GENERATED_TEMPLATE_VERSION, | ||
| manifestVersion, | ||
| ...metadata, | ||
| coreUpdateAvailable, | ||
| templateUpdateAvailable, | ||
| needsUpgrade: coreUpdateAvailable || templateUpdateAvailable, | ||
| }; | ||
| } | ||
| function getPackageName() { | ||
| if (PACKAGE_NAME && PACKAGE_NAME !== PACKAGE_NAME_PLACEHOLDER) { | ||
| return PACKAGE_NAME; | ||
| function buildNotice(state) { | ||
| const project = getProjectName(); | ||
| const versionSummary = `Core v${state.generatedByCoreVersion} -> v${state.latestCoreVersion},Template v${state.manifestVersion} -> v${state.latestTemplateVersion}`; | ||
| if (state.templateUpdateAvailable) { | ||
| return [ | ||
| '[flow2spec/agent-instruction] 你必须在本轮回复开头转告用户以下更新提示:', | ||
| '', | ||
| `📦 [flow2spec] 当前项目「${project}」有模板更新(${versionSummary})。`, | ||
| '', | ||
| '[flow2spec/agent-instruction] 可先执行 `flow2spec update --core`,再执行 `flow2spec init <已初始化的 agents>`。init 后读取 `.Knowledge/manifest-routing.json`:projectRev 与 pkgRev 相等则更新完成并删除 `.Knowledge/update-check.json`;不等时再执行 f2s-kb-upgrade(可从步骤 2c 继续)。', | ||
| ].join('\n'); | ||
| } | ||
| return '@double-coding/flow2spec'; | ||
| return [ | ||
| '[flow2spec/agent-instruction] 你必须在本轮回复开头转告用户以下更新提示:', | ||
| '', | ||
| `📦 [flow2spec] 当前项目「${project}」仅有 Core 程序更新(${versionSummary}),模板版本未变化。`, | ||
| '', | ||
| '[flow2spec/agent-instruction] 可执行 `flow2spec update --core`,随后执行一次幂等 `flow2spec init <已初始化的 agents>` 刷新 Hook。Template Version 未变化,不进入 f2s-kb-upgrade;完成后删除 `.Knowledge/update-check.json`。', | ||
| ].join('\n'); | ||
| } | ||
| function queryNpmLatest(pkgName) { | ||
| return execFileSync('npm', ['view', pkgName, 'version'], { | ||
| encoding: 'utf8', | ||
| timeout: 5000, | ||
| stdio: ['ignore', 'pipe', 'ignore'], | ||
| }).trim(); | ||
| function emitNotice(notice) { | ||
| process.stdout.write(JSON.stringify({ | ||
| additional_context: notice, | ||
| hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: notice }, | ||
| }) + '\n'); | ||
| } | ||
| // ── 配置开关 ────────────────────────────────────────────────────────────────── | ||
| function isEnabled() { | ||
| function writeCache(state) { | ||
| try { | ||
| const cfg = JSON.parse(fs.readFileSync( | ||
| path.join(process.cwd(), 'flow2spec.config.json'), 'utf8' | ||
| )); | ||
| const uc = cfg && cfg.updateCheck; | ||
| if (uc && typeof uc.enabled === 'boolean') return uc.enabled; | ||
| return true; | ||
| } catch (_) { return true; } | ||
| fs.writeFileSync(CACHE_FILE, `${JSON.stringify({ | ||
| ...state, | ||
| latestNpm: state.latestTemplateVersion, | ||
| notice: state.needsUpgrade ? buildNotice(state) : '', | ||
| checkedAt: Date.now(), | ||
| }, null, 2)}\n`, 'utf8'); | ||
| } catch (_) {} | ||
| } | ||
| // ── 主流程 ──────────────────────────────────────────────────────────────────── | ||
| function deleteCache() { | ||
| try { fs.rmSync(CACHE_FILE, { force: true }); } catch (_) {} | ||
| } | ||
| function main() { | ||
| if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) return; | ||
| if (!isEnabled()) return; | ||
| const cache = readCache(); | ||
| if (cache) { | ||
| // 今天已检查过则不重复查 npm;若缓存显示仍需升级,每个新会话继续提醒。 | ||
| const needsUpgrade = cache.needsUpgrade === true || | ||
| cmpVer(cache.manifestVersion, cache.latestNpm) < 0; | ||
| if (needsUpgrade) { | ||
| const currentManifestVersion = getManifestVersion(); | ||
| if (currentManifestVersion && cache.latestNpm && | ||
| cmpVer(currentManifestVersion, cache.latestNpm) >= 0) { | ||
| deleteCache(); | ||
| return; | ||
| } | ||
| // SessionStart 进入新会话:缓存命中且仍需升级,直接 emit。 | ||
| const notice = buildNotice(cache.latestNpm, cache.manifestVersion); | ||
| emitNotice(notice); | ||
| } | ||
| return; | ||
| } | ||
| if (process.env.CI || process.env.CONTINUOUS_INTEGRATION || !isEnabled()) return; | ||
| const manifestVersion = getManifestVersion(); | ||
| if (!manifestVersion) return; // 无知识库,跳过 | ||
| if (!manifestVersion) return; | ||
| let latestNpm; | ||
| try { | ||
| const pkgName = getPackageName(); | ||
| latestNpm = queryNpmLatest(pkgName); | ||
| } catch (_) { | ||
| return; // 网络不通,静默退出,不写缓存(下次还会重试) | ||
| const cached = readCache(); | ||
| if (cached) { | ||
| const state = updateState({ | ||
| latestCoreVersion: cached.latestCoreVersion, | ||
| latestTemplateVersion: cached.latestTemplateVersion || cached.latestNpm, | ||
| }, manifestVersion); | ||
| if (!state.needsUpgrade) deleteCache(); | ||
| else emitNotice(buildNotice(state)); | ||
| return; | ||
| } | ||
| // 写缓存(无论是否需要升级,今天不再重复检查) | ||
| writeCache(latestNpm, manifestVersion); | ||
| if (cmpVer(manifestVersion, latestNpm) >= 0) return; // 已是最新 | ||
| const notice = buildNotice(latestNpm, manifestVersion); | ||
| emitNotice(notice); | ||
| let metadata; | ||
| try { metadata = queryLatestMetadata(getPackageName()); } catch (_) { return; } | ||
| if (!metadata.latestCoreVersion || !metadata.latestTemplateVersion) return; | ||
| const state = updateState(metadata, manifestVersion); | ||
| writeCache(state); | ||
| if (state.needsUpgrade) emitNotice(buildNotice(state)); | ||
| } | ||
| main(); |
| { | ||
| "version": "3.4.1", | ||
| "version": "3.5.0", | ||
| "projectRev": 2, | ||
@@ -4,0 +4,0 @@ "knowledgeRoot": ".Knowledge", |
| --- | ||
| id: flow2spec-dsh-adapter | ||
| revision: 0 | ||
| revision: 1 | ||
| summary: "DeepSeek Harness 项目级技能初始化与目录适配" | ||
@@ -16,2 +16,3 @@ primary: feature | ||
| - 缺少根 `AGENTS.md` 时生成完整入口,已有入口不覆盖。 | ||
| - 原生 Cordis 插件属于后续路线图事项。 | ||
| - 原生 Cordis 插件已落地:独立仓 [Flow2Spec-DeepSeek-Harness](https://github.com/double-coding-lab/Flow2Spec-DeepSeek-Harness),npm 包 `@double-coding/flow2spec-deepseek-harness`。 | ||
| - `flow2spec init dsh` 仍作为未安装原生插件时的项目级兼容入口。 |
@@ -100,5 +100,5 @@ --- | ||
| 1. 读 `flow2spec.config.json` → 若 `updateCheck.enabled` 不为 `true`,跳过,不做任何提示。 | ||
| 2. 读 `.Knowledge/update-check.json` → 若文件存在且 `checkedAt` 与今日为同一自然日(`new Date(checkedAt).toDateString() === new Date().toDateString()`),不重复查 npm;但若 `needsUpgrade=true` 或 `latestNpm > manifestVersion`,本会话首次回复用户时仍须提醒执行 `f2s-kb-upgrade`;若当前 `.Knowledge/manifest-routing.json.version` 已不低于 `latestNpm`,删除该缓存并不再提示。 | ||
| 2. 读 `.Knowledge/update-check.json` → 若文件存在且 `checkedAt` 与今日为同一自然日,不重复查 npm;`coreUpdateAvailable=true` 时可执行 `flow2spec update --core`。若 `templateUpdateAvailable=false`,随后只执行一次幂等 `flow2spec init <已初始化的 agents>` 刷新 Hook,删除缓存,不进入 `f2s-kb-upgrade`;若 `templateUpdateAvailable=true`,更新 Core 后执行 init,再以 `projectRev` / `pkgRev` 判定快速路径或完整流程。`.Knowledge/manifest-routing.json.version` 表示 Template Version,禁止与 Core Version 直接比较。 | ||
| 3. 上述两步均未跳过时:执行当前 agent 配置根下的更新检测脚本(Claude:`node .claude/hooks/f2s-update-check.js`;Cursor:`node .cursor/hooks/f2s-update-check.js`;Codex:`node .codex/hooks/f2s-update-check.js`),解析标准输出的 JSON: | ||
| - 若含 `hookSpecificOutput.additionalContext`:**告知用户**该内容(建议执行 `f2s-kb-upgrade` skill)。 | ||
| - 若含 `hookSpecificOutput.additionalContext`:**告知用户**该内容,并按其中 agent-instruction 分别处理 Core-only 与 Template 更新。 | ||
| - 无输出或解析失败:静默,不提示。 | ||
@@ -109,3 +109,3 @@ 4. 以上步骤出现任何错误,静默跳过,不影响正常对话。 | ||
| 新增或修改 `.Knowledge/topics/<topic>.md`、调整 `manifest-routing.topicDependencies`、删除 / 迁移 topic 时,**创作侧** 准则以 **`rules/f2s-topic-authoring.*`** 为单一事实源(**Cursor/Claude**:`rules/f2s-topic-authoring.mdc`;**Codex**:`.codex/topics/f2s-topic-authoring.md`)。本入口为**消费侧**(如何按已有 topic 路由 / 读取 / 兜底),与之并存;硬冲突时以本入口为准。`f2s-kb-build` / `f2s-kb-add` / `f2s-kb-feat` / `f2s-kb-fix` / `f2s-kb-sync` / `f2s-kb-migrate` / `f2s-kb-rm` 在涉及 topic 落盘前须 Read 该条全文。 | ||
| 新增或修改 `.Knowledge/topics/<topic>.md`、调整 `manifest-routing.topicDependencies`、删除 / 迁移 topic 时,**创作侧** 准则以 **`rules/f2s-topic-authoring.*`** 为单一事实源(**Cursor/Claude**:`rules/f2s-topic-authoring.mdc`;**Codex**:`.codex/topics/f2s-topic-authoring.md`)。本入口为**消费侧**(如何按已有 topic 路由 / 读取 / 兜底),与之并存;硬冲突时以本入口为准。`f2s-kb-build` / `f2s-kb-add` / `f2s-kb-feat` / `f2s-kb-fix` / `f2s-kb-sync` / `f2s-kb-rm` 在涉及 topic 落盘前须 Read 该条全文。 | ||
@@ -112,0 +112,0 @@ ## 禁止项 |
@@ -42,3 +42,2 @@ --- | ||
| | 合并上下文冲突、解决知识库冲突 | `f2s-kb-merge` | | ||
| | 知识库迁移、旧版迁移 | `f2s-kb-migrate` | | ||
| | 删除项目上下文 | `f2s-kb-rm` | | ||
@@ -45,0 +44,0 @@ | 知识库模板升级、知识库升级、一键升级迁移 | `f2s-kb-upgrade` | |
@@ -18,3 +18,3 @@ --- | ||
| - 在 `taskToTopicRules[].topics` 中新增引用某个 topic id; | ||
| - 删除或迁移 topic(`f2s-kb-rm` / `f2s-kb-migrate` / `f2s-kb-upgrade`)。 | ||
| - 删除或迁移 topic(`f2s-kb-rm` / `f2s-kb-upgrade`)。 | ||
@@ -21,0 +21,0 @@ ## 1. topic 命名 |
| --- | ||
| name: f2s-kb-upgrade | ||
| description: 知识库模板升级技能(仅指本 SKILL):**流程分流 V1** 须先 f2s-kb-migrate 再在流程内代跑 flow2spec init;**现行库(流程代号 V2+,含已用 .Knowledge 的 Flow2Spec npm v3.x 等项目)** 则代跑 init 以对齐 manifest-routing + matchers 分片(包内 `manifest-matchers.json` 仅作 init 合并种子,不落盘 .Knowledge)。触发:f2s-kb-upgrade、一键升级迁移、旧项目升级、知识库模板升级。注意:不要把单独的 flow2spec init 称作「升级命令」;**V1/V2+ 为技能内分流代号,不等于 npm 包主版本号**。 | ||
| description: 知识库模板升级技能(仅指本 SKILL):**现行库(流程代号 V2+,含已用 .Knowledge 的 Flow2Spec npm v3.x 等项目)** 代跑 init 以对齐 manifest-routing + matchers 分片(包内 `manifest-matchers.json` 仅作 init 合并种子,不落盘 .Knowledge);**非主题版本更新**(init 后 projectRev == pkgRev)可由 agent 直接代跑 flow2spec init 完成,无需进入本技能完整流程;**旧版布局(流程分流 V1)已不再内置迁移支持**(f2s-kb-migrate 已随包移除)。触发:f2s-kb-upgrade、旧项目升级、知识库模板升级。注意:不要把单独的 flow2spec init 称作「升级命令」;**V1/V2+ 为技能内分流代号,不等于 npm 包主版本号**。 | ||
| --- | ||
@@ -15,3 +15,3 @@ | ||
| - **`flow2spec init` 不写业务知识**:不替代 `f2s-kb-add`、`f2s-kb-fix`、`f2s-kb-feat`、`f2s-kb-sync`、`f2s-kb-build` 等对 `stock-docs` / `req-docs` / `topics` 正文与业务向路由词条的维护。 | ||
| - 本技能跑通的是 **包版本下的目录、模板占位、路由结构对齐**;用户若说「把新能力写进知识库」,应引导 **`f2s-kb-sync` / `f2s-kb-add`** 等,而非仅 `f2s-kb-upgrade`。 | ||
| - 本技能跑通的是 **Template Version 下的目录、模板占位、路由结构对齐**;Core 代码版本与 Template Version 独立。用户若说「把新能力写进知识库」,应引导 **`f2s-kb-sync` / `f2s-kb-add`** 等,而非仅 `f2s-kb-upgrade`。 | ||
| - 本技能负责存量 `topicMetadata` 审计:`primary` / `tags` 仅用于治理、审计、盘点和阅读预期,不参与路由命中或执行强制性;执行强制性仍以 `AGENTS.md`、rules、skills 与 topic 正文为准。 | ||
@@ -21,3 +21,3 @@ | ||
| **字段位置**:`templates/{zh-CN,en-US}/knowledge/manifest-routing.json` 的根级整数字段 `projectRev`(起始 `1`)。 | ||
| **字段位置**:Core 包模板 `knowledge/manifest-routing.json` 的根级整数字段 `projectRev`(起始 `1`)。 | ||
@@ -32,11 +32,11 @@ **字段写入语义(必读)**: | ||
| **必须 bump 的修改**(每次发版至少 `+1`): | ||
| - 包模板 `templates/<locale>/knowledge/topics/<topic>.md` 任一文件的**正文**修改、新增、删除或改名; | ||
| - 包模板 `templates/<locale>/knowledge/matchers/<id>.json` 的 `includeAny` 词条、`id` 或新增 / 删除 matcher 文件; | ||
| - 包模板 `templates/<locale>/knowledge/manifest-routing.json` 的 `topicPaths` / `taskToTopicRules` / `topicDependencies` / `fallbackTopic` / `topicMetadata` 任一段修改; | ||
| - 包模板 `templates/<locale>/knowledge/index.md` 「主题一览」节或包级章节修改。 | ||
| - 包模板 `knowledge/topics/<topic>.md` 任一文件的**正文**修改、新增、删除或改名; | ||
| - 包模板 `knowledge/matchers/<id>.json` 的 `includeAny` 词条、`id` 或新增 / 删除 matcher 文件; | ||
| - 包模板 `knowledge/manifest-routing.json` 的 `topicPaths` / `taskToTopicRules` / `topicDependencies` / `fallbackTopic` / `topicMetadata` 任一段修改; | ||
| - 包模板 `knowledge/index.md` 「主题一览」节或包级章节修改。 | ||
| **不需要 bump 的修改**: | ||
| - 包源码(`lib/`、`cli.js`、`scripts/`)、`AGENTS.md`、`README*` 文档; | ||
| - `templates/<locale>/flow2spec.config.json` 默认值; | ||
| - `templates/<locale>/rules/*` / `templates/<locale>/skills/*` 仅规则与技能正文修改(这些与主题层无关,无需触发完整流程)。 | ||
| - `flow2spec.config.json` 默认值; | ||
| - `rules/*` / `skills/*` 仅规则与技能正文修改(这些与主题层无关,无需触发完整流程,但仍须显式提升 Template Version 以便 Hook 分发新版内容)。 | ||
@@ -50,3 +50,3 @@ **判定准则一句话**:模板里 `knowledge/` 目录下 topic / matcher / manifest / index 任一**主题层产物**变了 → 必 bump;否则不动。漏 bump 会让用户的 `f2s-kb-upgrade` 跑快速路径,错过包带来的主题变更。 | ||
| - **主必控**(主 agent 不可下放): | ||
| 1. **版本分流**:**V1** 先走 `f2s-kb-migrate` 再进入本技能;**现行库(V2+)** 直接进入 `init` 流程(含 Flow2Spec **npm v3.x** 等,只要已满足步骤 0 中「现行库」条件,均走此支,**勿**因主版本为 3 再单独设一套流程)。 | ||
| 1. **版本分流**:**V1(旧版布局)** 已不再支持自动迁移,命中即停止本技能并告知用户处理方式(见步骤 0);**现行库(V2+)** 直接进入 `init` 流程(含 Flow2Spec **npm v3.x** 等,只要已满足步骤 0 中「现行库」条件,均走此支,**勿**因主版本为 3 再单独设一套流程)。 | ||
| 2. **`init` 后重读**:从磁盘重读 `f2s-kb-upgrade/SKILL.md`,对比标识是否变化。 | ||
@@ -59,12 +59,18 @@ 3. **整技能重跑**:SKILL 有变化时,按新版字面从头再跑一轮,直至连续两轮无变化。 | ||
| ## 与 `f2s-kb-migrate` 为何并存 | ||
| ## 非主题版本更新:agent 可直接代跑 `init`(无需进入本技能) | ||
| | 技能 | 解决的问题 | | ||
| | --- | --- | | ||
| | **`f2s-kb-migrate`** | **结构搬家**:`docs-index.md` / `index-doc.md`、`rules/main.md(c)`、业务 `skills/`、散落 `stock-docs`/`req-docs` → **迁入 `.Knowledge`**,落盘 `migration-report.md`、删除清单需用户确认。不代跑 npm 包升级。 | | ||
| | **本技能 `f2s-kb-upgrade`** | **包与模板对齐**:代跑 **`flow2spec init`**,合并 **`manifest-routing.json`** 与 **`matchers/*.json`**,刷新各 agent **`rules`/`skills`**(或 Codex **`AGENTS.md`**);`init` 另将当前语言的 **`index.md` → `.Knowledge/template/index.template.md`** 作对照快照,**`.Knowledge/index.md`** 由步骤 3b **diff 对齐**,init **不**自动改其正文。 | | ||
| 版本检查提示 Core 更新但 `templateUpdateAvailable=false` 时,agent 可**直接替用户更新 Core 并执行** `flow2spec init <已初始化的 agents>`: | ||
| - **旧项目一键闭环**:**先 `f2s-kb-migrate`** → **再本技能**(`init`)。禁止仅用 `init` 代替完整迁移。 | ||
| - **已是新版 `.Knowledge` 的项目**:**只跑本技能**,勿重复 migrate。 | ||
| - `init` 后读项目侧 `.Knowledge/manifest-routing.json`:`projectRev` 与 `pkgRev` **相等**(非主题版本更新)→ 更新完成,删除 `.Knowledge/update-check.json`,**无需进入本技能**; | ||
| - 两者**不等**(包含主题层变更)→ 进入本技能完整流程(从步骤 2c 起判定,不重复 `init`)。 | ||
| - 该路径与本技能步骤 2c「快速路径」同一判定口径;用户显式要求「完整流程 / 覆盖重置」时仍按本技能全文执行。 | ||
| - `.Knowledge/manifest-routing.json.version` 表示 Template Version;禁止拿它与 Core Version 直接比较。 | ||
| ## 旧版布局(V1)不再内置迁移 | ||
| `f2s-kb-migrate` 已随包移除,旧版知识组织的自动迁移不再兼容。命中步骤 0 的 V1 信号时,本技能**停止执行**并告知用户两种处理方式: | ||
| 1. 使用仍内置 `f2s-kb-migrate` 的历史版本包(npm `@double-coding/flow2spec@3.4.x` 及更早)完成一次性迁移,再回到最新版本跑本技能; | ||
| 2. 手动将旧版规则、业务 skills、散落的 `stock-docs` / `req-docs` 迁入 `.Knowledge` 形态后,再跑本技能。 | ||
| **为何每个已配置客户端目录下都有一份同名 `SKILL.md`?** | ||
@@ -100,36 +106,21 @@ 各客户端只加载**自身配置根**下的 `skills/`。`flow2spec init` 会向所选 agent 目录**同步落盘**当前语言对应的技能内容。 | ||
| ### 步骤 -1:全局 flow2spec 版本预检(必须,先于一切,主 agent 前台探测) | ||
| ### 步骤 -1:CLI/Core/Template 版本预检(必须,先于一切) | ||
| **目的**:让「能用全局 `flow2spec` 就用全局」,只在**没装**或**版本过旧**时才动手升级;已装且已是 latest 时**完全跳过**升级动作,同时决定步骤 2 命令的**默认形态**(用 `flow2spec init` 还是 `npx @latest init`)。 | ||
| 主 agent 前台执行只读探测: | ||
| **动作**:主 agent 在进入步骤 0 **之前**,**顺序、前台**执行以下 3 条探测(都是纯查询,无副作用,秒级返回;无需拆子 agent): | ||
| ```bash | ||
| # 1. 探测本机全局是否装了 flow2spec | ||
| flow2spec --version 2>/dev/null || echo __F2S_NOT_INSTALLED__ | ||
| # 2. 查询 npm 上 latest 版本号(网络受限时可能失败,允许失败) | ||
| npm view @double-coding/flow2spec version 2>/dev/null || echo __F2S_NPM_UNREACHABLE__ | ||
| # 3. (备用)若第 1 步返回 __F2S_NOT_INSTALLED__,用来确认 npx 可用 | ||
| command -v npx >/dev/null 2>&1 && echo __NPX_OK__ || echo __NPX_MISSING__ | ||
| flow2spec version | ||
| flow2spec update --check | ||
| ``` | ||
| **判定 3 分支**(按结果选一条,写入本轮上下文并影响步骤 2 与步骤 5 摘要): | ||
| 按输出记录 CLI Version、Core Version、Core Range、Template Version、Protocol Version,以及 npm 最新 Core/Template: | ||
| | 情况 | 判定条件 | 行动 | 步骤 2 命令默认形态 | | ||
| | --- | --- | --- | --- | | ||
| | **A. 已装且是 latest** | 第 1 步返回版本号 `V`,第 2 步返回版本号 `L`,且 `V === L` | **完全跳过升级**,本轮不派子 agent、不跑 `npm i -g` | **`flow2spec init <agents...>`**(用全局) | | ||
| | **B. 已装但落后** | 第 1 步返回版本号 `V`,第 2 步返回版本号 `L`,且 `V !== L`(`V < L` 或 semver 不等) | **派独立子 agent 后台跑** `npm i -g @double-coding/flow2spec@latest`(fire-and-forget,不等待,不阻塞主流程);本轮步骤 2 仍用 `npx @latest` 保证本次拿到 latest 模板 | **`npx @double-coding/flow2spec@latest init <agents...>`** | | ||
| | **C. 未装 or 版本无法确认** | 第 1 步命中 `__F2S_NOT_INSTALLED__`,或第 2 步命中 `__F2S_NPM_UNREACHABLE__` 且第 1 步也未拿到版本号 | 若 A 情况「已装 latest」不成立且**未装**:派独立子 agent 后台跑 `npm i -g ...@latest`(同 B);若第 2 步失败但第 1 步已装某版本:视作 B 且无法比对 latest,**不派**升级、仅提示「latest 未知,保守用 npx」 | **`npx @double-coding/flow2spec@latest init <agents...>`** | | ||
| | 情况 | 行动 | 步骤 2 默认命令 | | ||
| | --- | --- | --- | | ||
| | **A. Template 已是最新** | 若只有 Core 更新,执行 `flow2spec update --core` 后幂等 init 刷新 Hook,删除缓存并**停止本技能**;Core 也最新则直接停止 | `flow2spec init <agents...>` | | ||
| | **B. Template 有更新且最新 Core 落在当前 Core Range** | 执行 `flow2spec update --core`,继续步骤 0;CLI 无需因 Core 兼容更新而发版或升级 | `flow2spec init <agents...>` | | ||
| | **C. 未安装、版本未知或最新 Core 超出范围** | 使用显式 latest CLI/Core 组合,避免 npx 复用旧 Core;若最新 Core 仍不兼容,先升级支持它的 CLI | `npx --yes --package <cli-package>@latest --package <core-package>@latest flow2spec init <agents...>` | | ||
| **编排(必须)**: | ||
| 预检失败时允许回退 C,但不得把 Core Version 当作 Template Version。此步骤不强制创建子 agent,也不后台安装全局包。 | ||
| - **A 分支**:主 agent 直接跳过所有升级动作,**不派**子 agent;本轮步骤 2 命令首选 `flow2spec init`。 | ||
| - **B / C 分支**:若确需升级(未装或版本落后),派**独立子 agent** fire-and-forget 执行 `npm i -g @double-coding/flow2spec@latest`,**不等待完成**、**不阻塞**主流程;成败均不进入 SKILL 结论。该派子**强制**执行,**不受** `flow2spec.config.json.subAgent` 字段约束(全局 npm 装包不属业务拆分范畴)。 | ||
| - **写权**:子 agent 仅执行该 shell,**不**触碰 `.Knowledge` / `manifest-routing.json` / `index.md` 等任何项目文件;写权硬约束不变。 | ||
| - **探测失败兜底**:若 3 条探测全部失败(无 shell 权限、极端受限环境),按 C 分支处理并用 `npx @latest`;此时也可以直接放弃步骤 -1、把升级留给 `cli.js` 的 `maybeAutoUpdateGlobalInstall()` 收尾兜底。 | ||
| **与 cli.js 的关系**: | ||
| - `cli.js` 内 `maybeAutoUpdateGlobalInstall()` 是 `init` 收尾兜底逻辑,**与本步不冲突**:本步在前台 init 之前完成探测/派工,cli 那段在 init 收尾时再兜一次;两次都成功就是 no-op,第一次失败第二次还能补救。 | ||
| ### 步骤 0:版本判定与分流(必须,先于 init) | ||
@@ -139,3 +130,3 @@ | ||
| **V1 — 旧版知识组织(须先迁移再 init)** | ||
| **V1 — 旧版知识组织(不再内置迁移)** | ||
| 命中**任一**强信号则按 V1: | ||
@@ -146,3 +137,3 @@ | ||
| **动作**:先按 **`f2s-kb-migrate`** 全流程执行(含 `migration-report`、删除清单确认),**再**进入步骤 1–5 执行 `flow2spec init`。 | ||
| **动作**:**停止本技能**,按上文「旧版布局(V1)不再内置迁移」告知用户两种处理方式(历史版本包一次性迁移 / 手动迁入 `.Knowledge`);**不得**在 V1 布局上继续步骤 1–5。 | ||
@@ -157,3 +148,3 @@ **现行库(V2+)— 已上 `.Knowledge` + 新版路由(仅包级 / 形态对齐)** | ||
| **动作**:直接进入步骤 1–5;**无需** migrate,除非用户明确要求重做迁移。 | ||
| **动作**:直接进入步骤 1–5。 | ||
@@ -172,6 +163,6 @@ ### 步骤 1:确认本技能内 `init` 模式(必须) | ||
| 1. **步骤 -1 判定为 A(已装且是 latest)**:直接用全局 CLI(**首选**): | ||
| 1. **步骤 -1 判定为 A/B(本地 CLI/Core 可用)**:直接使用当前 CLI: | ||
| - `flow2spec init <agents...>` | ||
| 2. **步骤 -1 判定为 B/C(未装 / 落后 / latest 未知)**:拉 npm latest 跑(**保证本次拿到最新模板**): | ||
| - `npx @double-coding/flow2spec@latest init <agents...>` | ||
| 2. **步骤 -1 判定为 C**:显式组合 latest CLI/Core,保证本次拿到最新 Core 与模板: | ||
| - `npx --yes --package <cli-package>@latest --package <core-package>@latest flow2spec init <agents...>` | ||
| 3. 覆盖重置时: | ||
@@ -185,3 +176,3 @@ - 在上述命令末尾追加 `--reset-knowledge` | ||
| > **辅助命令(用户可自查)**:`flow2spec --version` 看当前全局版本;`flow2spec update` 触发 CLI 内置的自更新。这两条**不**替代本 SKILL 的完整流程——它们只是「让全局 CLI 保鲜」,主题层对齐仍须走本 SKILL 步骤 2 及以后。 | ||
| > **辅助命令(用户可自查)**:`flow2spec version` 查看五维版本;`flow2spec update --check|--cli|--core` 分别检查、更新 CLI、更新兼容 Core。这些命令不替代 Template Version 变化后的本技能完整流程。 | ||
@@ -329,3 +320,3 @@ **步骤 2 完成后**:立刻执行上文 **「init 与技能自更新」**:重读 **`skills/f2s-kb-upgrade/SKILL.md`**;若有更新则**按新版字面从步骤 2c 起重跑**(**不再次 init**;避免用旧版 SKILL 做后续校验)。 | ||
| - **步骤 -1 全局版本预检**:`A 已装且是 latest(跳过升级) / B 已装但落后(已派子 agent 后台升级 npm i -g) / C 未装或 latest 未知(已派 / 保守用 npx)`;当前版本=`<V>`,latest=`<L 或 未知>` | ||
| - 本技能内代跑命令:`<实际执行的 flow2spec init ... 或 npx @latest init ...>` | ||
| - 本技能内代跑命令:`<实际执行的 flow2spec init ... 或显式 latest CLI/Core 组合 init ...>` | ||
| - init 模式:`增量` / `覆盖重置(--reset-knowledge)` | ||
@@ -361,4 +352,4 @@ - 执行结果:`成功` / `失败` | ||
| 1. 是否已做 **步骤 -1**:在进入步骤 0 前**已顺序前台执行 3 条探测**(`flow2spec --version` / `npm view ... version` / `npx` 可用性),并按 A/B/C 分支得出结论;仅在 B/C 时才**派独立子 agent**后台跑 `npm i -g @double-coding/flow2spec@latest`(不等待),A 分支**未派**任何升级动作;步骤 2 命令默认形态是否随分支选定(A→`flow2spec init`,B/C→`npx @latest init`);摘要中已写清分支与版本对比。 | ||
| 2. 是否已做 **步骤 0**:V1 未跳过 migrate、**现行库(V2+)** 未误跑 migrate。 | ||
| 1. 是否已做 **步骤 -1**:执行 `flow2spec version` 与 `flow2spec update --check`,记录 CLI/Core/Core Range/Template/Protocol;Core-only 更新是否直接刷新 Core/Hook 并停止,Template 更新是否按 A/B/C 选择当前 CLI 或显式 latest CLI/Core 组合。 | ||
| 2. 是否已做 **步骤 0**:V1 已停止执行并告知用户处理方式、**现行库(V2+)** 正常进入 `init` 流程。 | ||
| 3. 是否在 **步骤 2 开始前** 记录了项目侧 `projectRev`(`projectRev`),并在 **步骤 2 的 `init` 之后** 重读 `pkgRev`、执行 **步骤 2c** 判定。 | ||
@@ -365,0 +356,0 @@ 4. 是否在 **步骤 2 的 `init` 之后**重读过 **`f2s-kb-upgrade/SKILL.md`**:完整流程下有变化必须**按新版字面从步骤 2c 起重跑**(**不再次 init**);快速路径下可跳过该闭环(见「init 与技能自更新」「快速路径例外」)。 |
| --- | ||
| name: f2s-kb-migrate | ||
| description: Migrate a legacy knowledge base to `.Knowledge` in one pass: use the configuration-root `docs-index.md` plus the unified rule entry (legacy `rules/main.md(c)` or current package `rules/f2s-flow2spec-unified-entry.md(c)`) as primary index clues; fully process business `rules/` and business `skills/` (excluding `f2s-*` package skills), and fully migrate `stock-docs`/`req-docs`; **after migration acceptance, must write** `.Knowledge/migration-report.md` (migration mapping table + proposed deletion path list); **closing must delete** migrated legacy `rules/`, migrated business `skills/`, and legacy `docs-index.md`/`index-doc.md`; the user only **reviews/revises the deletion list (exclusions)**; triggers: f2s-kb-migrate、知识库迁移、旧版迁移、knowledge-base migration、legacy migration | ||
| --- | ||
| > Execution scope: this is an `f2s-*` skill workflow, not a CLI subcommand. Migration targets include: | ||
| > 1) Structure layer: `.Knowledge/topics`, `.Knowledge/index.md`, `.Knowledge/manifest-routing.json`, `.Knowledge/matchers/*.json` | ||
| > 2) Document layer: `.Knowledge/stock-docs`, `.Knowledge/req-docs` | ||
| > | ||
| > **Hard boundary**: `skills/f2s-*` (under each agent configuration root) are Flow2Spec package skills / execution-layer capabilities. They **must not** be written into `.Knowledge` (including `topics/stock-docs/req-docs`) and must not be used as sources for "business skill migration". They also **must not** be deleted in this workflow (version alignment is handled by `flow2spec init` / package upgrade). | ||
| > | ||
| > **Baseline rule keep-list (must not delete)**: `rules/f2s-flow2spec-unified-entry.md(c)`, `rules/f2s-implement-tech-design.md(c)`, `rules/f2s-stock-docs-vs-req-docs.md(c)`. | ||
| ## Orchestration (main / sub-agent) | ||
| - The meaning of `subAgent` / `switchAgentVerification` uses the unified entry as the only source of truth: **Cursor/Claude** read the configuration-root `rules/f2s-flow2spec-unified-entry.*`; **Codex** reads `.codex/topics/f2s-flow2spec-unified-entry.md` (same source, mirrored by `flow2spec init`). This section does not repeat those definitions. | ||
| - **Sub-agent responsibility** (only when `subAgent=true`): under the main agent's given inventory, perform moving work and generate **draft fragments** for `migration-report.md`; all outputs are submitted as patches and merged/written by the main agent. | ||
| - **Main agent must control**: | ||
| - `.Knowledge/.migrate-state.json` **write authority belongs to main** (state-machine source of truth; concurrent main/sub writes can misalign queues). | ||
| - The **"Deletion execution record"** section of `migration-report.md` is always appended by the main agent. | ||
| - **Deletion-list confirmation** and closed-loop cleanup must be completed by the main agent. | ||
| - **Write-authority hard rule**: `manifest-routing.json` / `.Knowledge/index.md` / `.Knowledge/.migrate-state.json` / the migration report "Deletion execution record" are always written by the main agent. | ||
| - By default, the writing side verifies its own work; this SKILL does not bind cross-agent verification. | ||
| # f2s-kb-migrate (Legacy Knowledge Base -> New Knowledge Base) | ||
| ## Why this coexists with `f2s-kb-upgrade` | ||
| | Skill | Problem solved | | ||
| | --- | --- | | ||
| | **This skill `f2s-kb-migrate`** | **One-time structural move**: legacy indexes (`docs-index.md` / `index-doc.md`), `rules/main.md(c)`, business `skills/`, scattered `stock-docs`/`req-docs` -> **`.Knowledge`**, plus deletion list and `migration-report.md`. | | ||
| | **`f2s-kb-upgrade`** | **Knowledge-base template upgrade skill (the only "upgrade" meaning)**: execute the full **`skills/f2s-kb-upgrade/SKILL.md`** workflow. It runs **`flow2spec init`** inside the process to align **`manifest-routing` + `matchers/`** and each agent's **`rules`/`skills`**. It includes **V1 / current repository (V2+)** branching (legacy projects must **migrate first, then run this skill**; **V2+ includes npm v3.x and other projects already using `.Knowledge`**, see `f2s-kb-upgrade` step 0). | | ||
| - **After migration acceptance and deletion-list confirmation are complete**: remind the user to execute, or execute for them, the **full `f2s-kb-upgrade` skill** (whose **step 2** runs **`flow2spec init`**) to align the Flow2Spec package version, routing shards, and configuration-root artifacts to the current package. **Do not** let the user think that running `init` alone completes knowledge-base template upgrade. | ||
| - **Projects already stably using `.Knowledge` with no legacy-index burden**: do not run this skill again; daily package/template alignment uses **`f2s-kb-upgrade`** only (not just `init`). | ||
| **Why does each agent directory have a same-named `SKILL.md`?** Each tool reads only its own configuration-root `skills/`; `flow2spec init` **syncs** the current-language skill content into the selected agent directories. | ||
| ## What This Command Does (External Wording) | ||
| Move the legacy "scattered configuration-root document index + rules + business skills + stock/req document trees" **as a whole into the new `.Knowledge`**, then perform **legacy entry and legacy business-artifact cleanup**, cutting over from the old knowledge-base organization. | ||
| Objects that must be covered: | ||
| 1. **Index entry**: business docs and rule clues declared/mapped in configuration-root `docs-index.md` (compatible with `index-doc.md`). | ||
| 2. **Rule entry**: the rule set declared/referenced in `rules/main.md` / `rules/main.mdc` (common legacy form) or `rules/f2s-flow2spec-unified-entry.md` / `rules/f2s-flow2spec-unified-entry.mdc` (compatible with historical `rules/flow2spec-unified-entry.md(c)`), plus other business rule files under `rules/`. | ||
| 3. **Business skills**: business skill directories under each agent configuration-root `skills/`, excluding `f2s-*` (full inventory). | ||
| 4. **Document trees**: legacy `stock-docs/`, `req-docs/` (or synonymous directories) fully migrated into the corresponding `.Knowledge` directories. | ||
| For objects **not covered by the index**: | ||
| - First output a candidate list (path + inferred reason: naming/directory/reference relationship). | ||
| - **By default, user confirmation is required** before including them in migration. Only when evidence is very strong (for example explicitly referenced by `rules/main` / `f2s-flow2spec-unified-entry`, or clearly referenced by an indexed document) may the Agent decide to include them, and the basis must be written in the migration summary. | ||
| Cleanup after migration (mandatory closing; only when migration has no failures and no pending confirmations; **`skills/f2s-*` are never deleted**): | ||
| - **Must execute**: delete migrated business rule files in legacy **`rules/`** (including `main.md(c)` if it is only a legacy entry), but **must not delete** the three `f2s-*` root rule files in the baseline keep-list. | ||
| - **Must execute**: delete migrated **business** subdirectories under legacy **`skills/`** (**excluding** `f2s-*`; if a directory still has unmigrated items, do not delete it until completed or removed from the list). | ||
| - **Must execute**: delete legacy entry **`docs-index.md`** (compatible with **`index-doc.md`**) to avoid dual entry points with `.Knowledge/index.md`. | ||
| - **Default optional deletion sub-list** (the user may exclude): legacy **`stock-docs/`** and **`req-docs/`** source directories, only when the corresponding document-layer migration has passed acceptance with no failures and no pending confirmations. | ||
| **Meaning of user confirmation (important)**: | ||
| - This is **not** asking "whether to clean up"; cleanup is part of the workflow. | ||
| - Instead, output the default-selected **"deletion path list"** (rule files one by one, business skill directories one by one, index filenames, and optional legacy document root directories) and ask the user to **review**. The user can only: | ||
| - Reply "**确认清单**" to delete according to the current list; or | ||
| - Reply "**排除:<路径…>**" to remove specified items from the list before deletion (removed items must be written to `.migrate-state.json` `notes[]` with reasons). | ||
| - If the user asks to **defer deleting a path**, keep that item in the list, end the cleanup round with `status=paused`, and **do not** pretend migration closure is complete. | ||
| ## Applicable Scenarios | ||
| - The project still uses legacy knowledge organization (`docs-index.md` / `index-doc.md` + `rules/main.md(c)` or `rules/f2s-flow2spec-unified-entry.md(c)` (compatible with old `flow2spec-unified-entry.md(c)`) + business `skills/` + scattered `stock-docs`/`req-docs`). | ||
| - The user wants to migrate to the new `.Knowledge` format and confirm topic by topic to avoid one-shot large changes. | ||
| - The user needs **all req-docs / stock-docs** migrated into `.Knowledge` and wants to cut over from legacy knowledge-base directories/wording (paths, index, topic text unified to the new architecture). | ||
| ## Input | ||
| - Optional inputs: | ||
| - Legacy unified rule entry path: `rules/main.md` / `rules/main.mdc` and/or `rules/f2s-flow2spec-unified-entry.md` / `rules/f2s-flow2spec-unified-entry.mdc` (compatible with old `rules/flow2spec-unified-entry.md(c)`) | ||
| - Legacy `index-doc.md` (or `docs-index.md`) path | ||
| - Legacy stock document directory (for example `stock-docs/`, `docs/stock/`) | ||
| - Legacy requirement document directory (for example `req-docs/`, `docs/req/`) | ||
| - Migration scope (all topics / specified topics) | ||
| - If not provided, locate the above files in the repository first and ask the user to confirm. | ||
| ## Resumable Migration State File (Required) | ||
| - State file path: `.Knowledge/.migrate-state.json` | ||
| - Purpose: record migration progress and support recovery after session interruption without migrating completed items again. | ||
| - Initialization timing: create immediately after the user confirms "start migration". | ||
| - Ending timing: | ||
| - All migration complete and user confirms completion: delete the state file. | ||
| - User actively says "stop": keep the state file for later recovery. | ||
| - `.migrate-state.json` is written only by the main agent; sub-agents submit patch fragments for the main agent to merge (write-authority hard rule). | ||
| Recommended fields (minimal set): | ||
| ```json | ||
| { | ||
| "version": "1", | ||
| "status": "running", | ||
| "currentStage": "inventory|orphans|topics|stock-docs|req-docs|cleanup", | ||
| "topicQueue": [], | ||
| "topicDone": [], | ||
| "bizRuleQueue": [], | ||
| "bizRuleDone": [], | ||
| "bizSkillQueue": [], | ||
| "bizSkillDone": [], | ||
| "stockQueue": [], | ||
| "stockDone": [], | ||
| "reqQueue": [], | ||
| "reqDone": [], | ||
| "pendingManual": [], | ||
| "failed": [], | ||
| "notes": [], | ||
| "updatedAt": "ISO-8601" | ||
| } | ||
| ``` | ||
| Update rules (required): | ||
| 1. After completing each topic, business skill directory, business rule file, or document file, immediately write the state-file update. | ||
| 2. When receiving "重试 <topic|file>", roll back that item's state before retrying. | ||
| 3. When receiving "继续", read the state file first and continue from unfinished queues. | ||
| 4. When receiving "停止", write `status=paused` and end this round. | ||
| 5. When receiving a resume request, first show a state summary (current stage, remaining counts, failed/pending items) and wait for user confirmation to continue. | ||
| ## Mandatory Flow (Phased Execution) | ||
| ### Step 1: Read Legacy Mappings | ||
| 1. Read `docs-index.md` (compatible with `index-doc.md`) and extract "business document -> rule/topic" mappings (**primary index**). | ||
| 2. Read **`rules/main.md` (compatible with `main.mdc`)** or **`rules/f2s-flow2spec-unified-entry.md` (compatible with `f2s-flow2spec-unified-entry.mdc`; compatible with old `flow2spec-unified-entry.md(c)`)** (usually only one exists), and extract module/topic directory clues (**cross-check with the index**). | ||
| 3. **Full inventory of business rule files**: scan files under `rules/` except the following, and build `bizRuleQueue` (deduped): | ||
| - Unified entry: `main.md(c)`, `f2s-flow2spec-unified-entry.md(c)`, `flow2spec-unified-entry.md(c)` (compatible with old name) | ||
| - Baseline keep: `f2s-implement-tech-design.md(c)`, `f2s-stock-docs-vs-req-docs.md(c)` | ||
| 4. **Full inventory of business skills**: scan each agent configuration-root `skills/` directory; **exclude** `f2s-*`; all other directories enter `bizSkillQueue` (deduped). | ||
| 5. Scan legacy `stock-docs` and `req-docs` candidate source directories if they exist. | ||
| 6. Generate the migration inventory and show it to the user for confirmation: | ||
| - Topic list (deduped, sorted) | ||
| - Business rule file list (`bizRuleQueue`) | ||
| - Business skill directory list (`bizSkillQueue`) | ||
| - `stock-docs` file list | ||
| - `req-docs` file list | ||
| 7. Document classification rules (must be explicit): | ||
| - Source path matches `stock-docs` (including synonyms such as `docs/stock`) -> migrate to `.Knowledge/stock-docs` | ||
| - Source path matches `req-docs` (including synonyms such as `docs/req`) -> migrate to `.Knowledge/req-docs` | ||
| - Unclassifiable files -> put into "manual confirmation list"; do not migrate before confirmation | ||
| 8. Compute "out-of-index candidates" (`orphans`): | ||
| - Files in `bizRuleQueue` not covered by `docs-index` / unified entry (`rules/main` or `f2s-flow2spec-unified-entry`) | ||
| - Directories in `bizSkillQueue` not covered by index mappings | ||
| - By default, require user confirmation for every item; only in high-confidence reference scenarios may the Agent include it autonomously, and the basis must be appended to state-file `notes[]` (without breaking JSON parseability). | ||
| 9. After the user confirms the inventory, initialize the state file and write queues (inventory/orphans/topics/stock/req). | ||
| ### Step 2: Migrate Topic by Topic (Structure-Layer Core) | ||
| For each topic, execute in this order: | ||
| 1. Collect legacy materials for the topic: | ||
| - Related `rules/*.md(c)` (business rules) | ||
| - Related **business** `skills/<non-f2s-*>` (merge their content into the topic narrative/workflow; do not copy them as skill files under `.Knowledge`) | ||
| - **Business document** paths in index mappings | ||
| - **Must not** include any file under `skills/f2s-*` | ||
| 2. Generate or update `.Knowledge/topics/<topic>.md`: | ||
| - Body text uses the new architecture vocabulary (`.Knowledge` layering, `manifest` routing, `stock-docs`/`req-docs` responsibilities). | ||
| - Remove legacy-only paths/terms (such as old `docs-index` root paths or scattered legacy directory names) and replace them with `.Knowledge/...` or stable paths relative to `.Knowledge`. | ||
| - **Authoring-side guideline**: if this step generates/rewrites a topic or adjusts `topicMetadata` / `topicDependencies`, first Read the full `rules/f2s-topic-authoring.*` (**Cursor/Claude**: `rules/f2s-topic-authoring.mdc`; **Codex**: `.codex/topics/f2s-topic-authoring.md`) before writing. | ||
| 3. Update the topic index row in `.Knowledge/index.md`, and maintain the "Associated documents (summary)" column (1-3 key `stock-docs/req-docs` **clickable Markdown links** per topic, format: `[title](relative path)`). | ||
| 4. Update the routing manifest as needed: | ||
| - `.Knowledge/manifest-routing.json`: `topicPaths`, `taskToTopicRules[]`, `topicDependencies`, `topicMetadata`, `fallbackTopic` | ||
| - `.Knowledge/matchers/<matcherId>.json`: `includeAny` (consistent with `manifest-routing.taskToTopicRules[].matcherPath`) | ||
| 5. Output this topic's migration summary and **pause**, prompting the user: | ||
| - Reply "继续" to migrate the next topic | ||
| - Or reply "停止" to stop this round | ||
| - Or reply "重试 <topic>" to redo the current topic | ||
| > Before receiving "继续", do not migrate the next topic. | ||
| > After completing each topic, update the state file before waiting. | ||
| ### Step 3: Migrate `stock-docs` (Document Layer) | ||
| After step 2 completes, execute: | ||
| 1. Migrate into `.Knowledge/stock-docs/<relative-path>` according to "source-directory relative path"; do not flatten. | ||
| 2. Default scenario is first migration from a legacy repo into the new knowledge base, so target paths are treated as "not existing". | ||
| 3. After each file migration, output a result and pause, waiting for "继续 / 停止 / 重试 <文件>". | ||
| 4. After all files complete, output a `stock-docs` sub-summary (success/failure/pending confirmation). | ||
| > Before receiving "继续", do not migrate the next file. | ||
| > After completing each file, update the state file before waiting. | ||
| ### Step 4: Migrate `req-docs` (Document Layer) | ||
| After the `stock-docs` phase completes, execute: | ||
| 1. Migrate into `.Knowledge/req-docs/<relative-path>` according to "source-directory relative path"; do not flatten. | ||
| 2. Default scenario is first migration from a legacy repo into the new knowledge base, so target paths are treated as "not existing". | ||
| 3. After each file migration, output a result and pause, waiting for "继续 / 停止 / 重试 <文件>". | ||
| 4. After all files complete, output a `req-docs` sub-summary (success/failure/pending confirmation). | ||
| > Before receiving "继续", do not migrate the next file. | ||
| > After completing each file, update the state file before waiting. | ||
| ### Step 5: Closing After All Migration Completes (Required: Migration Report + Deletion-List Confirmation) | ||
| When topic migration (step 2) and document-layer `stock-docs` / `req-docs` migration (steps 3-4) have **all passed acceptance** (no failures and no blocking pending confirmations, or pending items are separately listed in the report), execute the following substeps in order. | ||
| #### 5.0 Migration Report (Required: Write Project Markdown) | ||
| 1. **Must** create or overwrite this file in the project repository: **`.Knowledge/migration-report.md`** (relative to project root; same repo as `.Knowledge`, convenient for review and traceability). | ||
| 2. The report body must contain at least two major blocks (tables or nested lists are allowed; all paths use POSIX style relative to project root): | ||
| - **"Migration mapping table"**: | ||
| - **Topics**: each migrated `topic` -> legacy sources (corresponding `rules/*.md(c)`, business `skills/<dir>`, `docs-index` mapping-line summary) -> new path `.Knowledge/topics/<topic>.md`; also indicate whether `.Knowledge/index.md` / routing-manifest fields were modified. | ||
| - **`stock-docs`**: every **source path -> `.Knowledge/stock-docs/...` target path** (include skipped files and reasons; write "none" if none). | ||
| - **`req-docs`**: same as above. | ||
| - **"Proposed deletion path list"**: exactly consistent with the **default-selected deletion list** shown to the user in step 5.2 (each file under `rules/`, each business `skills/` directory to delete, `docs-index`/`index-doc`, and optionally legacy `stock-docs/`/`req-docs/` roots). Prefer `- [ ] <path>` for each item so humans can review/check. | ||
| 3. If the user later sends **"排除:<路径…>"** in step 5.2, update the same file **before physical deletion**: append or write in a "User exclusions" section the excluded paths and reasons, and sync the "Proposed deletion path list" checkbox state or list so the report on disk matches the final deletion set. | ||
| 4. After physical deletion is executed according to the final list in step 5.2 step 3, append a **`## Deletion Execution Record`** section at the **end of the same file** (include execution time and actual deleted paths; for undeleted items, state reason and `status=paused`, etc.). Do not leave this only in the conversation. | ||
| 5. The migration report "Deletion execution record" section is always appended by the main agent; sub-agents must not write it directly (write-authority hard rule). | ||
| > **Forbidden**: entering physical deletion or ending the migration closure before `.Knowledge/migration-report.md` is written. | ||
| #### 5.1 Overall Summary (In Conversation, May Match Report Summary) | ||
| - Migrated topic list | ||
| - New/updated `.Knowledge` files | ||
| - Migrated `stock-docs` files | ||
| - Migrated `req-docs` files | ||
| - Unmigrated or failed items | ||
| #### 5.2 Required Cleanup Phase (Deletion-List Confirmation; Must Not Skip) | ||
| 1. Output the default-selected **"deletion path list"** (same source as the "Proposed deletion path list" in `migration-report.md`), including at least: | ||
| - Every **business rule** file path under legacy **`rules/`** to delete (may include `main.md(c)`; **must not include** the baseline `f2s-*` root rules) | ||
| - Every subdirectory path under legacy **business** `skills/` to delete (**excluding** `f2s-*`) | ||
| - Legacy **`docs-index.md` / `index-doc.md`** | ||
| - (Optional sub-list) legacy **`stock-docs/`** and **`req-docs/`** root directories, only when document migration has passed acceptance and no pending items remain; the user may exclude them. | ||
| 2. Wait for the user to reply **"确认清单"** or **"排除:<路径…>"** to update the list. **Do not** ask a binary "whether to clean up" question. | ||
| 3. Delete according to the **final list**; **do not** delete paths outside the list; **do not** delete **`skills/f2s-*`**. | ||
| 4. After closing is complete, handle the state file: | ||
| - Fully completed round: delete `.Knowledge/.migrate-state.json` | ||
| - Paused/aborted round: keep `.Knowledge/.migrate-state.json` (`status=paused`) and record undeleted paths and reasons | ||
| ## Output Summary Format (Recommended) | ||
| ```markdown | ||
| ## Topic Migration Complete: <topic> | ||
| ### Sources | ||
| - rules: <legacy paths...> | ||
| - business docs: <document paths from index mappings...> | ||
| - mapping: <docs-index / index-doc row or document name> | ||
| ### Written | ||
| - .Knowledge/topics/<topic>.md | ||
| - .Knowledge/index.md (updated <x> rows) | ||
| - .Knowledge/manifest-routing.json (updated fields: ...) | ||
| - .Knowledge/matchers/<id>.json (updated `includeAny`, etc.: ...) | ||
| ### Next Step | ||
| - Reply "继续" to migrate the next topic | ||
| - Reply "停止" to stop migration | ||
| ``` | ||
| ```markdown | ||
| ## Document Migration Complete: <stock-docs|req-docs>/<file> | ||
| ### Source | ||
| - source: <legacy path...> | ||
| ### Written | ||
| - .Knowledge/<stock-docs|req-docs>/<relative-path> | ||
| ### Next Step | ||
| - Reply "继续" to migrate the next file | ||
| - Reply "停止" to stop migration | ||
| ``` | ||
| ## Constraints | ||
| - Must confirm topic by topic; do not skip confirmation and migrate everything in batch. | ||
| - `stock-docs` / `req-docs` must confirm file by file; do not batch-migrate without confirmation. | ||
| - Document migration must preserve source-directory relative paths; do not flatten to single-layer filenames. | ||
| - **`f2s-*` skills must not enter `.Knowledge` and must not be merged into topics during topic migration.** | ||
| - **Business** `skills/` (non-`f2s-*`) must be fully inventoried; out-of-index items require user confirmation by default before migration. | ||
| - Before all topics complete, do not delete legacy business `rules/` or old business `skills/` that are **non-`f2s-*`**; the baseline `f2s-*` root rule files are never deleted. | ||
| - Before document migration completes, do not delete legacy document directories. | ||
| - Before deleting legacy directories, complete **"deletion path list"** review (exclusions allowed); **do not** replace list confirmation with "whether to clean up". | ||
| - During migration, modify only `.Knowledge` and (after **final deletion-list** confirmation) deletion of old paths in the list; do not modify business code. | ||
| - Must maintain `.Knowledge/.migrate-state.json`; do not keep migration progress only in memory. | ||
| - After topic and document-layer migration acceptance, **first** write `.Knowledge/migration-report.md` (including migration mapping table and proposed deletion path list), then enter physical deletion; report and in-conversation deletion list must share a traceable source. | ||
| - `.migrate-state.json` / `migration-report.md` deletion execution record / `manifest-routing.json` / `.Knowledge/index.md` are always written by the main agent. | ||
| ## Migration Report Template (Recommended Structure for `migration-report.md`) | ||
| The following skeleton may be copied and filled; all paths are relative to project root. | ||
| ```markdown | ||
| # Knowledge-Base Migration Report | ||
| - **Generated at (ISO-8601)**: <...> | ||
| - **Configuration root (for example `.cursor/`)**: <...> | ||
| ## Migration Mapping Table | ||
| ### Topics (legacy sources -> new path) | ||
| | topic ID | legacy rules / legacy business skills / index clues | new path | | ||
| | --- | --- | --- | | ||
| | <id> | <...> | `.Knowledge/topics/<id>.md` | | ||
| ### stock-docs (source -> target) | ||
| | source path | target path | note | | ||
| | --- | --- | --- | | ||
| | <...> | `.Knowledge/stock-docs/...` | success / skip reason | | ||
| ### req-docs (source -> target) | ||
| | source path | target path | note | | ||
| | --- | --- | --- | | ||
| | <...> | `.Knowledge/req-docs/...` | success / skip reason | | ||
| ## Proposed Deletion Path List (default selected; same as in-conversation list) | ||
| - [ ] `<path>` (each file under `rules/`) | ||
| - [ ] `<path>` (business `skills/<dir>`, excluding `f2s-*`) | ||
| - [ ] `.cursor/docs-index.md` (or actual path) | ||
| - [ ] (optional) legacy `stock-docs/` / `req-docs/` root directories | ||
| ## User Exclusions (if any) | ||
| - (write "none" if none) | ||
| ## Failed or Unmigrated Items (if any) | ||
| - (write "none" if none) | ||
| ## Deletion Execution Record | ||
| (Append only after physical deletion: time, deleted list, undeleted items and reasons) | ||
| ``` | ||
| ## Completion Self-Check | ||
| 1. Topic count aligns with the legacy mapping count (unless the user explicitly skipped items). | ||
| 2. Every `manifest.topics[].path` exists. | ||
| 3. `index` can locate every migrated topic. | ||
| 4. `topicMetadata` only references topicIds that exist in `topicPaths`; `primary` / `tags` / `confidence` are valid. | ||
| 5. `.Knowledge/stock-docs` and `.Knowledge/req-docs` match the confirmed migration lists. | ||
| 6. Manual confirmation list is empty; if not, deleting legacy document directories is forbidden. | ||
| 7. Legacy business `rules/`, old business `skills/` that are **non-`f2s-*`**, legacy indexes, and legacy document directories (if listed) were deleted according to the **final deletion list**; the three `f2s-*` root rules in the baseline keep-list are still kept. | ||
| 8. Legacy entries `docs-index.md` / `index-doc.md` and `rules/main.md(c)` were deleted according to the list (and `.Knowledge` can replace their responsibilities), or explicitly kept due to user exclusion and written to `notes[]`. | ||
| 9. State file matches migration result (delete if complete; keep with `status=paused` if paused). | ||
| 10. `.Knowledge/index.md` has synced the "Associated documents (summary)" column for every topic (may write "none", but not blank). | ||
| 11. `skills/f2s-*` were not accidentally deleted and were not written into `.Knowledge`. | ||
| 12. `.Knowledge/migration-report.md` is written and contains the **migration mapping table** and **proposed deletion path list**; if deletion was executed, **`## Deletion Execution Record`** was appended and matches actual disk state. | ||
| 13. State-machine file and deletion execution record were not written by sub-agents without authority; manifest / index were written by the main agent only. |
| --- | ||
| name: f2s-kb-migrate | ||
| description: 旧版知识库一次性迁到 `.Knowledge`:以配置根 `docs-index.md` + 规则统一入口(旧版 `rules/main.md(c)` 或新版包 `rules/f2s-flow2spec-unified-entry.md(c)`)为主索引线索,全量处理业务 `rules/` 与业务 `skills/`(排除 `f2s-*` 包技能),并全量迁移 `stock-docs`/`req-docs`;**迁移验收后必选**落盘 `.Knowledge/migration-report.md`(迁移对照表 + 拟删除路径列表);**收尾必选**删除已迁旧的 `rules/`、已迁业务 `skills/`、旧版 `docs-index.md`/`index-doc.md`;用户只**核对/修订删除清单(排除项)**;触发:f2s-kb-migrate、知识库迁移、旧版迁移 | ||
| --- | ||
| > 执行口径:这是 `f2s-*` 技能流程,不是 CLI 子命令。迁移目标包含: | ||
| > 1) 结构层:`.Knowledge/topics`、`.Knowledge/index.md`、`.Knowledge/manifest-routing.json`、`.Knowledge/matchers/*.json` | ||
| > 2) 文档层:`.Knowledge/stock-docs`、`.Knowledge/req-docs` | ||
| > | ||
| > **硬边界**:`skills/f2s-*`(各 agent 配置根下)属于 Flow2Spec 包技能/执行层能力,**不得**写入 `.Knowledge`(含 `topics/stock-docs/req-docs`),也不得作为“业务技能迁移”的源;**不得**在本流程中删除(版本对齐走 `flow2spec init` / 包升级)。 | ||
| > | ||
| > **基线规则保留清单(不得删除)**:`rules/f2s-flow2spec-unified-entry.md(c)`、`rules/f2s-implement-tech-design.md(c)`、`rules/f2s-stock-docs-vs-req-docs.md(c)`。 | ||
| ## 编排(主 / 子 agent) | ||
| - 两字段(`subAgent` / `switchAgentVerification`)语义以统一入口为唯一事实源:**Cursor/Claude** 读配置根 `rules/f2s-flow2spec-unified-entry.*`;**Codex** 读 `.codex/topics/f2s-flow2spec-unified-entry.md`(与上同源,`flow2spec init` 镜像)。本节不复述。 | ||
| - **子 agent 职责**(仅当 `subAgent=true`):在主给定清单下做搬运工作、生成 `migration-report.md` 的**草案片段**;产出一律以 patch 形式提交,由主 agent 合并落盘。 | ||
| - **主必控**: | ||
| - `.Knowledge/.migrate-state.json` **写权归主**(状态机事实源,主 / 子抢写会致队列错位); | ||
| - `migration-report.md` 的 **「删除执行记录」** 小节恒由主 agent 追加; | ||
| - **删除清单确认**与闭环收尾必主完成。 | ||
| - **写权硬约束**:`manifest-routing.json` / `.Knowledge/index.md` / `.Knowledge/.migrate-state.json` / 迁移报告「删除执行记录」均恒由主 agent 落盘。 | ||
| - 默认落盘侧自验;本 SKILL 不绑定交叉校验。 | ||
| # f2s-kb-migrate(旧版知识库 -> 新版知识库) | ||
| ## 与 `f2s-kb-upgrade` 为何并存 | ||
| | 技能 | 解决的问题 | | ||
| | --- | --- | | ||
| | **本技能 `f2s-kb-migrate`** | **一次性结构搬家**:旧索引(`docs-index.md` / `index-doc.md`)、`rules/main.md(c)`、业务 `skills/`、散落 `stock-docs`/`req-docs` → **`.Knowledge`**,并处理删除清单与 `migration-report.md`。 | | ||
| | **`f2s-kb-upgrade`** | **知识库模板升级技能(唯一「升级」口径)**:按 **`skills/f2s-kb-upgrade/SKILL.md`** 全文执行;其中代跑 **`flow2spec init`** 以对齐 **`manifest-routing` + `matchers/`** 与各 agent **`rules`/`skills`**;含 **V1 / 现行库(V2+)** 分流(旧项目须 **migrate 后再跑本技能**;**V2+ 含 npm v3.x 等已上 `.Knowledge` 的项目**,见 `f2s-kb-upgrade` 步骤 0)。 | | ||
| - **迁移验收、删除清单确认完成后**:应提醒或代用户执行 **`f2s-kb-upgrade` 技能全文**(其中 **步骤 2** 会代跑 **`flow2spec init`**),把 Flow2Spec 包版本、路由分片与配置根产物对齐到当前包。**勿**让用户以为「单独执行 `init`」即完成知识库模板升级。 | ||
| - **已在稳定使用 `.Knowledge` 且无旧索引负担的项目**:不要重复跑本技能;日常包/模板对齐走 **`f2s-kb-upgrade`** 技能即可(不是只跑 `init`)。 | ||
| **为何各 agent 下都有同名 `SKILL.md`?** 各工具只读各自配置根下的 `skills/`;`flow2spec init` 会向所选 agent **同步**当前语言对应的技能内容。 | ||
| ## 本命令做什么(对外口径) | ||
| 把旧版“散落在配置根的文档索引 + 规则 + 业务技能 + stock/req 文档树”,**整体搬迁并改写到新版 `.Knowledge`**,完成后再做**旧版入口与旧版业务产物清理**,实现与旧版知识库组织方式的切割。 | ||
| 必须覆盖的对象: | ||
| 1. **索引入口**:配置根 `docs-index.md`(兼容 `index-doc.md`)中声明/映射到的业务文档与规则线索。 | ||
| 2. **规则入口**:`rules/main.md` / `rules/main.mdc`(旧版常见)或 `rules/f2s-flow2spec-unified-entry.md` / `rules/f2s-flow2spec-unified-entry.mdc`(兼容历史命名 `rules/flow2spec-unified-entry.md(c)`)中声明/引用的规则集合(以及 `rules/` 下其它业务规则文件)。 | ||
| 3. **业务技能**:各 agent 配置根 `skills/` 下除 `f2s-*` 以外的业务技能目录(全量盘点)。 | ||
| 4. **文档树**:旧版 `stock-docs/`、`req-docs/`(或同义目录)**全量**迁入 `.Knowledge` 对应目录。 | ||
| 对“索引未覆盖”的对象: | ||
| - 先输出候选清单(路径 + 推断理由:命名/目录/引用关系)。 | ||
| - **默认必须让用户确认**是否纳入迁移;仅当证据非常充分(例如被 `rules/main` / `f2s-flow2spec-unified-entry` 显式引用、或被已索引文档明确引用)才允许 Agent 自行判定纳入,并在迁移摘要中写明判定依据。 | ||
| 迁移完成后的清理(**必选收尾**;且迁移结果无失败、无待确认项;**`skills/f2s-*` 永不删除**): | ||
| - **必须执行**:删除旧版 **`rules/` 中已迁移业务规则文件**(含 `main.md(c)` 若仅作为旧入口),但**不得删除**基线规则保留清单中的 3 个 `f2s-*` 根规则文件。 | ||
| - **必须执行**:删除旧版 **业务** `skills/` 下**已迁移**的子目录(**排除** `f2s-*`;若某目录下仍有未迁完项则不得删该目录,须先补齐或从清单剔除)。 | ||
| - **必须执行**:删除旧版入口 **`docs-index.md`**(兼容 **`index-doc.md`**),避免与 `.Knowledge/index.md` 双入口并存。 | ||
| - **默认一并列入删除子清单**(用户可在清单中排除):旧版 **`stock-docs/`**、**`req-docs/`** 源目录(仅当对应文档层迁移验收通过、无失败/无待确认项时执行实际删除)。 | ||
| **用户确认的含义(重要)**: | ||
| - **不是**询问「要不要做清理」;清理是流程的一部分。 | ||
| - **而是**输出**默认全选的「删除路径清单」**(规则文件逐条、业务 skill 目录逐条、索引文件名、以及可选的旧文档根目录),请用户**核对**;用户只能: | ||
| - 回复「**确认清单**」按当前清单执行删除;或 | ||
| - 回复「**排除:<路径…>**」从清单中移除指定项后再执行(移除项须写入 `.migrate-state.json` 的 `notes[]` 并说明原因)。 | ||
| - 若用户要求**暂缓删除某路径**,须在清单中保留该项并结束本轮清理(状态文件 `status=paused`),**不得**假装已完成迁移闭环。 | ||
| ## 适用场景 | ||
| - 项目仍在使用旧版知识组织(`docs-index.md` / `index-doc.md` + `rules/main.md(c)` 或 `rules/f2s-flow2spec-unified-entry.md(c)`(兼容旧 `flow2spec-unified-entry.md(c)`)+ 业务 `skills/` + 散落 `stock-docs`/`req-docs`)。 | ||
| - 希望迁移到新版 `.Knowledge`,并且按主题逐个确认,避免一次性大改。 | ||
| - 需要 **req-docs / stock-docs 全量** 迁入 `.Knowledge`,并与旧版知识库目录/表述做切割(路径、索引、主题文案统一到新架构口径)。 | ||
| ## 输入 | ||
| - 可选输入: | ||
| - 旧版规则统一入口路径:`rules/main.md` / `rules/main.mdc` 和/或 `rules/f2s-flow2spec-unified-entry.md` / `rules/f2s-flow2spec-unified-entry.mdc`(兼容旧 `rules/flow2spec-unified-entry.md(c)`) | ||
| - 旧版 `index-doc.md`(或 `docs-index.md`)路径 | ||
| - 旧版存量文档目录(如 `stock-docs/`、`docs/stock/`) | ||
| - 旧版需求文档目录(如 `req-docs/`、`docs/req/`) | ||
| - 迁移范围(全部主题 / 指定主题) | ||
| - 不提供时,先在仓库中定位上述文件并向用户确认。 | ||
| ## 断点续迁状态文件(必须启用) | ||
| - 状态文件路径:`.Knowledge/.migrate-state.json` | ||
| - 作用:记录迁移进度,支持会话中断后恢复,不重复迁移已完成项。 | ||
| - 初始化时机:用户确认“开始迁移”后立即创建。 | ||
| - 结束时机: | ||
| - 全部迁移完成且用户确认结束:删除状态文件。 | ||
| - 用户主动“停止”:保留状态文件,等待下次恢复。 | ||
| - `.migrate-state.json` 只由主 agent 写;子 agent 以 patch 片段提交由主合并(写权硬约束)。 | ||
| 建议字段(最小集): | ||
| ```json | ||
| { | ||
| "version": "1", | ||
| "status": "running", | ||
| "currentStage": "inventory|orphans|topics|stock-docs|req-docs|cleanup", | ||
| "topicQueue": [], | ||
| "topicDone": [], | ||
| "bizRuleQueue": [], | ||
| "bizRuleDone": [], | ||
| "bizSkillQueue": [], | ||
| "bizSkillDone": [], | ||
| "stockQueue": [], | ||
| "stockDone": [], | ||
| "reqQueue": [], | ||
| "reqDone": [], | ||
| "pendingManual": [], | ||
| "failed": [], | ||
| "notes": [], | ||
| "updatedAt": "ISO-8601" | ||
| } | ||
| ``` | ||
| 更新规则(必须执行): | ||
| 1. 每完成 1 个主题、1 个业务技能目录、1 个业务规则文件或 1 个文档文件后,立即落盘更新状态文件。 | ||
| 2. 收到“重试 <topic|file>”时,先回滚该项状态,再执行重试。 | ||
| 3. 收到“继续”时,优先读取状态文件,从未完成队列继续。 | ||
| 4. 收到“停止”时,写入 `status=paused` 并结束本轮。 | ||
| 5. 收到恢复请求时,先展示状态摘要(当前阶段、剩余数量、失败/待确认项)并等待用户确认继续。 | ||
| ## 强制流程(分阶段执行) | ||
| ### 步骤 1:读取旧版映射 | ||
| 1. 读取 `docs-index.md`(兼容 `index-doc.md`),提取“业务文档 -> 规则/主题”映射(**主索引**)。 | ||
| 2. 读取 **`rules/main.md`(兼容 `main.mdc`)** 或 **`rules/f2s-flow2spec-unified-entry.md`(兼容 `f2s-flow2spec-unified-entry.mdc`;兼容旧 `flow2spec-unified-entry.md(c)`)**(二者通常只存在其一),提取模块/主题目录线索(**与索引交叉校验**)。 | ||
| 3. **全量盘点业务规则文件**:扫描 `rules/` 下除以下文件外的业务规则文件,建立 `bizRuleQueue`(去重): | ||
| - 统一入口:`main.md(c)`、`f2s-flow2spec-unified-entry.md(c)`、`flow2spec-unified-entry.md(c)`(兼容旧命名) | ||
| - 基线保留:`f2s-implement-tech-design.md(c)`、`f2s-stock-docs-vs-req-docs.md(c)` | ||
| 4. **全量盘点业务技能**:扫描各 agent 配置根 `skills/` 目录,**排除** `f2s-*`,其余目录一律进入 `bizSkillQueue`(去重)。 | ||
| 5. 扫描旧版 `stock-docs` 与 `req-docs` 候选来源目录(若存在)。 | ||
| 6. 生成待迁移清单并展示给用户确认: | ||
| - 主题清单(去重、排序) | ||
| - 业务规则文件清单(`bizRuleQueue`) | ||
| - 业务技能目录清单(`bizSkillQueue`) | ||
| - `stock-docs` 文件清单 | ||
| - `req-docs` 文件清单 | ||
| 7. 文档分类口径(必须明确): | ||
| - 来源路径命中 `stock-docs`(含同义目录如 `docs/stock`) -> 迁移到 `.Knowledge/stock-docs` | ||
| - 来源路径命中 `req-docs`(含同义目录如 `docs/req`) -> 迁移到 `.Knowledge/req-docs` | ||
| - 无法判定的文件 -> 列入“待人工确认清单”,未确认前不迁移 | ||
| 8. 计算“索引外候选”(`orphans`): | ||
| - `bizRuleQueue` 中未被 `docs-index` / 统一入口(`rules/main` 或 `f2s-flow2spec-unified-entry`)覆盖的文件 | ||
| - `bizSkillQueue` 中未被索引映射覆盖的目录 | ||
| - 对每一项默认要求用户确认是否迁移;仅在高置信引用场景允许 Agent 自判纳入,并将依据追加写入状态文件 `notes[]`(不得破坏 JSON 可解析性)。 | ||
| 9. 用户确认清单后,初始化状态文件并写入队列(inventory/orphans/topics/stock/req)。 | ||
| ### 步骤 2:逐主题迁移(结构层核心) | ||
| 对每个主题按以下顺序执行: | ||
| 1. 汇总该主题旧资料: | ||
| - 相关 `rules/*.md(c)`(业务规则) | ||
| - 相关 **业务** `skills/<非 f2s-*>`(将其内容合并进主题叙述/流程,不复制为 `.Knowledge` 下的技能文件) | ||
| - 索引映射中的**业务文档**路径 | ||
| - **不得**包含 `skills/f2s-*` 下任何文件 | ||
| 2. 生成或更新 `.Knowledge/topics/<topic>.md`: | ||
| - 正文表述统一为新架构口径(`.Knowledge` 分层、`manifest` 路由、`stock-docs`/`req-docs` 分工)。 | ||
| - 去除旧版独有路径/术语(如旧 `docs-index` 根路径、旧散落目录名),改为指向 `.Knowledge/...` 或相对 `.Knowledge` 的稳定路径。 | ||
| - **创作侧准则**:本步生成 / 重写 topic 或调整 `topicMetadata` / `topicDependencies`,须先 Read `rules/f2s-topic-authoring.*` 全文(**Cursor/Claude**:`rules/f2s-topic-authoring.mdc`;**Codex**:`.codex/topics/f2s-topic-authoring.md`),再落盘。 | ||
| 3. 更新 `.Knowledge/index.md` 的主题索引行,并同步维护“关联文档(摘要)”列(每主题 1-3 条关键 `stock-docs/req-docs` **可点击 Markdown 链接**,格式:`[标题](相对路径)`)。 | ||
| 4. 按需更新路由清单: | ||
| - `.Knowledge/manifest-routing.json`:`topicPaths`、`taskToTopicRules[]`、`topicDependencies`、`topicMetadata`、`fallbackTopic` | ||
| - `.Knowledge/matchers/<matcherId>.json`:`includeAny`(与 `manifest-routing.taskToTopicRules[].matcherPath` 一致) | ||
| 5. 输出本主题迁移摘要并**暂停**,提示用户: | ||
| - 回复“继续”迁移下一个主题 | ||
| - 或回复“停止”终止本轮 | ||
| - 或回复“重试 <topic>”重做当前主题 | ||
| > 未收到“继续”前,不得迁移下一个主题。 | ||
| > 每完成一个主题,必须先更新状态文件再进入等待。 | ||
| ### 步骤 3:迁移 `stock-docs`(文档层) | ||
| 当步骤 2 全部完成后,执行: | ||
| 1. 按“来源目录相对路径”迁移到 `.Knowledge/stock-docs/<relative-path>`,不做平铺。 | ||
| 2. 默认场景视为在旧版仓库首次迁移到新版知识库,目标路径按“不存在”执行。 | ||
| 3. 每迁移 1 个文件输出一次结果并暂停,等待“继续 / 停止 / 重试 <文件>”。 | ||
| 4. 全部完成后输出 `stock-docs` 子摘要(成功/失败/待确认)。 | ||
| > 未收到“继续”前,不得迁移下一个文件。 | ||
| > 每完成一个文件,必须先更新状态文件再进入等待。 | ||
| ### 步骤 4:迁移 `req-docs`(文档层) | ||
| 当 `stock-docs` 阶段完成后,执行: | ||
| 1. 按“来源目录相对路径”迁移到 `.Knowledge/req-docs/<relative-path>`,不做平铺。 | ||
| 2. 默认场景视为在旧版仓库首次迁移到新版知识库,目标路径按“不存在”执行。 | ||
| 3. 每迁移 1 个文件输出一次结果并暂停,等待“继续 / 停止 / 重试 <文件>”。 | ||
| 4. 全部完成后输出 `req-docs` 子摘要(成功/失败/待确认)。 | ||
| > 未收到“继续”前,不得迁移下一个文件。 | ||
| > 每完成一个文件,必须先更新状态文件再进入等待。 | ||
| ### 步骤 5:全部迁移完成后的收尾(必选:迁移报告落盘 + 删除清单确认) | ||
| 当主题(步骤 2)与文档层 `stock-docs` / `req-docs`(步骤 3–4)**全部验收通过**(无失败、无阻塞性待确认项,或已在报告中单列)后,按顺序执行以下子步骤。 | ||
| #### 5.0 迁移报告(必选:写入项目 Markdown) | ||
| 1. **必须**在项目仓库中创建或覆盖文件:**`.Knowledge/migration-report.md`**(相对项目根;与 `.Knowledge` 同库,便于评审与留痕)。 | ||
| 2. 报告正文须至少包含两大块(可用表格或分级列表,路径一律用**相对项目根**的 POSIX 风格): | ||
| - **「迁移对照表」**: | ||
| - **主题**:每个已迁移 `topic` → 旧侧来源(对应 `rules/*.md(c)`、业务 `skills/<dir>`、`docs-index` 映射行摘要)→ 新路径 `.Knowledge/topics/<topic>.md`;并注明本次是否改写了 `.Knowledge/index.md` / 路由清单相关字段。 | ||
| - **`stock-docs`**:每条 **源路径 → `.Knowledge/stock-docs/...` 目标路径**(含跳过的文件及原因,若无则写「无」)。 | ||
| - **`req-docs`**:同上。 | ||
| - **「拟删除路径清单」**:与下文步骤 5.2 中向用户展示的**默认全选删除清单**逐项一致(`rules/` 下每个文件、业务 `skills/` 下每个待删目录、`docs-index`/`index-doc`、以及可选列入的旧 `stock-docs/`/`req-docs/` 根目录);每条建议用 `- [ ] <路径>`,便于人类勾选核对。 | ||
| 3. 若用户随后在步骤 5.2 中发出 **「排除:<路径…>」**,须在**执行物理删除前**更新同一文件:追加或在「用户排除项」小节中写明排除路径与原因,并同步更新「拟删除路径清单」勾选状态或列表,使**磁盘上的报告与最终删除集合一致**。 | ||
| 4. 在步骤 5.2 第 3 步按最终清单**执行完物理删除后**,须在**同一文件末尾**追加小节 **`## 删除执行记录`**(含执行时间、实际已删路径列表;未删项注明原因与 `status=paused` 等),不得仅留在对话里。 | ||
| 5. 迁移报告的「删除执行记录」小节恒由主 agent 追加,子 agent 不得直接写入(写权硬约束)。 | ||
| > **禁止**:未完成 `.Knowledge/migration-report.md` 落盘即进入物理删除或结束本轮迁移闭环。 | ||
| #### 5.1 总摘要(对话内,可与报告摘要一致) | ||
| - 已迁移主题列表 | ||
| - 新增/更新的 `.Knowledge` 文件 | ||
| - 已迁移 `stock-docs` 文件 | ||
| - 已迁移 `req-docs` 文件 | ||
| - 未迁移或失败项 | ||
| #### 5.2 必选清理阶段(删除清单确认,不得跳过) | ||
| 1. 输出**默认全选**的「**删除路径清单**」(须与 `migration-report.md` 中「拟删除路径清单」同源),至少包含: | ||
| - 旧版 **`rules/`** 下每个将删除的**业务规则**文件路径(可含 `main.md(c)`;**不含**基线保留清单中的 `f2s-*` 根规则) | ||
| - 旧版 **业务** `skills/` 下每个将删除的子目录路径(**不含** `f2s-*`) | ||
| - 旧版 **`docs-index.md` / `index-doc.md`** | ||
| - (可选子清单)旧版 **`stock-docs/`**、**`req-docs/`** 根目录:仅当文档迁移验收通过且无待确认项时列入;用户可排除。 | ||
| 2. 等待用户回复 **「确认清单」** 或 **「排除:<路径…>」** 更新清单;**禁止**使用「是否执行清理」类二选一提问。 | ||
| 3. 按**最终清单**执行删除;**不得**删除清单外的路径;**不得**删除 **`skills/f2s-*`**。 | ||
| 4. 收尾完成后处理状态文件: | ||
| - 本轮完整完成:删除 `.Knowledge/.migrate-state.json` | ||
| - 本轮暂停/中止:保留 `.Knowledge/.migrate-state.json`(`status=paused`),并记录未删路径与原因 | ||
| ## 输出摘要格式(建议) | ||
| ```markdown | ||
| ## 主题迁移完成:<topic> | ||
| ### 来源 | ||
| - rules: <旧路径...> | ||
| - 业务文档: <索引映射中的文档路径...> | ||
| - 映射: <docs-index / index-doc 行或文档名> | ||
| ### 已写入 | ||
| - .Knowledge/topics/<topic>.md | ||
| - .Knowledge/index.md(更新 <x> 行) | ||
| - .Knowledge/manifest-routing.json(更新字段:...) | ||
| - .Knowledge/matchers/<id>.json(更新 `includeAny` 等:...) | ||
| ### 下一步 | ||
| - 回复“继续”迁移下一个主题 | ||
| - 回复“停止”结束迁移 | ||
| ``` | ||
| ```markdown | ||
| ## 文档迁移完成:<stock-docs|req-docs>/<file> | ||
| ### 来源 | ||
| - source: <旧路径...> | ||
| ### 已写入 | ||
| - .Knowledge/<stock-docs|req-docs>/<relative-path> | ||
| ### 下一步 | ||
| - 回复“继续”迁移下一个文件 | ||
| - 回复“停止”结束迁移 | ||
| ``` | ||
| ## 约束 | ||
| - 必须逐主题确认,不可批量跳过确认直接全量迁移。 | ||
| - `stock-docs` / `req-docs` 必须逐文件确认,不可无确认批量迁移。 | ||
| - 文档迁移必须保留来源目录相对路径,不可平铺为单层文件名。 | ||
| - **`f2s-*` 技能不得进入 `.Knowledge`,不得在主题迁移中合并进 `topics`。** | ||
| - **业务** `skills/`(非 `f2s-*`)必须纳入全量盘点;索引未覆盖项默认必须用户确认后才可迁移。 | ||
| - 未完成全部主题前,禁止删除旧业务 `rules/` 与**非 `f2s-*`** 的旧业务 `skills/`;基线保留清单中的 `f2s-*` 根规则文件始终不得删除。 | ||
| - 未完成文档迁移前,禁止删除旧文档目录。 | ||
| - 删除旧目录前必须完成「**删除路径清单**」核对(允许排除项),**禁止**用「是否清理」替代清单确认。 | ||
| - 迁移过程只改 `.Knowledge` 与(**最终删除清单**确认后)对清单内旧路径的删除,不改业务代码。 | ||
| - 必须维护 `.Knowledge/.migrate-state.json`,禁止只在内存中维护迁移进度。 | ||
| - 主题与文档层迁移验收通过后,**必须先**写入 `.Knowledge/migration-report.md`(含迁移对照表与拟删除路径清单),再进入物理删除;报告与对话内删除清单须同源可追溯。 | ||
| - `.migrate-state.json` / `migration-report.md` 的删除执行记录 / `manifest-routing.json` / `.Knowledge/index.md` 均恒主落盘。 | ||
| ## 迁移报告模板(落盘 `migration-report.md` 时建议结构) | ||
| 以下骨架可直接复制后填空;路径均为相对项目根。 | ||
| ```markdown | ||
| # 知识库迁移报告 | ||
| - **生成时间(ISO-8601)**:<...> | ||
| - **配置根(如 `.cursor/`)**:<...> | ||
| ## 迁移对照表 | ||
| ### 主题(旧来源 → 新路径) | ||
| | topic ID | 旧 rules / 旧业务 skills / 索引线索 | 新路径 | | ||
| | --- | --- | --- | | ||
| | <id> | <...> | `.Knowledge/topics/<id>.md` | | ||
| ### stock-docs(源 → 目标) | ||
| | 源路径 | 目标路径 | 备注 | | ||
| | --- | --- | --- | | ||
| | <...> | `.Knowledge/stock-docs/...` | 成功 / 跳过原因 | | ||
| ### req-docs(源 → 目标) | ||
| | 源路径 | 目标路径 | 备注 | | ||
| | --- | --- | --- | | ||
| | <...> | `.Knowledge/req-docs/...` | 成功 / 跳过原因 | | ||
| ## 拟删除路径清单(默认全选;与对话内清单一致) | ||
| - [ ] `<路径>`(`rules/` 下逐文件) | ||
| - [ ] `<路径>`(业务 `skills/<dir>`,不含 `f2s-*`) | ||
| - [ ] `.cursor/docs-index.md`(或实际路径) | ||
| - [ ] (可选)旧 `stock-docs/` / `req-docs/` 根目录 | ||
| ## 用户排除项(如有) | ||
| - (无则写「无」) | ||
| ## 失败或未迁移项(如有) | ||
| - (无则写「无」) | ||
| ## 删除执行记录 | ||
| (仅在执行物理删除后追加:时间、已删列表、未删及原因) | ||
| ``` | ||
| ## 完成后自检 | ||
| 1. 主题总数是否与旧映射总数对齐(允许用户显式跳过)。 | ||
| 2. `manifest.topics[].path` 是否都存在。 | ||
| 3. `index` 是否可定位到每个已迁移主题。 | ||
| 4. `topicMetadata` 是否只引用 `topicPaths` 已存在 topicId;`primary` / `tags` / `confidence` 是否合法。 | ||
| 5. `.Knowledge/stock-docs`、`.Knowledge/req-docs` 是否与确认迁移清单一致。 | ||
| 6. 待人工确认清单是否已清空;未清空则禁止删除旧文档目录。 | ||
| 7. 旧业务 `rules/`、**非 `f2s-*`** 的旧业务 `skills/`、旧版索引及(若列入清单)旧文档目录是否已按**最终删除清单**执行删除;基线保留清单中的 3 个 `f2s-*` 根规则是否仍保留。 | ||
| 8. 旧版入口 `docs-index.md` / `index-doc.md` 与 `rules/main.md(c)` 是否已按清单删除(且 `.Knowledge` 已可替代其职责),或是否因用户排除而**明确保留**并写入 `notes[]`。 | ||
| 9. 状态文件是否与迁移结果一致(完成则删除,暂停则保留且 `status=paused`)。 | ||
| 10. `.Knowledge/index.md` 是否已为每个主题同步“关联文档(摘要)”列(可写“无”,但不得留空)。 | ||
| 11. `skills/f2s-*` 是否未被误删、未被写入 `.Knowledge`。 | ||
| 12. `.Knowledge/migration-report.md` 是否已落盘且包含 **迁移对照表**、**拟删除路径清单**;若已执行删除,是否已追加 **「删除执行记录」** 并与实际磁盘状态一致。 | ||
| 13. 状态机文件与删除执行记录未被子 agent 越权写入;manifest / index 由主 agent 单点落盘。 |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
31
10.71%847267
-5.82%124
-1.59%6067
-0.52%