@fenglimg/fabric-server 2.0.0-rc.11 → 2.0.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -88,7 +88,7 @@ import { agentsMetaNodeSchema, agentsMetaSchema as agentsMetaSchema2 } from "@fe
|
|
|
88
88
|
var AgentsMetaFileMissingError = class extends IOFabricError {
|
|
89
89
|
constructor(metaPath, opts) {
|
|
90
90
|
super(`Fabric agents metadata file is missing: ${metaPath}`, {
|
|
91
|
-
actionHint: opts?.actionHint ?? "Run `fab
|
|
91
|
+
actionHint: opts?.actionHint ?? "Run `fab install` to scaffold the .fabric/agents.meta.json file"
|
|
92
92
|
});
|
|
93
93
|
this.metaPath = metaPath;
|
|
94
94
|
}
|
|
@@ -1382,12 +1382,13 @@ async function runDoctorReport(target) {
|
|
|
1382
1382
|
const narrowTooFew = inspectNarrowTooFew(projectRoot, lintNow);
|
|
1383
1383
|
const sessionHintsStale = inspectSessionHintsStale(projectRoot, lintNow);
|
|
1384
1384
|
const relevanceFieldsMissing = inspectRelevanceFieldsMissing(projectRoot);
|
|
1385
|
+
const skillMdYamlInvalid = inspectSkillMdYamlInvalid(projectRoot);
|
|
1385
1386
|
const checks = [
|
|
1386
1387
|
createBootstrapAnchorCheck(bootstrapAnchor),
|
|
1387
1388
|
createKnowledgeDirMissingCheck(knowledgeDirMissing),
|
|
1388
1389
|
createForensicCheck(forensic, framework.kind, entryPoints.length),
|
|
1389
1390
|
// v2.0: removed `createInitContextCheck` — `.fabric/init-context.json`
|
|
1390
|
-
// is owned by the AI-side client init skill, not by `fabric
|
|
1391
|
+
// is owned by the AI-side client init skill, not by `fabric install` CLI.
|
|
1391
1392
|
// The file's absence is a legitimate post-init state when the skill has
|
|
1392
1393
|
// not yet run, so flagging it as a doctor manual_error misrepresents
|
|
1393
1394
|
// ownership.
|
|
@@ -1441,6 +1442,9 @@ async function runDoctorReport(target) {
|
|
|
1441
1442
|
// Info kind — applies to pending entries only; canonical entries get
|
|
1442
1443
|
// the fields written verbatim by fab_review.approve/modify.
|
|
1443
1444
|
createRelevanceFieldsMissingCheck(relevanceFieldsMissing),
|
|
1445
|
+
// rc.12 lint #29: skill_md_yaml_invalid. Warning kind — surfaces
|
|
1446
|
+
// SKILL.md frontmatter that Codex CLI silently drops at load.
|
|
1447
|
+
createSkillMdYamlInvalidCheck(skillMdYamlInvalid),
|
|
1444
1448
|
createPreexistingRootFilesCheck(preexistingRootFiles)
|
|
1445
1449
|
// v2.0 / rc.2: `createLegacyClientPathCheck` removed. The schema now
|
|
1446
1450
|
// rejects retired clientPaths keys (windsurf/rooCode/geminiCLI) at Zod
|
|
@@ -2128,7 +2132,7 @@ function createBootstrapAnchorCheck(inspection) {
|
|
|
2128
2132
|
"fixable_error",
|
|
2129
2133
|
"bootstrap_anchor_missing",
|
|
2130
2134
|
"Neither AGENTS.md nor CLAUDE.md exists at the repo root. Fabric requires a bootstrap anchor file at the project root.",
|
|
2131
|
-
"Run `fabric
|
|
2135
|
+
"Run `fabric install` to generate the AGENTS.md / CLAUDE.md bootstrap anchor at the repo root."
|
|
2132
2136
|
);
|
|
2133
2137
|
}
|
|
2134
2138
|
const present = [
|
|
@@ -2173,11 +2177,11 @@ function createForensicCheck(forensic, frameworkKind, entryPointCount) {
|
|
|
2173
2177
|
"manual_error",
|
|
2174
2178
|
"forensic_missing",
|
|
2175
2179
|
`${forensic.error ?? ".fabric/forensic.json is missing."} Live scan detects ${frameworkKind} with ${entryPointCount} entry point${entryPointCount === 1 ? "" : "s"}.`,
|
|
2176
|
-
"Run `fab
|
|
2180
|
+
"Run `fab install` to regenerate .fabric/forensic.json."
|
|
2177
2181
|
);
|
|
2178
2182
|
}
|
|
2179
2183
|
if (!forensic.valid) {
|
|
2180
|
-
return issueCheck("Scan evidence", "error", "manual_error", "forensic_invalid", forensic.error ?? ".fabric/forensic.json is invalid.", "Run `fab
|
|
2184
|
+
return issueCheck("Scan evidence", "error", "manual_error", "forensic_invalid", forensic.error ?? ".fabric/forensic.json is invalid.", "Run `fab install` to regenerate .fabric/forensic.json.");
|
|
2181
2185
|
}
|
|
2182
2186
|
return okCheck("Scan evidence", `.fabric/forensic.json is valid for ${forensic.report?.framework.kind ?? "unknown"}.`);
|
|
2183
2187
|
}
|
|
@@ -2257,8 +2261,8 @@ function createMcpConfigInWrongFileCheck(inspection) {
|
|
|
2257
2261
|
"error",
|
|
2258
2262
|
"fixable_error",
|
|
2259
2263
|
"mcp_config_in_wrong_file",
|
|
2260
|
-
`.claude/settings.json contains mcpServers.fabric \u2014 this file is for hooks/permissions only. Run --fix to remove it, then re-run fab
|
|
2261
|
-
"Run `fab doctor --fix` to remove mcpServers.fabric from .claude/settings.json, then run `fab
|
|
2264
|
+
`.claude/settings.json contains mcpServers.fabric \u2014 this file is for hooks/permissions only. Run --fix to remove it, then re-run fab install to write .mcp.json.`,
|
|
2265
|
+
"Run `fab doctor --fix` to remove mcpServers.fabric from .claude/settings.json, then run `fab install` to write .mcp.json."
|
|
2262
2266
|
);
|
|
2263
2267
|
}
|
|
2264
2268
|
return okCheck("Claude MCP config location", "mcpServers.fabric is not in .claude/settings.json.");
|
|
@@ -3642,6 +3646,93 @@ function createRelevanceFieldsMissingCheck(inspection) {
|
|
|
3642
3646
|
"Run `fab doctor --apply-lint` to back-fill the schema defaults (relevance_scope: broad, relevance_paths: [])."
|
|
3643
3647
|
);
|
|
3644
3648
|
}
|
|
3649
|
+
var SKILL_MD_FRONTMATTER_ROOTS = [".claude/skills", ".codex/skills"];
|
|
3650
|
+
var SKILL_FRONTMATTER_KEY_PATTERN = /^([A-Za-z_][A-Za-z0-9_-]*):[ \t]+(.+?)[ \t]*$/u;
|
|
3651
|
+
var SKILL_QUOTED_VALUE_LEADS = /* @__PURE__ */ new Set(['"', "'", "[", "{", ">", "|"]);
|
|
3652
|
+
function inspectSkillMdYamlInvalid(projectRoot) {
|
|
3653
|
+
const candidates = [];
|
|
3654
|
+
for (const rootRel of SKILL_MD_FRONTMATTER_ROOTS) {
|
|
3655
|
+
const rootAbs = join5(projectRoot, rootRel);
|
|
3656
|
+
if (!existsSync4(rootAbs)) continue;
|
|
3657
|
+
let dirEntries;
|
|
3658
|
+
try {
|
|
3659
|
+
dirEntries = readdirSync(rootAbs, { withFileTypes: true });
|
|
3660
|
+
} catch {
|
|
3661
|
+
continue;
|
|
3662
|
+
}
|
|
3663
|
+
for (const dirEntry of dirEntries) {
|
|
3664
|
+
if (!dirEntry.isDirectory()) continue;
|
|
3665
|
+
const skillFile = join5(rootAbs, dirEntry.name, "SKILL.md");
|
|
3666
|
+
if (!existsSync4(skillFile)) continue;
|
|
3667
|
+
let raw;
|
|
3668
|
+
try {
|
|
3669
|
+
raw = readFileSync(skillFile, "utf8");
|
|
3670
|
+
} catch {
|
|
3671
|
+
continue;
|
|
3672
|
+
}
|
|
3673
|
+
const frontmatter = extractSkillFrontmatterLines(raw);
|
|
3674
|
+
if (frontmatter === null) continue;
|
|
3675
|
+
for (const { line, lineNumber } of frontmatter) {
|
|
3676
|
+
const match = SKILL_FRONTMATTER_KEY_PATTERN.exec(line);
|
|
3677
|
+
if (!match) continue;
|
|
3678
|
+
const [, key, value] = match;
|
|
3679
|
+
if (value.length === 0) continue;
|
|
3680
|
+
if (SKILL_QUOTED_VALUE_LEADS.has(value[0])) continue;
|
|
3681
|
+
const colonSpaceIdx = value.indexOf(": ");
|
|
3682
|
+
const trailingColon = value.endsWith(":");
|
|
3683
|
+
if (colonSpaceIdx < 0 && !trailingColon) continue;
|
|
3684
|
+
const anchor = colonSpaceIdx >= 0 ? colonSpaceIdx : value.length - 1;
|
|
3685
|
+
const previewStart = Math.max(0, anchor - 25);
|
|
3686
|
+
const previewEnd = Math.min(value.length, anchor + 30);
|
|
3687
|
+
const preview = `${previewStart > 0 ? "\u2026" : ""}${value.slice(previewStart, previewEnd)}${previewEnd < value.length ? "\u2026" : ""}`;
|
|
3688
|
+
candidates.push({
|
|
3689
|
+
path: posix.join(rootRel, dirEntry.name, "SKILL.md"),
|
|
3690
|
+
line: lineNumber,
|
|
3691
|
+
key,
|
|
3692
|
+
preview
|
|
3693
|
+
});
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
candidates.sort((a, b) => {
|
|
3698
|
+
const byPath = a.path.localeCompare(b.path);
|
|
3699
|
+
return byPath !== 0 ? byPath : a.line - b.line;
|
|
3700
|
+
});
|
|
3701
|
+
return { candidates };
|
|
3702
|
+
}
|
|
3703
|
+
function extractSkillFrontmatterLines(raw) {
|
|
3704
|
+
const rawLines = raw.split(/\r?\n/u);
|
|
3705
|
+
if (rawLines.length < 2) return null;
|
|
3706
|
+
if (rawLines[0]?.trim() !== "---") return null;
|
|
3707
|
+
const out = [];
|
|
3708
|
+
for (let i = 1; i < rawLines.length; i++) {
|
|
3709
|
+
const line = rawLines[i];
|
|
3710
|
+
if (line.trim() === "---") {
|
|
3711
|
+
return out;
|
|
3712
|
+
}
|
|
3713
|
+
out.push({ line, lineNumber: i + 1 });
|
|
3714
|
+
}
|
|
3715
|
+
return null;
|
|
3716
|
+
}
|
|
3717
|
+
function createSkillMdYamlInvalidCheck(inspection) {
|
|
3718
|
+
if (inspection.candidates.length === 0) {
|
|
3719
|
+
return okCheck(
|
|
3720
|
+
"Skill markdown YAML",
|
|
3721
|
+
"All .claude/.codex SKILL.md frontmatter values parse as strict YAML."
|
|
3722
|
+
);
|
|
3723
|
+
}
|
|
3724
|
+
const first = inspection.candidates[0];
|
|
3725
|
+
const detail = `${first.path}:${first.line} (key \`${first.key}\` value contains an unquoted ': ' \u2014 preview: \`${first.preview}\`)`;
|
|
3726
|
+
const plural = inspection.candidates.length === 1;
|
|
3727
|
+
return issueCheck(
|
|
3728
|
+
"Skill markdown YAML",
|
|
3729
|
+
"warn",
|
|
3730
|
+
"warning",
|
|
3731
|
+
"skill_md_yaml_invalid",
|
|
3732
|
+
`${inspection.candidates.length} SKILL.md frontmatter ${plural ? "value contains" : "values contain"} an unquoted ': ' that strict YAML parsers reject (Claude Code tolerates it; Codex CLI drops the skill at load). First: ${detail}.`,
|
|
3733
|
+
'Quote the value with double quotes (`description: "\u2026"`) or rewrite the inner `key: value` token to `key=value`.'
|
|
3734
|
+
);
|
|
3735
|
+
}
|
|
3645
3736
|
function createNarrowTooFewCheck(inspection) {
|
|
3646
3737
|
const { structural_flagged, telemetry_flagged } = inspection;
|
|
3647
3738
|
if (!structural_flagged && !telemetry_flagged) {
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
readEventLedger,
|
|
15
15
|
runDoctorReport,
|
|
16
16
|
sha256
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-3ZBYYPXQ.js";
|
|
18
18
|
|
|
19
19
|
// src/http.ts
|
|
20
20
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -942,7 +942,7 @@ function toPosixPath(path) {
|
|
|
942
942
|
function buildRecommendations(input) {
|
|
943
943
|
const recommendations = [];
|
|
944
944
|
if (!input.hasExistingFabric) {
|
|
945
|
-
recommendations.push("L0: Run `fab
|
|
945
|
+
recommendations.push("L0: Run `fab install` to scaffold the .fabric/ knowledge layout (decisions, pitfalls, guidelines, models, processes).");
|
|
946
946
|
}
|
|
947
947
|
if (input.readmeQuality === "stub") {
|
|
948
948
|
recommendations.push("L0: Expand README.md before promoting project facts into Fabric knowledge entries.");
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
sha256,
|
|
27
27
|
stableStringify,
|
|
28
28
|
writeKnowledgeMeta
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-3ZBYYPXQ.js";
|
|
30
30
|
|
|
31
31
|
// src/index.ts
|
|
32
32
|
import { existsSync as existsSync4 } from "fs";
|
|
@@ -1890,7 +1890,7 @@ function formatPreexistingRootMessage(projectRoot) {
|
|
|
1890
1890
|
function createFabricServer(tracker) {
|
|
1891
1891
|
const server = new McpServer({
|
|
1892
1892
|
name: "fabric-knowledge-server",
|
|
1893
|
-
version: "2.0.0-rc.
|
|
1893
|
+
version: "2.0.0-rc.13"
|
|
1894
1894
|
});
|
|
1895
1895
|
registerPlanContext(server, tracker);
|
|
1896
1896
|
registerKnowledgeSections(server, tracker);
|
|
@@ -1990,7 +1990,7 @@ function createShutdownHandler(deps) {
|
|
|
1990
1990
|
};
|
|
1991
1991
|
}
|
|
1992
1992
|
async function startHttpServer(options) {
|
|
1993
|
-
const { createFabricHttpApp } = await import("./http-
|
|
1993
|
+
const { createFabricHttpApp } = await import("./http-B3UKRP5Y.js");
|
|
1994
1994
|
const { port, projectRoot, host = "127.0.0.1", authToken } = options;
|
|
1995
1995
|
const app = createFabricHttpApp({ projectRoot, host, authToken });
|
|
1996
1996
|
return await new Promise((resolveServer, rejectServer) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fenglimg/fabric-server",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"express": "^5.2.1",
|
|
14
14
|
"minimatch": "^10.0.1",
|
|
15
15
|
"zod": "^3.25.0",
|
|
16
|
-
"@fenglimg/fabric-shared": "2.0.0-rc.
|
|
16
|
+
"@fenglimg/fabric-shared": "2.0.0-rc.13"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/express": "^5.0.6",
|