@fenglimg/fabric-cli 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.
- package/README.md +3 -3
- package/dist/{chunk-5MQ52F42.js → chunk-FDRLV5PL.js} +13 -8
- package/dist/{chunk-HQLEHH4O.js → chunk-OHWQNSLH.js} +1 -1
- package/dist/{chunk-WPTA74BY.js → chunk-Q72D24BG.js} +6 -4
- package/dist/{chunk-AW3G7ZH5.js → chunk-X7QPY5KH.js} +68 -41
- package/dist/{hooks-NX32PPEN.js → hooks-HIWYI3VG.js} +2 -2
- package/dist/index.js +6 -6
- package/dist/{init-C56PWHID.js → install-SLS5W27W.js} +115 -109
- package/dist/{scan-66EKMNAY.js → scan-VHKZPT2W.js} +1 -1
- package/dist/{uninstall-DBAR2JBS.js → uninstall-JHUSFENL.js} +27 -18
- package/package.json +3 -3
- package/templates/hooks/configs/README.md +1 -1
- package/templates/hooks/knowledge-hint-broad.cjs +28 -107
- package/templates/skills/fabric-archive/SKILL.md +15 -15
- package/templates/skills/fabric-import/SKILL.md +26 -26
- package/templates/skills/fabric-review/SKILL.md +19 -19
package/README.md
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
1. 在 monorepo 根目录运行 `pnpm install`。
|
|
8
8
|
2. 用 `pnpm --filter @fenglimg/fabric-cli build` 构建 CLI。
|
|
9
|
-
3. 在目标项目运行 `fabric
|
|
9
|
+
3. 在目标项目运行 `fabric install`,完成一站式安装。
|
|
10
10
|
4. 启动 `fabric serve`,再去客户端里验证 `fab_plan_context` 和 `fab_get_knowledge_sections`。
|
|
11
11
|
|
|
12
|
-
`fabric
|
|
12
|
+
`fabric install` 会自动准备 bootstrap、MCP 配置和 git hooks。公共命令面只保留 `install`、`scan`、`doctor`、`serve`。
|
|
13
13
|
|
|
14
14
|
## 常用命令
|
|
15
15
|
|
|
16
|
-
- `fabric
|
|
16
|
+
- `fabric install`
|
|
17
17
|
- `fabric scan`
|
|
18
18
|
- `fabric doctor`
|
|
19
19
|
- `fabric doctor --json`
|
|
@@ -88,8 +88,8 @@ async function runInitScan(targetInput, options = {}) {
|
|
|
88
88
|
const nowIso = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
89
89
|
const tags = deriveTagsFromForensic(forensic);
|
|
90
90
|
const fabricConfig = readFabricConfig(target);
|
|
91
|
-
const
|
|
92
|
-
const resolvedLanguage =
|
|
91
|
+
const fabricLanguage = fabricConfig.fabric_language ?? "match-existing";
|
|
92
|
+
const resolvedLanguage = resolveFabricLanguage(fabricLanguage, target);
|
|
93
93
|
const candidates = [
|
|
94
94
|
buildTechStackEntry(forensic, nowIso, tags, resolvedLanguage),
|
|
95
95
|
buildModuleStructureEntry(forensic, nowIso, tags, resolvedLanguage),
|
|
@@ -186,7 +186,7 @@ var scanCommand = defineCommand({
|
|
|
186
186
|
}
|
|
187
187
|
});
|
|
188
188
|
var scan_default = scanCommand;
|
|
189
|
-
var
|
|
189
|
+
var STRICT_BASELINE_TEMPLATES = {
|
|
190
190
|
en: {
|
|
191
191
|
"tech-stack": {
|
|
192
192
|
title: ({ frameworkSummary }) => `Tech stack: ${frameworkSummary}`,
|
|
@@ -336,11 +336,16 @@ var BASELINE_TEMPLATES = {
|
|
|
336
336
|
}
|
|
337
337
|
}
|
|
338
338
|
};
|
|
339
|
+
var BASELINE_TEMPLATES = {
|
|
340
|
+
en: STRICT_BASELINE_TEMPLATES.en,
|
|
341
|
+
"zh-CN": STRICT_BASELINE_TEMPLATES["zh-CN"],
|
|
342
|
+
"zh-CN-hybrid": STRICT_BASELINE_TEMPLATES["zh-CN"]
|
|
343
|
+
};
|
|
339
344
|
function resolveTemplateTitle(template, inputs) {
|
|
340
345
|
return typeof template.title === "function" ? template.title(inputs) : template.title;
|
|
341
346
|
}
|
|
342
|
-
function
|
|
343
|
-
if (configured === "en" || configured === "zh-CN") {
|
|
347
|
+
function resolveFabricLanguage(configured, target) {
|
|
348
|
+
if (configured === "en" || configured === "zh-CN" || configured === "zh-CN-hybrid") {
|
|
344
349
|
return configured;
|
|
345
350
|
}
|
|
346
351
|
return detectExistingLanguage(target);
|
|
@@ -392,7 +397,7 @@ function detectExistingLanguage(target) {
|
|
|
392
397
|
return "en";
|
|
393
398
|
}
|
|
394
399
|
const ratio = cjkCount / denominator;
|
|
395
|
-
return ratio > ZH_CN_RATIO_THRESHOLD ? "zh-CN" : "en";
|
|
400
|
+
return ratio > ZH_CN_RATIO_THRESHOLD ? "zh-CN-hybrid" : "en";
|
|
396
401
|
}
|
|
397
402
|
function buildTechStackEntry(forensic, nowIso, tags, language = "en") {
|
|
398
403
|
const framework = forensic.framework;
|
|
@@ -982,9 +987,9 @@ var __testing__ = {
|
|
|
982
987
|
isBuildConfigPath,
|
|
983
988
|
extractFirstParagraph,
|
|
984
989
|
extractExplicitDescription,
|
|
985
|
-
// TASK-008: bilingual template registry + language detection
|
|
990
|
+
// TASK-008 / rc.12: bilingual template registry + language detection
|
|
986
991
|
detectExistingLanguage,
|
|
987
|
-
|
|
992
|
+
resolveFabricLanguage,
|
|
988
993
|
BASELINE_TEMPLATES
|
|
989
994
|
};
|
|
990
995
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
addFabricKnowledgeBaseSection,
|
|
4
4
|
installArchiveHintHook,
|
|
5
5
|
installFabricArchiveSkill,
|
|
6
6
|
installFabricImportSkill,
|
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
installKnowledgeHintNarrowHook,
|
|
10
10
|
mergeClaudeCodeHookConfig,
|
|
11
11
|
mergeCodexHookConfig,
|
|
12
|
-
mergeCursorHookConfig
|
|
13
|
-
|
|
12
|
+
mergeCursorHookConfig,
|
|
13
|
+
readFabricLanguagePreference
|
|
14
|
+
} from "./chunk-X7QPY5KH.js";
|
|
14
15
|
import {
|
|
15
16
|
t
|
|
16
17
|
} from "./chunk-6ICJICVU.js";
|
|
@@ -66,7 +67,8 @@ async function installHooks(target, _options = {}) {
|
|
|
66
67
|
results.push(await runSingleStep("claude-hook-config", () => mergeClaudeCodeHookConfig(normalizedTarget)));
|
|
67
68
|
results.push(await runSingleStep("codex-hook-config", () => mergeCodexHookConfig(normalizedTarget)));
|
|
68
69
|
results.push(await runSingleStep("cursor-hook-config", () => mergeCursorHookConfig(normalizedTarget)));
|
|
69
|
-
|
|
70
|
+
const fabricLanguage = readFabricLanguagePreference(normalizedTarget);
|
|
71
|
+
results.push(...await runStep(() => addFabricKnowledgeBaseSection(normalizedTarget, fabricLanguage)));
|
|
70
72
|
results.push(...validateHookPaths(normalizedTarget));
|
|
71
73
|
return summarizeResults(results);
|
|
72
74
|
}
|
|
@@ -307,10 +307,41 @@ var FABRIC_HOOK_COMMAND_PATHS = {
|
|
|
307
307
|
knowledgeHintNarrow: ".cursor/hooks/knowledge-hint-narrow.cjs"
|
|
308
308
|
}
|
|
309
309
|
};
|
|
310
|
-
var
|
|
311
|
-
var
|
|
312
|
-
var
|
|
313
|
-
var
|
|
310
|
+
var SECTION_TARGETS = ["CLAUDE.md", "AGENTS.md", join2(".cursor", "rules")];
|
|
311
|
+
var FABRIC_SECTION_BEGIN_MARKER = "<!-- fabric:knowledge-base:begin -->";
|
|
312
|
+
var FABRIC_SECTION_END_MARKER = "<!-- fabric:knowledge-base:end -->";
|
|
313
|
+
var FABRIC_SECTION_REGEX = /(?:\r?\n){0,2}<!-- fabric:knowledge-base:begin -->[\s\S]*?<!-- fabric:knowledge-base:end -->/;
|
|
314
|
+
function readFabricLanguagePreference(projectRoot) {
|
|
315
|
+
const configPath = join2(projectRoot, ".fabric", "fabric-config.json");
|
|
316
|
+
if (!existsSync2(configPath)) {
|
|
317
|
+
return "match-existing";
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
const raw = readFileSync(configPath, "utf8");
|
|
321
|
+
const parsed = JSON.parse(raw);
|
|
322
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
323
|
+
return "match-existing";
|
|
324
|
+
}
|
|
325
|
+
const value = parsed["fabric_language"];
|
|
326
|
+
return typeof value === "string" && value.length > 0 ? value : "match-existing";
|
|
327
|
+
} catch {
|
|
328
|
+
return "match-existing";
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function buildFabricKnowledgeBaseSection(fabricLanguage) {
|
|
332
|
+
return `${FABRIC_SECTION_BEGIN_MARKER}
|
|
333
|
+
|
|
334
|
+
## Fabric Knowledge Base
|
|
335
|
+
|
|
336
|
+
This project uses Fabric for persistent project knowledge under \`.fabric/knowledge/\`.
|
|
337
|
+
|
|
338
|
+
- **Discovery**: SessionStart lists available entries (broad menu); editing files may surface narrow hints
|
|
339
|
+
- **Usage**: call \`fab_get_knowledge_sections\` to fetch full content of any entry by id
|
|
340
|
+
- **Write flows**: see fabric-archive (record), fabric-review (validate), fabric-import (backfill) Skills
|
|
341
|
+
- **Language**: rendered per \`fabric_language\` in \`.fabric/fabric-config.json\` (current: \`${fabricLanguage}\`)
|
|
342
|
+
|
|
343
|
+
${FABRIC_SECTION_END_MARKER}`;
|
|
344
|
+
}
|
|
314
345
|
async function installFabricArchiveSkill(projectRoot, _options = {}) {
|
|
315
346
|
const source = await readTemplate(SKILL_TEMPLATE_REL);
|
|
316
347
|
const targets = SKILL_DESTINATIONS.fabricArchive.map((rel) => join2(projectRoot, rel));
|
|
@@ -416,12 +447,13 @@ async function mergeCursorHookConfig(projectRoot, _options = {}) {
|
|
|
416
447
|
[...HOOK_CONFIG_ARRAY_PATHS.cursor]
|
|
417
448
|
);
|
|
418
449
|
}
|
|
419
|
-
async function
|
|
450
|
+
async function addFabricKnowledgeBaseSection(projectRoot, fabricLanguage, _options = {}) {
|
|
451
|
+
const sectionBody = buildFabricKnowledgeBaseSection(fabricLanguage);
|
|
420
452
|
const results = [];
|
|
421
|
-
for (const rel of
|
|
453
|
+
for (const rel of SECTION_TARGETS) {
|
|
422
454
|
const target = join2(projectRoot, rel);
|
|
423
455
|
if (!existsSync2(target)) {
|
|
424
|
-
results.push({ step: "
|
|
456
|
+
results.push({ step: "section", path: target, status: "skipped", message: "absent" });
|
|
425
457
|
continue;
|
|
426
458
|
}
|
|
427
459
|
let existing;
|
|
@@ -429,47 +461,43 @@ async function addArchiveSkillPointer(projectRoot, _options = {}) {
|
|
|
429
461
|
existing = await readFile2(target, "utf8");
|
|
430
462
|
} catch (error) {
|
|
431
463
|
results.push({
|
|
432
|
-
step: "
|
|
464
|
+
step: "section",
|
|
433
465
|
path: target,
|
|
434
466
|
status: "error",
|
|
435
467
|
message: error instanceof Error ? error.message : String(error)
|
|
436
468
|
});
|
|
437
469
|
continue;
|
|
438
470
|
}
|
|
439
|
-
let next
|
|
440
|
-
|
|
441
|
-
if (
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
${
|
|
471
|
+
let next;
|
|
472
|
+
const match = existing.match(FABRIC_SECTION_REGEX);
|
|
473
|
+
if (match !== null) {
|
|
474
|
+
const before = existing.slice(0, match.index ?? 0);
|
|
475
|
+
const after = existing.slice((match.index ?? 0) + match[0].length);
|
|
476
|
+
const stripped = `${before}${after.replace(/^\r?\n/, "")}`;
|
|
477
|
+
const trailingNewline = stripped.length === 0 || stripped.endsWith("\n") ? "" : "\n";
|
|
478
|
+
next = `${stripped}${trailingNewline}
|
|
479
|
+
${sectionBody}
|
|
447
480
|
`;
|
|
448
|
-
wrote = true;
|
|
449
|
-
results.push({ step: "pointer", path: target, status: "written" });
|
|
450
|
-
}
|
|
451
|
-
if (next.includes(REVIEW_POINTER_LINE)) {
|
|
452
|
-
results.push({ step: "pointer-review", path: target, status: "skipped", message: "already-present" });
|
|
453
481
|
} else {
|
|
454
|
-
const trailingNewline =
|
|
455
|
-
next = `${
|
|
456
|
-
${
|
|
482
|
+
const trailingNewline = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
483
|
+
next = `${existing}${trailingNewline}
|
|
484
|
+
${sectionBody}
|
|
457
485
|
`;
|
|
458
|
-
wrote = true;
|
|
459
|
-
results.push({ step: "pointer-review", path: target, status: "written" });
|
|
460
486
|
}
|
|
461
|
-
if (next
|
|
462
|
-
results.push({ step: "
|
|
463
|
-
|
|
464
|
-
const trailingNewline = next.length === 0 || next.endsWith("\n") ? "" : "\n";
|
|
465
|
-
next = `${next}${trailingNewline}
|
|
466
|
-
${IMPORT_POINTER_LINE}
|
|
467
|
-
`;
|
|
468
|
-
wrote = true;
|
|
469
|
-
results.push({ step: "pointer-import", path: target, status: "written" });
|
|
487
|
+
if (next === existing) {
|
|
488
|
+
results.push({ step: "section", path: target, status: "skipped", message: "up-to-date" });
|
|
489
|
+
continue;
|
|
470
490
|
}
|
|
471
|
-
|
|
491
|
+
try {
|
|
472
492
|
await atomicWriteText(target, next);
|
|
493
|
+
results.push({ step: "section", path: target, status: "written" });
|
|
494
|
+
} catch (error) {
|
|
495
|
+
results.push({
|
|
496
|
+
step: "section",
|
|
497
|
+
path: target,
|
|
498
|
+
status: "error",
|
|
499
|
+
message: error instanceof Error ? error.message : String(error)
|
|
500
|
+
});
|
|
473
501
|
}
|
|
474
502
|
}
|
|
475
503
|
return results;
|
|
@@ -559,10 +587,9 @@ export {
|
|
|
559
587
|
HOOK_CONFIG_TARGETS,
|
|
560
588
|
HOOK_CONFIG_ARRAY_PATHS,
|
|
561
589
|
FABRIC_HOOK_COMMAND_PATHS,
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
POINTER_TARGETS,
|
|
590
|
+
SECTION_TARGETS,
|
|
591
|
+
FABRIC_SECTION_REGEX,
|
|
592
|
+
readFabricLanguagePreference,
|
|
566
593
|
installFabricArchiveSkill,
|
|
567
594
|
installFabricReviewSkill,
|
|
568
595
|
installFabricImportSkill,
|
|
@@ -572,5 +599,5 @@ export {
|
|
|
572
599
|
mergeClaudeCodeHookConfig,
|
|
573
600
|
mergeCodexHookConfig,
|
|
574
601
|
mergeCursorHookConfig,
|
|
575
|
-
|
|
602
|
+
addFabricKnowledgeBaseSection
|
|
576
603
|
};
|
package/dist/index.js
CHANGED
|
@@ -8,12 +8,12 @@ import { defineCommand, runMain } from "citty";
|
|
|
8
8
|
|
|
9
9
|
// src/commands/index.ts
|
|
10
10
|
var allCommands = {
|
|
11
|
-
|
|
12
|
-
scan: () => import("./scan-
|
|
11
|
+
install: () => import("./install-SLS5W27W.js").then((module) => module.default),
|
|
12
|
+
scan: () => import("./scan-VHKZPT2W.js").then((module) => module.default),
|
|
13
13
|
serve: () => import("./serve-NGLXHDYC.js").then((module) => module.default),
|
|
14
|
-
uninstall: () => import("./uninstall-
|
|
14
|
+
uninstall: () => import("./uninstall-JHUSFENL.js").then((module) => module.default),
|
|
15
15
|
doctor: () => import("./doctor-RILCO5OG.js").then((module) => module.default),
|
|
16
|
-
hooks: () => import("./hooks-
|
|
16
|
+
hooks: () => import("./hooks-HIWYI3VG.js").then((module) => module.default),
|
|
17
17
|
"plan-context-hint": () => import("./plan-context-hint-QMUPAXIB.js").then((module) => module.default)
|
|
18
18
|
};
|
|
19
19
|
|
|
@@ -21,8 +21,8 @@ var allCommands = {
|
|
|
21
21
|
var main = defineCommand({
|
|
22
22
|
meta: {
|
|
23
23
|
name: "fabric",
|
|
24
|
-
version: "2.0.0-rc.
|
|
25
|
-
description: 'Initialize and manage Fabric projects. Use "fabric
|
|
24
|
+
version: "2.0.0-rc.13",
|
|
25
|
+
description: 'Initialize and manage Fabric projects. Use "fabric install" for one-shot setup.'
|
|
26
26
|
},
|
|
27
27
|
subCommands: allCommands
|
|
28
28
|
});
|