@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 init` to scaffold the .fabric/agents.meta.json file"
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 init` CLI.
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 init` to generate the AGENTS.md / CLAUDE.md bootstrap anchor at the repo root."
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 init` to regenerate .fabric/forensic.json."
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 init` to regenerate .fabric/forensic.json.");
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 init to write .mcp.json.`,
2261
- "Run `fab doctor --fix` to remove mcpServers.fabric from .claude/settings.json, then run `fab init` to write .mcp.json."
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-MMGHD42I.js";
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 init` to scaffold the .fabric/ knowledge layout (decisions, pitfalls, guidelines, models, processes).");
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-MMGHD42I.js";
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.11"
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-U5RL5Q6P.js");
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.11",
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.11"
16
+ "@fenglimg/fabric-shared": "2.0.0-rc.13"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@types/express": "^5.0.6",