@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
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
import {
|
|
3
3
|
detectClientSupports,
|
|
4
4
|
resolveClients
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OHWQNSLH.js";
|
|
6
6
|
import {
|
|
7
7
|
FABRIC_HOOK_COMMAND_PATHS,
|
|
8
|
+
FABRIC_SECTION_REGEX,
|
|
8
9
|
HOOK_CONFIG_ARRAY_PATHS,
|
|
9
10
|
HOOK_CONFIG_TARGETS,
|
|
10
11
|
HOOK_SCRIPT_DESTINATIONS,
|
|
11
|
-
|
|
12
|
-
POINTER_LINE,
|
|
13
|
-
POINTER_TARGETS,
|
|
14
|
-
REVIEW_POINTER_LINE,
|
|
12
|
+
SECTION_TARGETS,
|
|
15
13
|
SKILL_DESTINATIONS
|
|
16
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-X7QPY5KH.js";
|
|
17
15
|
import {
|
|
18
16
|
paint
|
|
19
17
|
} from "./chunk-WWNXR34K.js";
|
|
@@ -115,12 +113,12 @@ async function unmergeCursorHookConfig(projectRoot, opts = {}) {
|
|
|
115
113
|
cleanEmpties: opts.cleanEmpties === true
|
|
116
114
|
});
|
|
117
115
|
}
|
|
118
|
-
async function
|
|
116
|
+
async function stripFabricKnowledgeBaseSection(projectRoot) {
|
|
119
117
|
const results = [];
|
|
120
|
-
for (const rel of
|
|
118
|
+
for (const rel of SECTION_TARGETS) {
|
|
121
119
|
const target = join(projectRoot, rel);
|
|
122
120
|
if (!existsSync(target)) {
|
|
123
|
-
results.push({ step: "
|
|
121
|
+
results.push({ step: "section", path: target, status: "skipped", message: "absent" });
|
|
124
122
|
continue;
|
|
125
123
|
}
|
|
126
124
|
let existing;
|
|
@@ -128,30 +126,41 @@ async function stripArchiveSkillPointers(projectRoot) {
|
|
|
128
126
|
existing = await readFile(target, "utf8");
|
|
129
127
|
} catch (error) {
|
|
130
128
|
results.push({
|
|
131
|
-
step: "
|
|
129
|
+
step: "section",
|
|
132
130
|
path: target,
|
|
133
131
|
status: "error",
|
|
134
132
|
message: error instanceof Error ? error.message : String(error)
|
|
135
133
|
});
|
|
136
134
|
continue;
|
|
137
135
|
}
|
|
138
|
-
const
|
|
139
|
-
|
|
136
|
+
const match = existing.match(FABRIC_SECTION_REGEX);
|
|
137
|
+
if (match === null) {
|
|
138
|
+
results.push({
|
|
139
|
+
step: "section",
|
|
140
|
+
path: target,
|
|
141
|
+
status: "skipped",
|
|
142
|
+
message: "no-fabric-section"
|
|
143
|
+
});
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const before = existing.slice(0, match.index ?? 0);
|
|
147
|
+
const after = existing.slice((match.index ?? 0) + match[0].length);
|
|
148
|
+
const filtered = `${before}${after.replace(/^\r?\n/, "")}`;
|
|
140
149
|
if (filtered === existing) {
|
|
141
150
|
results.push({
|
|
142
|
-
step: "
|
|
151
|
+
step: "section",
|
|
143
152
|
path: target,
|
|
144
153
|
status: "skipped",
|
|
145
|
-
message: "no-fabric-
|
|
154
|
+
message: "no-fabric-section"
|
|
146
155
|
});
|
|
147
156
|
continue;
|
|
148
157
|
}
|
|
149
158
|
try {
|
|
150
159
|
await atomicWriteText(target, filtered);
|
|
151
|
-
results.push({ step: "
|
|
160
|
+
results.push({ step: "section", path: target, status: "removed" });
|
|
152
161
|
} catch (error) {
|
|
153
162
|
results.push({
|
|
154
|
-
step: "
|
|
163
|
+
step: "section",
|
|
155
164
|
path: target,
|
|
156
165
|
status: "error",
|
|
157
166
|
message: error instanceof Error ? error.message : String(error)
|
|
@@ -164,9 +173,9 @@ async function uninstallBootstrapStage(projectRoot, opts = {}) {
|
|
|
164
173
|
const results = [];
|
|
165
174
|
await runAndCollect(
|
|
166
175
|
results,
|
|
167
|
-
"
|
|
176
|
+
"section",
|
|
168
177
|
projectRoot,
|
|
169
|
-
() =>
|
|
178
|
+
() => stripFabricKnowledgeBaseSection(projectRoot)
|
|
170
179
|
);
|
|
171
180
|
await runAndCollectOne(
|
|
172
181
|
results,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fenglimg/fabric-cli",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fab": "dist/index.js",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"tree-sitter-javascript": "^0.25.0",
|
|
21
21
|
"tree-sitter-typescript": "^0.23.2",
|
|
22
22
|
"web-tree-sitter": "^0.26.8",
|
|
23
|
-
"@fenglimg/fabric-
|
|
24
|
-
"@fenglimg/fabric-
|
|
23
|
+
"@fenglimg/fabric-shared": "2.0.0-rc.13",
|
|
24
|
+
"@fenglimg/fabric-server": "2.0.0-rc.13"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^22.15.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Client hook config templates
|
|
2
2
|
|
|
3
|
-
These JSON files are **fragment templates** consumed by `fabric
|
|
3
|
+
These JSON files are **fragment templates** consumed by `fabric install` and
|
|
4
4
|
`fabric hooks install`. They are not standalone client config files.
|
|
5
5
|
|
|
6
6
|
The supported clients are pinned by `packages/shared/src/schemas/fabric-config.ts`
|
|
@@ -49,41 +49,22 @@
|
|
|
49
49
|
const { spawnSync } = require("node:child_process");
|
|
50
50
|
const {
|
|
51
51
|
existsSync,
|
|
52
|
-
mkdirSync,
|
|
53
52
|
readdirSync,
|
|
54
53
|
readFileSync,
|
|
55
|
-
writeFileSync,
|
|
56
54
|
} = require("node:fs");
|
|
57
|
-
const {
|
|
55
|
+
const { join } = require("node:path");
|
|
58
56
|
|
|
59
57
|
// -----------------------------------------------------------------------------
|
|
60
|
-
// rc.
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
// update sidecar.
|
|
68
|
-
//
|
|
69
|
-
// The revision_hash is supplied by `fabric plan-context-hint --all`'s JSON
|
|
70
|
-
// payload (carried in payload.revision_hash since rc.5). Reusing the existing
|
|
71
|
-
// hash primitive keeps the gating predicate exactly aligned with the "is the
|
|
72
|
-
// knowledge graph different from last time?" question — no second hashing
|
|
73
|
-
// scheme to maintain. computeRevisionHash() is not needed at this layer; we
|
|
74
|
-
// compare the strings the CLI hands us.
|
|
75
|
-
//
|
|
76
|
-
// rc.8 underseed self-check: the retired `.fabric/.import-requested` sentinel
|
|
77
|
-
// mechanism is replaced by a deterministic three-condition probe in
|
|
78
|
-
// shouldRecommendImport(). When the probe says "recommend", a one-line
|
|
79
|
-
// `/fabric-import` banner is appended to the broad-injection output and
|
|
80
|
-
// the revision_hash gate is bypassed FOR THE BANNER ONLY (the broad-summary
|
|
81
|
-
// body itself remains hash-gated). See shouldRecommendImport() below for
|
|
82
|
-
// the full truth table.
|
|
58
|
+
// rc.12: SessionStart broad-menu is now unconditionally emitted on every
|
|
59
|
+
// SessionStart fire (matching Skill-style progressive disclosure). Prior
|
|
60
|
+
// versions (rc.5-rc.11) wrote `.fabric/.cache/sessionstart-last-hash` as a
|
|
61
|
+
// revision_hash cooldown sidecar to suppress re-emission on unchanged
|
|
62
|
+
// knowledge graphs; that gate was removed in rc.12. Orphaned sidecar files
|
|
63
|
+
// on existing dogfood repos are harmless dead state and are intentionally
|
|
64
|
+
// NOT cleaned up (zero-user clean-slate — no migration logic needed).
|
|
83
65
|
// -----------------------------------------------------------------------------
|
|
84
66
|
|
|
85
67
|
const FABRIC_DIR_REL = ".fabric";
|
|
86
|
-
const SESSIONSTART_HASH_CACHE_FILE = join(".fabric", ".cache", "sessionstart-last-hash");
|
|
87
68
|
|
|
88
69
|
// rc.8 underseed self-check constants (mirror fabric-hint.cjs ~line 76 / 83).
|
|
89
70
|
// Intentionally duplicated inline — hooks are independent .cjs files and
|
|
@@ -102,41 +83,6 @@ const KNOWLEDGE_CANONICAL_TYPES = [
|
|
|
102
83
|
];
|
|
103
84
|
const DEFAULT_UNDERSEED_NODE_THRESHOLD = 10;
|
|
104
85
|
|
|
105
|
-
/**
|
|
106
|
-
* Read the previously-emitted revision_hash from
|
|
107
|
-
* `.fabric/.cache/sessionstart-last-hash`. Missing file / read failure /
|
|
108
|
-
* empty file → null (treat as "no prior emit", forces re-emit).
|
|
109
|
-
*
|
|
110
|
-
* NEVER throws — best-effort read.
|
|
111
|
-
*/
|
|
112
|
-
function readSessionStartLastHash(projectRoot) {
|
|
113
|
-
try {
|
|
114
|
-
const p = join(projectRoot, SESSIONSTART_HASH_CACHE_FILE);
|
|
115
|
-
if (!existsSync(p)) return null;
|
|
116
|
-
const raw = readFileSync(p, "utf8").trim();
|
|
117
|
-
return raw.length > 0 ? raw : null;
|
|
118
|
-
} catch {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Write `hash` to `.fabric/.cache/sessionstart-last-hash` so subsequent
|
|
125
|
-
* SessionStart fires can compare. Creates the directory if missing.
|
|
126
|
-
* Best-effort: any write failure is swallowed so a read-only .fabric/
|
|
127
|
-
* never blocks session start.
|
|
128
|
-
*/
|
|
129
|
-
function writeSessionStartLastHash(projectRoot, hash) {
|
|
130
|
-
try {
|
|
131
|
-
if (typeof hash !== "string" || hash.length === 0) return;
|
|
132
|
-
const p = join(projectRoot, SESSIONSTART_HASH_CACHE_FILE);
|
|
133
|
-
mkdirSync(dirname(p), { recursive: true });
|
|
134
|
-
writeFileSync(p, hash, "utf8");
|
|
135
|
-
} catch {
|
|
136
|
-
// Silent — sidecar failure must never block session start.
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
86
|
// -----------------------------------------------------------------------------
|
|
141
87
|
// rc.8 underseed self-check helpers.
|
|
142
88
|
//
|
|
@@ -488,15 +434,21 @@ function renderTruncated(narrow) {
|
|
|
488
434
|
* (empty narrow set) so callers know to stay silent.
|
|
489
435
|
*/
|
|
490
436
|
function renderSummary(payload) {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
437
|
+
// Local rebind: `payload.narrow` in `--all` mode degenerates to the full
|
|
438
|
+
// shared index (every broad-scoped entry), so the field name `narrow` is
|
|
439
|
+
// misleading at this rendering layer. We rename the local variable to
|
|
440
|
+
// `entries` to avoid name confusion when reading renderSummary in isolation.
|
|
441
|
+
// The CLI protocol field name (`payload.narrow`) is unchanged — a wire-shape
|
|
442
|
+
// rename is a deferred independent task.
|
|
443
|
+
const entries = Array.isArray(payload && payload.narrow) ? payload.narrow : [];
|
|
444
|
+
if (entries.length === 0) return [];
|
|
445
|
+
|
|
446
|
+
const truncated = entries.length > TRUNCATION_THRESHOLD;
|
|
495
447
|
const banner = truncated
|
|
496
|
-
? `[fabric] Session start — ${
|
|
497
|
-
: `[fabric] Session start — ${
|
|
448
|
+
? `[fabric] Session start — ${entries.length} broad-scoped knowledge entries available (truncated):`
|
|
449
|
+
: `[fabric] Session start — ${entries.length} broad-scoped knowledge entries available:`;
|
|
498
450
|
|
|
499
|
-
const body = truncated ? renderTruncated(
|
|
451
|
+
const body = truncated ? renderTruncated(entries) : renderFull(entries);
|
|
500
452
|
|
|
501
453
|
const lines = [banner, ...body];
|
|
502
454
|
const revHash = typeof payload.revision_hash === "string" ? payload.revision_hash : null;
|
|
@@ -524,32 +476,15 @@ function main(env, stdio) {
|
|
|
524
476
|
if (payload === null || payload === undefined) return; // silent
|
|
525
477
|
|
|
526
478
|
// rc.8 underseed self-check: decide whether to surface the one-line
|
|
527
|
-
// `/fabric-import` recommendation
|
|
528
|
-
// revision_hash gate so the banner can bypass it (an unchanged
|
|
529
|
-
// knowledge graph would otherwise hide the recommendation forever).
|
|
530
|
-
// The broad-summary BODY itself remains hash-gated below — only the
|
|
531
|
-
// banner line is unconditionally emitted when the probe says so.
|
|
479
|
+
// `/fabric-import` recommendation banner alongside the broad summary.
|
|
532
480
|
const recommendImport = shouldRecommendImport(cwd);
|
|
533
481
|
|
|
534
|
-
// rc.
|
|
535
|
-
//
|
|
536
|
-
//
|
|
537
|
-
//
|
|
538
|
-
//
|
|
539
|
-
const
|
|
540
|
-
typeof payload.revision_hash === "string" ? payload.revision_hash : "";
|
|
541
|
-
let bodySuppressed = false;
|
|
542
|
-
if (currentHash.length > 0) {
|
|
543
|
-
const lastHash = readSessionStartLastHash(cwd);
|
|
544
|
-
if (lastHash !== null && lastHash === currentHash) {
|
|
545
|
-
bodySuppressed = true;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// Build emitted lines. When the body is hash-suppressed we skip the
|
|
550
|
-
// broad summary entirely; only the import banner (if applicable) goes
|
|
551
|
-
// to stderr in that case.
|
|
552
|
-
const lines = bodySuppressed ? [] : renderSummary(payload);
|
|
482
|
+
// rc.12: broad-summary body is unconditionally rendered on every
|
|
483
|
+
// SessionStart fire (Skill-style progressive disclosure). The prior
|
|
484
|
+
// revision_hash cooldown gate (rc.7 T8 — rc.11) was removed because
|
|
485
|
+
// compact/clear-triggered SessionStart re-fires must re-inject the menu
|
|
486
|
+
// for the agent's working memory.
|
|
487
|
+
const lines = renderSummary(payload);
|
|
553
488
|
|
|
554
489
|
if (recommendImport) {
|
|
555
490
|
lines.push(IMPORT_RECOMMENDATION_BANNER);
|
|
@@ -560,16 +495,6 @@ function main(env, stdio) {
|
|
|
560
495
|
for (const line of lines) {
|
|
561
496
|
err.write(`${line}\n`);
|
|
562
497
|
}
|
|
563
|
-
|
|
564
|
-
// Update sidecar AFTER successful emit. We only persist the hash when
|
|
565
|
-
// the broad-summary body actually went out (i.e. the gate let the body
|
|
566
|
-
// through). If the body was suppressed but the banner emitted on its
|
|
567
|
-
// own, we deliberately do NOT bump the sidecar — the next session
|
|
568
|
-
// should still get to compare against the prior canonical-graph hash
|
|
569
|
-
// and re-emit the body when the graph actually changes.
|
|
570
|
-
if (!bodySuppressed && currentHash.length > 0) {
|
|
571
|
-
writeSessionStartLastHash(cwd, currentHash);
|
|
572
|
-
}
|
|
573
498
|
} catch {
|
|
574
499
|
// Silent — never block session start on hook failure.
|
|
575
500
|
}
|
|
@@ -583,9 +508,6 @@ module.exports = {
|
|
|
583
508
|
renderTruncated,
|
|
584
509
|
renderSummary,
|
|
585
510
|
truncateSummary,
|
|
586
|
-
// rc.7 T8: revision_hash gating sidecar helpers (exported for unit testing).
|
|
587
|
-
readSessionStartLastHash,
|
|
588
|
-
writeSessionStartLastHash,
|
|
589
511
|
// rc.8 underseed self-check helpers (exported for unit testing).
|
|
590
512
|
countCanonicalNodes,
|
|
591
513
|
readUnderseedThreshold,
|
|
@@ -599,7 +521,6 @@ module.exports = {
|
|
|
599
521
|
MATURITY_PROVEN,
|
|
600
522
|
MATURITY_VERIFIED,
|
|
601
523
|
MATURITY_DRAFT,
|
|
602
|
-
SESSIONSTART_HASH_CACHE_FILE,
|
|
603
524
|
DEFAULT_UNDERSEED_NODE_THRESHOLD,
|
|
604
525
|
KNOWLEDGE_CANONICAL_TYPES,
|
|
605
526
|
IMPORT_RECOMMENDATION_BANNER,
|
|
@@ -19,7 +19,7 @@ If none of the above hold, stop the skill immediately and tell the user (UX i18n
|
|
|
19
19
|
- zh-CN: `没有触发归档信号;如需手动归档请显式调用 fabric-archive`
|
|
20
20
|
- en: `No archive signal detected; to manually archive, explicitly invoke fabric-archive`
|
|
21
21
|
|
|
22
|
-
(Render per `
|
|
22
|
+
(Render per `fabric_language` resolved in Phase 0.6 Config Load below.)
|
|
23
23
|
|
|
24
24
|
This skill is `Check-not-Ask`, not a preference interview:
|
|
25
25
|
|
|
@@ -47,9 +47,10 @@ If `.fabric/fabric-config.json` is missing or unreadable, use defaults silently.
|
|
|
47
47
|
|
|
48
48
|
### UX i18n Policy (5-class bilingualization)
|
|
49
49
|
|
|
50
|
-
The skill consults `
|
|
50
|
+
The skill consults `fabric_language` from `.fabric/fabric-config.json`
|
|
51
51
|
(固化于 init 时,via `scan.ts:detectExistingLanguage`; default `"en"` when no
|
|
52
|
-
CJK signal is detected in README + docs
|
|
52
|
+
CJK signal is detected in README + docs/; may resolve to `"match-existing"`,
|
|
53
|
+
`"zh-CN"`, `"en"`, or `"zh-CN-hybrid"`). All user-facing text in the
|
|
53
54
|
following 5 categories MUST be rendered in the resolved language:
|
|
54
55
|
|
|
55
56
|
1. **Roll-up templates** — the `# Archive Review — N candidates` batch
|
|
@@ -73,11 +74,10 @@ following 5 categories MUST be rendered in the resolved language:
|
|
|
73
74
|
|
|
74
75
|
Rendering rule:
|
|
75
76
|
|
|
76
|
-
- `
|
|
77
|
-
- `
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
or fully en.
|
|
77
|
+
- `fabric_language === "zh-CN"` → emit the zh-CN variant; pure monolingual, no language mixing inside a single user-facing block.
|
|
78
|
+
- `fabric_language === "en"` → emit the en variant; pure monolingual, no language mixing inside a single user-facing block.
|
|
79
|
+
- `fabric_language === "zh-CN-hybrid"` → emit Chinese narrative prose with English technical terms preserved. Protected tokens (always EN): MCP tool names (e.g. `fab_get_knowledge_sections`), CLI command names (e.g. `fab install`), file paths, technical concepts (`Skill`, `SessionStart`, `hook`, `MCP`, `revision_hash`, `pending`, `proven`, `verified`, `draft`).
|
|
80
|
+
- `fabric_language === "match-existing"` or any other value → emit the en variant; pure monolingual.
|
|
81
81
|
|
|
82
82
|
Protected tokens (`fab_extract_knowledge`, `relevance_scope`,
|
|
83
83
|
`relevance_paths`, `narrow`, `broad`, `source_sessions`, `proposed_reason`,
|
|
@@ -91,21 +91,21 @@ bilingualization scope is prose ONLY.
|
|
|
91
91
|
|
|
92
92
|
When a skill (this one or any sibling skill the user is composing with)
|
|
93
93
|
issues an `AskUserQuestion`, the `header` and `question` strings are
|
|
94
|
-
user-facing prose → translated per `
|
|
94
|
+
user-facing prose → translated per `fabric_language`. The `options[]`
|
|
95
95
|
array entries (e.g. `["approve", "reject", "modify", "defer", "skip"]` in
|
|
96
96
|
fabric-review, or `["team", "personal"]` for a layer-flip target) are
|
|
97
97
|
**routing keys** consumed by the skill state machine — they MUST remain
|
|
98
|
-
English regardless of `
|
|
98
|
+
English regardless of `fabric_language`.
|
|
99
99
|
|
|
100
100
|
```ts
|
|
101
|
-
// EN (
|
|
101
|
+
// EN (fabric_language === "en")
|
|
102
102
|
AskUserQuestion({
|
|
103
103
|
header: "Layer-flip target",
|
|
104
104
|
question: "Move '{title}' to which layer? (current: {current_layer})",
|
|
105
105
|
options: ["team", "personal"]
|
|
106
106
|
})
|
|
107
107
|
|
|
108
|
-
// zh-CN (
|
|
108
|
+
// zh-CN (fabric_language === "zh-CN")
|
|
109
109
|
AskUserQuestion({
|
|
110
110
|
header: "Layer 切换目标",
|
|
111
111
|
question: "将 '{title}' 切换到哪一层?(当前: {current_layer})",
|
|
@@ -214,7 +214,7 @@ ELSE:
|
|
|
214
214
|
|
|
215
215
|
#### On gate FAIL
|
|
216
216
|
|
|
217
|
-
Stop the skill with the gate-FAIL message (UX i18n Policy class 2 — errors/preconditions; render per `
|
|
217
|
+
Stop the skill with the gate-FAIL message (UX i18n Policy class 2 — errors/preconditions; render per `fabric_language`):
|
|
218
218
|
|
|
219
219
|
zh-CN variant:
|
|
220
220
|
|
|
@@ -317,7 +317,7 @@ Recent session contains an observation worth keeping?
|
|
|
317
317
|
|
|
318
318
|
Present all candidates in a single screen. UX i18n Policy classes 1 + 3 — the roll-up structure AND the per-candidate `Confirm?` prompt are bilingualized; protected tokens (`relevance_scope`, `relevance_paths`, `narrow`, `broad`, `layer`, `team`, `personal`, `pending_path`, etc.) appear verbatim in BOTH variants. Field VALUES (slugs, file paths, type/layer enum strings like `decision` / `team`) are data and are NOT translated.
|
|
319
319
|
|
|
320
|
-
en variant (`
|
|
320
|
+
en variant (`fabric_language === "en"`):
|
|
321
321
|
|
|
322
322
|
```md
|
|
323
323
|
# Archive Review — N candidates
|
|
@@ -337,7 +337,7 @@ relevance_paths: []
|
|
|
337
337
|
Confirm? ...
|
|
338
338
|
```
|
|
339
339
|
|
|
340
|
-
zh-CN variant (`
|
|
340
|
+
zh-CN variant (`fabric_language === "zh-CN"`):
|
|
341
341
|
|
|
342
342
|
```md
|
|
343
343
|
# 归档 Review — N 条候选
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fabric-import
|
|
3
|
-
description: Use this skill for cold-start enrichment of `.fabric/knowledge/` from existing project artifacts — mines `git log` and `docs/*.md` for candidate observations, proposes pending entries via `fab_extract_knowledge`, then deduplicates against canonical entries via `fab_review action
|
|
3
|
+
description: Use this skill for cold-start enrichment of `.fabric/knowledge/` from existing project artifacts — mines `git log` and `docs/*.md` for candidate observations, proposes pending entries via `fab_extract_knowledge`, then deduplicates against canonical entries via `fab_review action=search` (rejecting obvious duplicates, modifying-to-merge marginal duplicates). Triggered by user prompts like "import knowledge from git history" / "bootstrap fabric for this repo" or by an explicit fabric-import skill mention. Default layer is `team` (project artifacts are team-shared). The 3-phase pipeline is resumable via `.fabric/.import-state.json`.
|
|
4
4
|
allowed-tools: Read, Glob, Grep, Bash, mcp__fabric__fab_extract_knowledge, mcp__fabric__fab_review
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ allowed-tools: Read, Glob, Grep, Bash, mcp__fabric__fab_extract_knowledge, mcp__
|
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
10
10
|
|
|
11
|
-
`fabric-import` is a one-time (per project) cold-start skill that lifts existing project artifacts — git commit history and Markdown documentation — into the knowledge layer as pending entries. It is the bridge between a brand-new Fabric installation (which only has the 4–7 baseline entries produced by `fabric
|
|
11
|
+
`fabric-import` is a one-time (per project) cold-start skill that lifts existing project artifacts — git commit history and Markdown documentation — into the knowledge layer as pending entries. It is the bridge between a brand-new Fabric installation (which only has the 4–7 baseline entries produced by `fabric install`'s deterministic scan) and a useful corpus that reflects accumulated team thinking. Run it once when adopting Fabric on an existing repo, or after a major refactor that invalidates large chunks of canonical knowledge. Default layer is `team`: project artifacts in git/docs are team-shared by definition; the user can later layer-flip individual entries to `personal` via `fabric-review` modify.
|
|
12
12
|
|
|
13
13
|
## Precondition
|
|
14
14
|
|
|
@@ -23,13 +23,13 @@ If none of the above hold, stop the skill immediately and tell the user:
|
|
|
23
23
|
- zh-CN: `没有触发 import 信号;如需手动 import 请显式调用 fabric-import`
|
|
24
24
|
- en: `No import signal detected; to manually import, explicitly invoke fabric-import`
|
|
25
25
|
|
|
26
|
-
(Render per `
|
|
26
|
+
(Render per `fabric_language` resolved in Phase 0.5 Config Load below — class 2 of the UX i18n Policy.)
|
|
27
27
|
|
|
28
28
|
> **Recommendation source (rc.8+)**: 过去版本的 `.fabric/.import-requested` sentinel 机制已下线;推荐由 SessionStart hook 的 underseed 自检触发(`templates/hooks/knowledge-hint-broad.cjs` 的 `shouldRecommendImport()`:`agents.meta.json` 存在 + canonical 节点数 < `underseed_node_threshold` + `.import-state.json` 缺失三条件齐备时一次性提示)。本 skill 不再读写 sentinel 文件,也不需要在 Phase 3 完成时手动清理它。
|
|
29
29
|
|
|
30
30
|
This skill SHOULD be skipped (warn the user, do not proceed) when:
|
|
31
31
|
|
|
32
|
-
- `.fabric/` does not exist — direct the user to run `fabric
|
|
32
|
+
- `.fabric/` does not exist — direct the user to run `fabric install` first; `fabric-import` is NOT a substitute for the deterministic install-scan
|
|
33
33
|
- `.fabric/knowledge/` already holds **>`import_skip_canonical_threshold` canonical entries (config-resolved, default 50)** across all types — the project is mature; use `fabric-archive` (per-session capture) and `fabric-review` (lifecycle review) instead; bulk import would just create dup churn
|
|
34
34
|
- `.fabric/.import-state.json` exists with `phase: "complete"` and `last_checkpoint_at` is **<24h ago** — the user just ran import; surface the prior result rather than re-running
|
|
35
35
|
|
|
@@ -114,15 +114,16 @@ and `final_summary.proposed == 0`) → first-run window; otherwise re-run window
|
|
|
114
114
|
|
|
115
115
|
### UX i18n Policy (5-class bilingualization)
|
|
116
116
|
|
|
117
|
-
The skill consults `
|
|
118
|
-
(固化于
|
|
119
|
-
CJK signal is detected in README + docs
|
|
117
|
+
The skill consults `fabric_language` from `.fabric/fabric-config.json`
|
|
118
|
+
(固化于 install 时,via `scan.ts:detectExistingLanguage`; default `"en"` when no
|
|
119
|
+
CJK signal is detected in README + docs/; may resolve to `"match-existing"`,
|
|
120
|
+
`"zh-CN"`, `"en"`, or `"zh-CN-hybrid"`). All user-facing text in the
|
|
120
121
|
following 5 categories MUST be rendered in the resolved language:
|
|
121
122
|
|
|
122
123
|
1. **Roll-up templates** — final summary blocks (`# Import Summary — phase=...`,
|
|
123
124
|
`## Phase 2 — Mining`, `## Phase 3 — Dedup`, etc.). zh-CN ↔ en mirror.
|
|
124
125
|
2. **Errors / Preconditions warnings** — abort + gate-fail messages (e.g.
|
|
125
|
-
"请先运行 fabric
|
|
126
|
+
"请先运行 fabric install 完成基线扫描…" / "Please run fabric install first…").
|
|
126
127
|
zh-CN ↔ en mirror.
|
|
127
128
|
3. **Confirmation prompts** — re-run-within-24h prompt, reset prompts, etc.
|
|
128
129
|
zh-CN ↔ en mirror.
|
|
@@ -136,11 +137,10 @@ following 5 categories MUST be rendered in the resolved language:
|
|
|
136
137
|
|
|
137
138
|
Rendering rule:
|
|
138
139
|
|
|
139
|
-
- `
|
|
140
|
-
- `
|
|
141
|
-
-
|
|
142
|
-
|
|
143
|
-
or fully en.
|
|
140
|
+
- `fabric_language === "zh-CN"` → emit the zh-CN variant; pure monolingual, no language mixing inside a single user-facing block.
|
|
141
|
+
- `fabric_language === "en"` → emit the en variant; pure monolingual, no language mixing inside a single user-facing block.
|
|
142
|
+
- `fabric_language === "zh-CN-hybrid"` → emit Chinese narrative prose with English technical terms preserved. Protected tokens (always EN): MCP tool names (e.g. `fab_get_knowledge_sections`), CLI command names (e.g. `fab install`), file paths, technical concepts (`Skill`, `SessionStart`, `hook`, `MCP`, `revision_hash`, `pending`, `proven`, `verified`, `draft`).
|
|
143
|
+
- `fabric_language === "match-existing"` or any other value → emit the en variant; pure monolingual.
|
|
144
144
|
|
|
145
145
|
Protected tokens (`fab_extract_knowledge`, `fab_review`, `relevance_scope`,
|
|
146
146
|
`relevance_paths`, `broad`, `narrow`, `source_sessions`, `proposed_reason`,
|
|
@@ -153,20 +153,20 @@ The bilingualization scope is prose ONLY.
|
|
|
153
153
|
|
|
154
154
|
When a skill (this one or any sibling skill the user is composing with)
|
|
155
155
|
issues an `AskUserQuestion`, the `header` and `question` strings are
|
|
156
|
-
user-facing prose → translated per `
|
|
156
|
+
user-facing prose → translated per `fabric_language`. The `options[]`
|
|
157
157
|
array entries (e.g. `["approve", "reject", "modify", "defer", "skip"]` in
|
|
158
158
|
fabric-review) are **routing keys** consumed by the skill state machine —
|
|
159
|
-
they MUST remain English regardless of `
|
|
159
|
+
they MUST remain English regardless of `fabric_language`.
|
|
160
160
|
|
|
161
161
|
```ts
|
|
162
|
-
// EN (
|
|
162
|
+
// EN (fabric_language === "en")
|
|
163
163
|
AskUserQuestion({
|
|
164
164
|
header: "Review pending entry",
|
|
165
165
|
question: "What action for '{title}'?",
|
|
166
166
|
options: ["approve", "reject", "modify", "defer", "skip"]
|
|
167
167
|
})
|
|
168
168
|
|
|
169
|
-
// zh-CN (
|
|
169
|
+
// zh-CN (fabric_language === "zh-CN")
|
|
170
170
|
AskUserQuestion({
|
|
171
171
|
header: "审核 pending 条目",
|
|
172
172
|
question: "对 '{title}' 执行什么操作?",
|
|
@@ -186,7 +186,7 @@ The pipeline runs strictly in order. Each phase reads the prior phase's outputs
|
|
|
186
186
|
|
|
187
187
|
### Phase 1 — Init-Scan Reference (NO RE-IMPLEMENTATION)
|
|
188
188
|
|
|
189
|
-
> Verbatim boundary: `fabric
|
|
189
|
+
> Verbatim boundary: `fabric install` (v2.0+, deterministic CLI) already produces the baseline scan. Phase 1 of this skill **REFERENCES** that output. It does NOT redo the scan.
|
|
190
190
|
|
|
191
191
|
The deterministic init-scan has already populated `.fabric/knowledge/team/` with 4–7 baseline entries derived from:
|
|
192
192
|
|
|
@@ -203,8 +203,8 @@ Phase 1 actions performed by THIS skill:
|
|
|
203
203
|
2. Glob `.fabric/knowledge/team/**/*.md` to enumerate baseline entry titles. Capture the list — Phase 2 uses these titles as a **negative filter** (signals already covered by init-scan should be skipped, not re-proposed).
|
|
204
204
|
3. If `.fabric/agents.meta.json` is missing OR `.fabric/knowledge/team/` is empty: STOP. Tell the user (UX i18n Policy class 2 — errors/preconditions):
|
|
205
205
|
|
|
206
|
-
- zh-CN: `请先运行 fabric
|
|
207
|
-
- en: `Please run fabric
|
|
206
|
+
- zh-CN: `请先运行 fabric install 完成基线扫描,再调用 fabric-import`
|
|
207
|
+
- en: `Please run fabric install first to complete the baseline scan, then invoke fabric-import`
|
|
208
208
|
|
|
209
209
|
…and exit.
|
|
210
210
|
4. Update `.fabric/.import-state.json`: `phase = "P1-done"`, `p1_baseline_titles = [<list>]`, `last_checkpoint_at = <ISO8601 now>`.
|
|
@@ -288,7 +288,7 @@ For each commit:
|
|
|
288
288
|
- `refactor(...)` with body → likely **decision** (architectural choice was made)
|
|
289
289
|
- `docs(...)` → usually a **guideline** if the body announces a convention; skip if it's just typo/reformat
|
|
290
290
|
- `chore(...)`, `test(...)`, `ci(...)` → almost always skip (mechanical; no reusable insight)
|
|
291
|
-
2. Read the commit body. Extract the LLM-judged "core observation" — what would a future engineer want to know about this commit beyond the diff? Aim for 1–2 sentences in zh-CN (project
|
|
291
|
+
2. Read the commit body. Extract the LLM-judged "core observation" — what would a future engineer want to know about this commit beyond the diff? Aim for 1–2 sentences in zh-CN (project fabric_language; mirror fabric-archive M3 style).
|
|
292
292
|
3. Apply the **Skip Decision Tree** below. If the commit is skip-worthy, record it in `p2_processed_commits[]` with `skipped: true` and move on.
|
|
293
293
|
4. For non-skipped commits, classify type / propose slug / draft summary. Then call `fab_extract_knowledge` with the **mandatory broad + [] scope** (see "Mandatory Scope Rule" above):
|
|
294
294
|
|
|
@@ -394,7 +394,7 @@ After Step 2.2 completes (or hits the cap), update `.fabric/.import-state.json`:
|
|
|
394
394
|
|
|
395
395
|
When the user invocation includes `dry-run` / `预览` / `--dry-run` keywords, Phase 2 runs WITHOUT calling `fab_extract_knowledge`. Instead it prints a table. UX i18n Policy class 4 — dry-run table headers; the header + column titles are bilingualized; row content (slug / commit sha / doc path) is data and is NOT translated. The protected tokens `broad`, `relevance_scope`, `relevance_paths` appear verbatim in both variants:
|
|
396
396
|
|
|
397
|
-
zh-CN variant (`
|
|
397
|
+
zh-CN variant (`fabric_language === "zh-CN"`):
|
|
398
398
|
|
|
399
399
|
```md
|
|
400
400
|
# Import 预览 — 将提议 N 条 pending 条目(全部 relevance_scope=broad, relevance_paths=[])
|
|
@@ -406,7 +406,7 @@ zh-CN variant (`knowledge_language === "zh-CN"`):
|
|
|
406
406
|
| 3 | git 50367b5 | pitfalls | thundering-herd-no-backoff | broad+[] | 重试无指数回退导致雪崩;必须 jittered exponential backoff。|
|
|
407
407
|
```
|
|
408
408
|
|
|
409
|
-
en variant (`
|
|
409
|
+
en variant (`fabric_language === "en"`):
|
|
410
410
|
|
|
411
411
|
```md
|
|
412
412
|
# Import Dry Run — would propose N pending entries (all relevance_scope=broad, relevance_paths=[])
|
|
@@ -648,9 +648,9 @@ The contract: re-invoking fabric-import after ANY interruption (Ctrl-C, crash, n
|
|
|
648
648
|
|
|
649
649
|
## Output Contract
|
|
650
650
|
|
|
651
|
-
After Phase 3 completes (or on any phase exit due to cap / error / interrupt), the skill MUST produce a roll-up. UX i18n Policy class 1 — render either the en variant or the zh-CN variant per `
|
|
651
|
+
After Phase 3 completes (or on any phase exit due to cap / error / interrupt), the skill MUST produce a roll-up. UX i18n Policy class 1 — render either the en variant or the zh-CN variant per `fabric_language`; the protected tokens (`relevance_scope`, `relevance_paths`, `broad`, `pending_path`, `layer`, `team`, `personal`, `fab_review`, `.fabric/.import-state.json`, etc.) appear verbatim in BOTH variants.
|
|
652
652
|
|
|
653
|
-
en variant (`
|
|
653
|
+
en variant (`fabric_language === "en"`):
|
|
654
654
|
|
|
655
655
|
```md
|
|
656
656
|
# Import Summary — phase=<P1-done | P2-done | complete>
|
|
@@ -678,7 +678,7 @@ en variant (`knowledge_language === "en"`):
|
|
|
678
678
|
- If any kept entry is actually narrow-scoped, narrow it via `fab_review action="modify"` with `changes.relevance_scope="narrow"` + `changes.relevance_paths=[...]` (this skill cannot narrow — see Mandatory Scope Rule in Phase 2).
|
|
679
679
|
```
|
|
680
680
|
|
|
681
|
-
zh-CN variant (`
|
|
681
|
+
zh-CN variant (`fabric_language === "zh-CN"`):
|
|
682
682
|
|
|
683
683
|
```md
|
|
684
684
|
# Import 汇总 — phase=<P1-done | P2-done | complete>
|