@fenglimg/fabric-cli 2.2.0-rc.4 → 2.2.0-rc.9
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 +8 -5
- package/dist/{chunk-5JG4QJLO.js → chunk-27HK6H5Y.js} +10 -5
- package/dist/{chunk-F6ITRM7T.js → chunk-2KBCTMID.js} +29 -6
- package/dist/chunk-3D7B2UAZ.js +149 -0
- package/dist/{chunk-XC5RUHLK.js → chunk-3IOLS5EK.js} +23 -38
- package/dist/{chunk-XHHCRDIR.js → chunk-7ZDXBOOU.js} +174 -211
- package/dist/{doctor-U5W4CX5I.js → chunk-E7HJUU34.js} +103 -51
- package/dist/{chunk-XCBVSGCS.js → chunk-FNHDQTPC.js} +1 -10
- package/dist/{chunk-2CY4BMTH.js → chunk-HORSMSZL.js} +9 -5
- package/dist/{chunk-BO4XIZWZ.js → chunk-NLNH64A3.js} +5 -18
- package/dist/{chunk-H3FE6VIK.js → chunk-PTGQAZEW.js} +13 -3
- package/dist/chunk-QFIVFZRH.js +13 -0
- package/dist/{chunk-5SSNE5GM.js → chunk-QPAW6IYT.js} +125 -39
- package/dist/{chunk-COI5VDFU.js → chunk-WA3DYGSY.js} +1 -2
- package/dist/{plan-context-hint-CHVZGOZ5.js → chunk-YM4XATJF.js} +29 -4
- package/dist/{config-VJMXCLXW.js → config-A3LTECAY.js} +4 -3
- package/dist/context-7NUKXDB6.js +117 -0
- package/dist/doctor-MDTZWKBK.js +24 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +131 -21
- package/dist/info-7FKBTMVO.js +139 -0
- package/dist/install-v2-I6PJ6IFT.js +3279 -0
- package/dist/{metrics-RER6NLFC.js → metrics-HMFH4YHK.js} +1 -1
- package/dist/{onboard-coverage-JWQWDZW7.js → onboard-coverage-XSG77LL3.js} +48 -27
- package/dist/plan-context-hint-G75R4P4J.js +12 -0
- package/dist/{scope-explain-BWRWBCCP.js → scope-explain-HLJZ2M33.js} +3 -2
- package/dist/{status-7UFLWRX7.js → status-4R3TM4FJ.js} +8 -5
- package/dist/{store-ZEZMQVG7.js → store-HOCORVL3.js} +96 -350
- package/dist/{sync-EA5HZMXM.js → sync-DT5UJMMR.js} +36 -13
- package/dist/{uninstall-F75MPKQC.js → uninstall-IFN2KYBK.js} +71 -140
- package/dist/{whoami-3FRWYGML.js → whoami-ITGEFWH4.js} +9 -7
- package/package.json +7 -5
- package/templates/hooks/cite-policy-evict.cjs +5 -5
- package/templates/hooks/configs/README.md +14 -27
- package/templates/hooks/configs/claude-code.json +1 -1
- package/templates/hooks/configs/codex-hooks.json +3 -3
- package/templates/hooks/fabric-hint.cjs +326 -161
- package/templates/hooks/knowledge-hint-broad.cjs +431 -271
- package/templates/hooks/knowledge-hint-narrow.cjs +64 -77
- package/templates/hooks/lib/banner-i18n.cjs +31 -0
- package/templates/hooks/lib/bindings-snapshot-reader.cjs +118 -7
- package/templates/hooks/lib/cite-line-parser.cjs +12 -20
- package/templates/hooks/lib/client-adapter.cjs +66 -7
- package/templates/hooks/lib/nudge-policy.cjs +117 -0
- package/templates/hooks/lib/state-store.cjs +60 -0
- package/templates/hooks/post-tooluse-mutation.cjs +112 -11
- package/templates/skills/fabric/SKILL.md +100 -0
- package/templates/skills/fabric-archive/SKILL.md +29 -26
- package/templates/skills/fabric-archive/ref/dry-run-scope.md +1 -1
- package/templates/skills/fabric-archive/ref/i18n-policy.md +2 -3
- package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +2 -3
- package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +1 -1
- package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +1 -1
- package/templates/skills/fabric-archive/ref/phase-3-6-related-edges.md +18 -0
- package/templates/skills/fabric-archive/ref/phase-3-7-semantic-scope.md +47 -0
- package/templates/skills/fabric-audit/SKILL.md +13 -3
- package/templates/skills/fabric-connect/SKILL.md +3 -3
- package/templates/skills/fabric-import/SKILL.md +7 -7
- package/templates/skills/fabric-import/ref/i18n-policy.md +2 -3
- package/templates/skills/fabric-import/ref/state-recovery.md +1 -2
- package/templates/skills/fabric-review/SKILL.md +5 -5
- package/templates/skills/fabric-review/ref/cite-contract.md +1 -1
- package/templates/skills/fabric-review/ref/i18n-policy.md +2 -3
- package/templates/skills/fabric-review/ref/output-contract.md +1 -1
- package/templates/skills/fabric-review/ref/per-mode-flows.md +2 -2
- package/templates/skills/fabric-review/ref/worked-examples.md +1 -1
- package/templates/skills/fabric-store/SKILL.md +1 -1
- package/templates/skills/fabric-sync/SKILL.md +1 -1
- package/templates/skills/lib/shared-policy.md +2 -2
- package/dist/install-7XJ64WSC.js +0 -2743
- package/templates/hooks/configs/cursor-hooks.json +0 -30
- package/templates/hooks/lib/cite-contract-reminder.cjs +0 -179
- package/templates/hooks/lib/summary-fallback.cjs +0 -210
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
deepMerge
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3IOLS5EK.js";
|
|
5
5
|
|
|
6
6
|
// src/install/write-bootstrap-snapshot.ts
|
|
7
7
|
import { existsSync, readFileSync } from "fs";
|
|
8
8
|
import { mkdir } from "fs/promises";
|
|
9
9
|
import { dirname, join } from "path";
|
|
10
10
|
import { atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
11
|
-
import {
|
|
11
|
+
import { resolveBootstrapCanonical } from "@fenglimg/fabric-shared/templates/bootstrap-canonical";
|
|
12
12
|
var FABRIC_AGENTS_RELPATH = join(".fabric", "AGENTS.md");
|
|
13
13
|
var PROJECT_RULES_RELPATH = join(".fabric", "project-rules.md");
|
|
14
14
|
function fabricAgentsSnapshotPath(targetRoot) {
|
|
@@ -25,32 +25,38 @@ function readProjectRulesIfPresent(targetRoot) {
|
|
|
25
25
|
async function writeFabricAgentsSnapshot(targetRoot) {
|
|
26
26
|
const step = "bootstrap-snapshot";
|
|
27
27
|
const target = fabricAgentsSnapshotPath(targetRoot);
|
|
28
|
+
const canonical = resolveBootstrapCanonical();
|
|
28
29
|
if (existsSync(target)) {
|
|
29
30
|
try {
|
|
30
31
|
const existing = readFileSync(target, "utf8");
|
|
31
|
-
if (existing ===
|
|
32
|
+
if (existing === canonical) {
|
|
32
33
|
return { step, path: target, status: "skipped", message: "up-to-date" };
|
|
33
34
|
}
|
|
34
35
|
} catch {
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
await mkdir(dirname(target), { recursive: true });
|
|
38
|
-
await atomicWriteText(target,
|
|
39
|
+
await atomicWriteText(target, canonical);
|
|
39
40
|
return { step, path: target, status: "written" };
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
// src/install/skills-and-hooks.ts
|
|
43
|
-
import { chmodSync, existsSync as existsSync2, readdirSync, readFileSync as readFileSync2
|
|
44
|
+
import { chmodSync, existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "fs";
|
|
44
45
|
import { mkdir as mkdir2, readFile, rm } from "fs/promises";
|
|
45
46
|
import { dirname as dirname2, join as join2, parse, resolve } from "path";
|
|
46
47
|
import { fileURLToPath } from "url";
|
|
48
|
+
import { resolveGlobalLocale } from "@fenglimg/fabric-shared";
|
|
47
49
|
import { atomicWriteJson, atomicWriteText as atomicWriteText2 } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
48
50
|
import {
|
|
49
51
|
BOOTSTRAP_MARKER_BEGIN,
|
|
50
52
|
BOOTSTRAP_MARKER_END,
|
|
51
|
-
BOOTSTRAP_REGEX
|
|
52
|
-
LEGACY_KB_REGEX
|
|
53
|
+
BOOTSTRAP_REGEX
|
|
53
54
|
} from "@fenglimg/fabric-shared/templates/bootstrap-canonical";
|
|
55
|
+
var SKILL_ROUTER_TEMPLATE_REL = "skills/fabric/SKILL.md";
|
|
56
|
+
var ROUTER_INTENT_MARKER_BEGIN = "<!-- fabric:router-intent:begin -->";
|
|
57
|
+
var ROUTER_INTENT_MARKER_END = "<!-- fabric:router-intent:end -->";
|
|
58
|
+
var ROUTER_INTENT_GENERATED_NOTE = "<!-- \u672C\u5757\u7531 `fabric install` \u4ECE 7 \u4E2A leaf skill \u7684 description Triggers \u5B50\u53E5\u751F\u6210\u3002\u4E25\u7981\u624B\u7F16;\u6539 leaf description \u540E\u91CD\u8DD1 `fabric install`\u3002 -->";
|
|
59
|
+
var ROUTER_INTENT_REGEX = /<!-- fabric:router-intent:begin -->[\s\S]*?<!-- fabric:router-intent:end -->/u;
|
|
54
60
|
var SKILL_TEMPLATE_REL = "skills/fabric-archive/SKILL.md";
|
|
55
61
|
var SKILL_REVIEW_TEMPLATE_REL = "skills/fabric-review/SKILL.md";
|
|
56
62
|
var SKILL_IMPORT_TEMPLATE_REL = "skills/fabric-import/SKILL.md";
|
|
@@ -67,8 +73,13 @@ var HOOK_POST_TOOLUSE_SCRIPT_TEMPLATE_REL = "hooks/post-tooluse-mutation.cjs";
|
|
|
67
73
|
var HOOK_LIB_TEMPLATE_DIR_REL = "hooks/lib";
|
|
68
74
|
var CLAUDE_HOOK_CONFIG_TEMPLATE_REL = "hooks/configs/claude-code.json";
|
|
69
75
|
var CODEX_HOOK_CONFIG_TEMPLATE_REL = "hooks/configs/codex-hooks.json";
|
|
70
|
-
var CURSOR_HOOK_CONFIG_TEMPLATE_REL = "hooks/configs/cursor-hooks.json";
|
|
71
76
|
var SKILL_DESTINATIONS = {
|
|
77
|
+
// B2 skill-router: the fabric/ router skill — single-file (no ref/), installed
|
|
78
|
+
// alongside the 7 leaf skills as the human-facing dispatch entry point.
|
|
79
|
+
fabricRouter: [
|
|
80
|
+
".claude/skills/fabric/SKILL.md",
|
|
81
|
+
".codex/skills/fabric/SKILL.md"
|
|
82
|
+
],
|
|
72
83
|
fabricArchive: [
|
|
73
84
|
".claude/skills/fabric-archive/SKILL.md",
|
|
74
85
|
".codex/skills/fabric-archive/SKILL.md"
|
|
@@ -105,6 +116,59 @@ var SKILL_DESTINATIONS = {
|
|
|
105
116
|
".codex/skills/fabric-connect/SKILL.md"
|
|
106
117
|
]
|
|
107
118
|
};
|
|
119
|
+
var FABRIC_SKILL_INSTALL_SPECS = {
|
|
120
|
+
fabricRouter: {
|
|
121
|
+
slug: "fabric",
|
|
122
|
+
templateRel: SKILL_ROUTER_TEMPLATE_REL,
|
|
123
|
+
destinations: SKILL_DESTINATIONS.fabricRouter,
|
|
124
|
+
step: "skill-router"
|
|
125
|
+
},
|
|
126
|
+
fabricArchive: {
|
|
127
|
+
slug: "fabric-archive",
|
|
128
|
+
templateRel: SKILL_TEMPLATE_REL,
|
|
129
|
+
destinations: SKILL_DESTINATIONS.fabricArchive,
|
|
130
|
+
step: "skill",
|
|
131
|
+
includeRefFiles: true
|
|
132
|
+
},
|
|
133
|
+
fabricReview: {
|
|
134
|
+
slug: "fabric-review",
|
|
135
|
+
templateRel: SKILL_REVIEW_TEMPLATE_REL,
|
|
136
|
+
destinations: SKILL_DESTINATIONS.fabricReview,
|
|
137
|
+
step: "skill-review",
|
|
138
|
+
includeRefFiles: true
|
|
139
|
+
},
|
|
140
|
+
fabricImport: {
|
|
141
|
+
slug: "fabric-import",
|
|
142
|
+
templateRel: SKILL_IMPORT_TEMPLATE_REL,
|
|
143
|
+
destinations: SKILL_DESTINATIONS.fabricImport,
|
|
144
|
+
step: "skill-import",
|
|
145
|
+
includeRefFiles: true
|
|
146
|
+
},
|
|
147
|
+
fabricSync: {
|
|
148
|
+
slug: "fabric-sync",
|
|
149
|
+
templateRel: SKILL_SYNC_TEMPLATE_REL,
|
|
150
|
+
destinations: SKILL_DESTINATIONS.fabricSync,
|
|
151
|
+
step: "skill-sync"
|
|
152
|
+
},
|
|
153
|
+
fabricStore: {
|
|
154
|
+
slug: "fabric-store",
|
|
155
|
+
templateRel: SKILL_STORE_TEMPLATE_REL,
|
|
156
|
+
destinations: SKILL_DESTINATIONS.fabricStore,
|
|
157
|
+
step: "skill-store"
|
|
158
|
+
},
|
|
159
|
+
fabricAudit: {
|
|
160
|
+
slug: "fabric-audit",
|
|
161
|
+
templateRel: SKILL_AUDIT_TEMPLATE_REL,
|
|
162
|
+
destinations: SKILL_DESTINATIONS.fabricAudit,
|
|
163
|
+
step: "skill-audit"
|
|
164
|
+
},
|
|
165
|
+
fabricConnect: {
|
|
166
|
+
slug: "fabric-connect",
|
|
167
|
+
templateRel: SKILL_CONNECT_TEMPLATE_REL,
|
|
168
|
+
destinations: SKILL_DESTINATIONS.fabricConnect,
|
|
169
|
+
step: "skill-connect"
|
|
170
|
+
}
|
|
171
|
+
};
|
|
108
172
|
var DEPRECATED_SKILL_DIRS = [
|
|
109
173
|
".claude/skills/fabric-init",
|
|
110
174
|
".codex/skills/fabric-init"
|
|
@@ -112,54 +176,46 @@ var DEPRECATED_SKILL_DIRS = [
|
|
|
112
176
|
var HOOK_SCRIPT_DESTINATIONS = {
|
|
113
177
|
fabricHint: [
|
|
114
178
|
".claude/hooks/fabric-hint.cjs",
|
|
115
|
-
".codex/hooks/fabric-hint.cjs"
|
|
116
|
-
".cursor/hooks/fabric-hint.cjs"
|
|
179
|
+
".codex/hooks/fabric-hint.cjs"
|
|
117
180
|
],
|
|
118
181
|
knowledgeHintBroad: [
|
|
119
182
|
".claude/hooks/knowledge-hint-broad.cjs",
|
|
120
|
-
".codex/hooks/knowledge-hint-broad.cjs"
|
|
121
|
-
".cursor/hooks/knowledge-hint-broad.cjs"
|
|
183
|
+
".codex/hooks/knowledge-hint-broad.cjs"
|
|
122
184
|
],
|
|
123
185
|
knowledgeHintNarrow: [
|
|
124
186
|
".claude/hooks/knowledge-hint-narrow.cjs",
|
|
125
|
-
".codex/hooks/knowledge-hint-narrow.cjs"
|
|
126
|
-
".cursor/hooks/knowledge-hint-narrow.cjs"
|
|
187
|
+
".codex/hooks/knowledge-hint-narrow.cjs"
|
|
127
188
|
],
|
|
128
189
|
// v2.0.0-rc.34 TASK-06: Claude Code — UserPromptSubmit cite-policy long-
|
|
129
190
|
// session evict sidecar.
|
|
130
|
-
// v2.0.0-rc.37 NEW-21: extended to Codex
|
|
131
|
-
//
|
|
191
|
+
// v2.0.0-rc.37 NEW-21: extended to Codex SessionStart slot.
|
|
192
|
+
// Codex doesn't have an equivalent per-prompt event, so cite-policy-
|
|
132
193
|
// evict.cjs runs in "SessionStart mode" (one-shot stderr emit per session
|
|
133
194
|
// boot, no turn-counter). Cadence is lower than Claude Code's per-prompt
|
|
134
|
-
// window but strictly better than 0 (rc.32 baseline measured Codex
|
|
195
|
+
// window but strictly better than 0 (rc.32 baseline measured Codex
|
|
135
196
|
// at 3.1% cite coverage when no cite-reminder surface existed).
|
|
136
197
|
citePolicyEvict: [
|
|
137
198
|
".claude/hooks/cite-policy-evict.cjs",
|
|
138
|
-
".codex/hooks/cite-policy-evict.cjs"
|
|
139
|
-
".cursor/hooks/cite-policy-evict.cjs"
|
|
199
|
+
".codex/hooks/cite-policy-evict.cjs"
|
|
140
200
|
],
|
|
141
|
-
// lifecycle-refactor W2-T2: SessionEnd marker hook —
|
|
201
|
+
// lifecycle-refactor W2-T2: SessionEnd marker hook — both clients.
|
|
142
202
|
sessionEndMarker: [
|
|
143
203
|
".claude/hooks/session-end-marker.cjs",
|
|
144
|
-
".codex/hooks/session-end-marker.cjs"
|
|
145
|
-
".cursor/hooks/session-end-marker.cjs"
|
|
204
|
+
".codex/hooks/session-end-marker.cjs"
|
|
146
205
|
],
|
|
147
|
-
// lifecycle-refactor W2-T3: PostToolUse mutation marker hook —
|
|
206
|
+
// lifecycle-refactor W2-T3: PostToolUse mutation marker hook — both.
|
|
148
207
|
postTooluseMutation: [
|
|
149
208
|
".claude/hooks/post-tooluse-mutation.cjs",
|
|
150
|
-
".codex/hooks/post-tooluse-mutation.cjs"
|
|
151
|
-
".cursor/hooks/post-tooluse-mutation.cjs"
|
|
209
|
+
".codex/hooks/post-tooluse-mutation.cjs"
|
|
152
210
|
]
|
|
153
211
|
};
|
|
154
212
|
var HOOK_LIB_DESTINATIONS = [
|
|
155
213
|
".claude/hooks/lib",
|
|
156
|
-
".codex/hooks/lib"
|
|
157
|
-
".cursor/hooks/lib"
|
|
214
|
+
".codex/hooks/lib"
|
|
158
215
|
];
|
|
159
216
|
var HOOK_CONFIG_TARGETS = {
|
|
160
217
|
claudeCode: ".claude/settings.json",
|
|
161
|
-
codex: ".codex/hooks.json"
|
|
162
|
-
cursor: ".cursor/hooks.json"
|
|
218
|
+
codex: ".codex/hooks.json"
|
|
163
219
|
};
|
|
164
220
|
var HOOK_CONFIG_ARRAY_PATHS = {
|
|
165
221
|
// F2: "hooks.UserPromptSubmit" MUST be listed — the Claude Code template
|
|
@@ -177,8 +233,7 @@ var HOOK_CONFIG_ARRAY_PATHS = {
|
|
|
177
233
|
"hooks.PostToolUse",
|
|
178
234
|
"hooks.SessionEnd"
|
|
179
235
|
],
|
|
180
|
-
codex: ["events.Stop", "events.SessionStart", "events.PreToolUse", "events.PostToolUse", "events.SessionEnd"]
|
|
181
|
-
cursor: ["hooks.stop", "hooks.sessionStart", "hooks.preToolUse", "hooks.postToolUse", "hooks.sessionEnd"]
|
|
236
|
+
codex: ["events.Stop", "events.SessionStart", "events.PreToolUse", "events.PostToolUse", "events.SessionEnd"]
|
|
182
237
|
};
|
|
183
238
|
var FABRIC_HOOK_COMMAND_PATHS = {
|
|
184
239
|
claudeCode: {
|
|
@@ -199,33 +254,8 @@ var FABRIC_HOOK_COMMAND_PATHS = {
|
|
|
199
254
|
citePolicyEvict: '"$(git rev-parse --show-toplevel)/.codex/hooks/cite-policy-evict.cjs"',
|
|
200
255
|
sessionEndMarker: '"$(git rev-parse --show-toplevel)/.codex/hooks/session-end-marker.cjs"',
|
|
201
256
|
postTooluseMutation: '"$(git rev-parse --show-toplevel)/.codex/hooks/post-tooluse-mutation.cjs"'
|
|
202
|
-
},
|
|
203
|
-
cursor: {
|
|
204
|
-
fabricHint: ".cursor/hooks/fabric-hint.cjs",
|
|
205
|
-
knowledgeHintBroad: ".cursor/hooks/knowledge-hint-broad.cjs",
|
|
206
|
-
knowledgeHintNarrow: ".cursor/hooks/knowledge-hint-narrow.cjs",
|
|
207
|
-
citePolicyEvict: ".cursor/hooks/cite-policy-evict.cjs",
|
|
208
|
-
sessionEndMarker: ".cursor/hooks/session-end-marker.cjs",
|
|
209
|
-
postTooluseMutation: ".cursor/hooks/post-tooluse-mutation.cjs"
|
|
210
257
|
}
|
|
211
258
|
};
|
|
212
|
-
function readFabricLanguagePreference(projectRoot) {
|
|
213
|
-
const configPath = join2(projectRoot, ".fabric", "fabric-config.json");
|
|
214
|
-
if (!existsSync2(configPath)) {
|
|
215
|
-
return "match-existing";
|
|
216
|
-
}
|
|
217
|
-
try {
|
|
218
|
-
const raw = readFileSync2(configPath, "utf8");
|
|
219
|
-
const parsed = JSON.parse(raw);
|
|
220
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
221
|
-
return "match-existing";
|
|
222
|
-
}
|
|
223
|
-
const value = parsed["fabric_language"];
|
|
224
|
-
return typeof value === "string" && value.length > 0 ? value : "match-existing";
|
|
225
|
-
} catch {
|
|
226
|
-
return "match-existing";
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
259
|
var SKILL_TOKEN_ERROR_TOKENS = 1e4;
|
|
230
260
|
var STALE_INSTALL_RATIO = 1.5;
|
|
231
261
|
function estimateSkillTokens(text) {
|
|
@@ -254,107 +284,98 @@ function inspectStaleInstall(target, source) {
|
|
|
254
284
|
}
|
|
255
285
|
return null;
|
|
256
286
|
}
|
|
257
|
-
async function
|
|
258
|
-
const source = await readTemplate(
|
|
259
|
-
validateSkillCanonicalSize(source,
|
|
260
|
-
const targets =
|
|
287
|
+
async function installFabricSkill(projectRoot, spec) {
|
|
288
|
+
const source = await readTemplate(spec.templateRel);
|
|
289
|
+
validateSkillCanonicalSize(source, spec.slug);
|
|
290
|
+
const targets = spec.destinations.map((rel) => join2(projectRoot, rel));
|
|
261
291
|
const results = [];
|
|
262
292
|
for (const target of targets) {
|
|
263
293
|
const staleMsg = inspectStaleInstall(target, source);
|
|
264
|
-
const result = await copyTextIdempotent(
|
|
294
|
+
const result = await copyTextIdempotent(spec.step, source, target);
|
|
265
295
|
if (staleMsg && result.status === "written") {
|
|
266
296
|
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
267
297
|
}
|
|
268
298
|
results.push(result);
|
|
269
299
|
}
|
|
270
|
-
|
|
300
|
+
if (spec.includeRefFiles) {
|
|
301
|
+
results.push(...await installSkillRefFiles(projectRoot, spec.slug));
|
|
302
|
+
}
|
|
271
303
|
return results;
|
|
272
304
|
}
|
|
305
|
+
async function installFabricArchiveSkill(projectRoot, _options = {}) {
|
|
306
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricArchive);
|
|
307
|
+
}
|
|
273
308
|
async function installFabricReviewSkill(projectRoot, _options = {}) {
|
|
274
|
-
|
|
275
|
-
validateSkillCanonicalSize(source, "fabric-review");
|
|
276
|
-
const targets = SKILL_DESTINATIONS.fabricReview.map((rel) => join2(projectRoot, rel));
|
|
277
|
-
const results = [];
|
|
278
|
-
for (const target of targets) {
|
|
279
|
-
const staleMsg = inspectStaleInstall(target, source);
|
|
280
|
-
const result = await copyTextIdempotent("skill-review", source, target);
|
|
281
|
-
if (staleMsg && result.status === "written") {
|
|
282
|
-
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
283
|
-
}
|
|
284
|
-
results.push(result);
|
|
285
|
-
}
|
|
286
|
-
results.push(...await installSkillRefFiles(projectRoot, "fabric-review"));
|
|
287
|
-
return results;
|
|
309
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricReview);
|
|
288
310
|
}
|
|
289
311
|
async function installFabricImportSkill(projectRoot, _options = {}) {
|
|
290
|
-
|
|
291
|
-
validateSkillCanonicalSize(source, "fabric-import");
|
|
292
|
-
const targets = SKILL_DESTINATIONS.fabricImport.map((rel) => join2(projectRoot, rel));
|
|
293
|
-
const results = [];
|
|
294
|
-
for (const target of targets) {
|
|
295
|
-
const staleMsg = inspectStaleInstall(target, source);
|
|
296
|
-
const result = await copyTextIdempotent("skill-import", source, target);
|
|
297
|
-
if (staleMsg && result.status === "written") {
|
|
298
|
-
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
299
|
-
}
|
|
300
|
-
results.push(result);
|
|
301
|
-
}
|
|
302
|
-
results.push(...await installSkillRefFiles(projectRoot, "fabric-import"));
|
|
303
|
-
return results;
|
|
312
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricImport);
|
|
304
313
|
}
|
|
305
314
|
async function installFabricSyncSkill(projectRoot, _options = {}) {
|
|
306
|
-
|
|
307
|
-
validateSkillCanonicalSize(source, "fabric-sync");
|
|
308
|
-
const targets = SKILL_DESTINATIONS.fabricSync.map((rel) => join2(projectRoot, rel));
|
|
309
|
-
const results = [];
|
|
310
|
-
for (const target of targets) {
|
|
311
|
-
const staleMsg = inspectStaleInstall(target, source);
|
|
312
|
-
const result = await copyTextIdempotent("skill-sync", source, target);
|
|
313
|
-
if (staleMsg && result.status === "written") {
|
|
314
|
-
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
315
|
-
}
|
|
316
|
-
results.push(result);
|
|
317
|
-
}
|
|
318
|
-
return results;
|
|
315
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricSync);
|
|
319
316
|
}
|
|
320
317
|
async function installFabricStoreSkill(projectRoot, _options = {}) {
|
|
321
|
-
|
|
322
|
-
validateSkillCanonicalSize(source, "fabric-store");
|
|
323
|
-
const targets = SKILL_DESTINATIONS.fabricStore.map((rel) => join2(projectRoot, rel));
|
|
324
|
-
const results = [];
|
|
325
|
-
for (const target of targets) {
|
|
326
|
-
const staleMsg = inspectStaleInstall(target, source);
|
|
327
|
-
const result = await copyTextIdempotent("skill-store", source, target);
|
|
328
|
-
if (staleMsg && result.status === "written") {
|
|
329
|
-
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
330
|
-
}
|
|
331
|
-
results.push(result);
|
|
332
|
-
}
|
|
333
|
-
return results;
|
|
318
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricStore);
|
|
334
319
|
}
|
|
335
320
|
async function installFabricAuditSkill(projectRoot, _options = {}) {
|
|
336
|
-
|
|
337
|
-
validateSkillCanonicalSize(source, "fabric-audit");
|
|
338
|
-
const targets = SKILL_DESTINATIONS.fabricAudit.map((rel) => join2(projectRoot, rel));
|
|
339
|
-
const results = [];
|
|
340
|
-
for (const target of targets) {
|
|
341
|
-
const staleMsg = inspectStaleInstall(target, source);
|
|
342
|
-
const result = await copyTextIdempotent("skill-audit", source, target);
|
|
343
|
-
if (staleMsg && result.status === "written") {
|
|
344
|
-
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
345
|
-
}
|
|
346
|
-
results.push(result);
|
|
347
|
-
}
|
|
348
|
-
return results;
|
|
321
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricAudit);
|
|
349
322
|
}
|
|
350
323
|
async function installFabricConnectSkill(projectRoot, _options = {}) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
324
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricConnect);
|
|
325
|
+
}
|
|
326
|
+
function extractSkillMdDescription(skillMd) {
|
|
327
|
+
const fm = skillMd.match(/^---\n([\s\S]*?)\n---/u);
|
|
328
|
+
if (!fm) return "";
|
|
329
|
+
const desc = fm[1].match(/^description:\s*(.+?)\s*$/mu);
|
|
330
|
+
if (!desc) return "";
|
|
331
|
+
return desc[1].replace(/^["'](.+)["']$/u, "$1").trim();
|
|
332
|
+
}
|
|
333
|
+
function extractTriggersClause(description) {
|
|
334
|
+
const m = description.match(/Triggers?\s+([\s\S]+)$/u);
|
|
335
|
+
if (!m) return "";
|
|
336
|
+
return m[1].trim().replace(/[.。]\s*$/u, "").replace(/\|/gu, "\\|");
|
|
337
|
+
}
|
|
338
|
+
function renderRouterIntentBlock(leaves) {
|
|
339
|
+
const rows = leaves.map((l) => `| ${l.triggers} | \`${l.slug}\` |`).join("\n");
|
|
340
|
+
const enumVals = leaves.map((l) => l.slug.replace(/^fabric-/u, "")).join(" | ");
|
|
341
|
+
return [
|
|
342
|
+
ROUTER_INTENT_MARKER_BEGIN,
|
|
343
|
+
ROUTER_INTENT_GENERATED_NOTE,
|
|
344
|
+
"",
|
|
345
|
+
"| \u7528\u6237\u610F\u56FE(leaf description Triggers) | \u4E0B\u6E38 skill |",
|
|
346
|
+
"| --- | --- |",
|
|
347
|
+
rows,
|
|
348
|
+
"",
|
|
349
|
+
`\`S_CLASSIFY\` \u7684 \`task_type\` \u679A\u4E3E:\`${enumVals}\``,
|
|
350
|
+
ROUTER_INTENT_MARKER_END
|
|
351
|
+
].join("\n");
|
|
352
|
+
}
|
|
353
|
+
async function buildRouterSkillSource() {
|
|
354
|
+
const template = await readTemplate(SKILL_ROUTER_TEMPLATE_REL);
|
|
355
|
+
if (!ROUTER_INTENT_REGEX.test(template)) {
|
|
356
|
+
throw new Error(
|
|
357
|
+
`fabric/SKILL.md is missing the ${ROUTER_INTENT_MARKER_BEGIN} \u2026 ${ROUTER_INTENT_MARKER_END} marker pair \u2014 cannot regenerate the Intent Map. This is a Fabric release bug (router template was hand-edited away from the managed-block contract).`
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
const leafSpecs = Object.values(FABRIC_SKILL_INSTALL_SPECS).filter(
|
|
361
|
+
(spec) => spec.slug !== "fabric"
|
|
362
|
+
);
|
|
363
|
+
const leaves = [];
|
|
364
|
+
for (const spec of leafSpecs) {
|
|
365
|
+
const leafMd = await readTemplate(spec.templateRel);
|
|
366
|
+
leaves.push({ slug: spec.slug, triggers: extractTriggersClause(extractSkillMdDescription(leafMd)) });
|
|
367
|
+
}
|
|
368
|
+
return template.replace(ROUTER_INTENT_REGEX, renderRouterIntentBlock(leaves));
|
|
369
|
+
}
|
|
370
|
+
async function installFabricRouterSkill(projectRoot, _options = {}) {
|
|
371
|
+
const source = await buildRouterSkillSource();
|
|
372
|
+
validateSkillCanonicalSize(source, "fabric");
|
|
373
|
+
const spec = FABRIC_SKILL_INSTALL_SPECS.fabricRouter;
|
|
374
|
+
const targets = spec.destinations.map((rel) => join2(projectRoot, rel));
|
|
354
375
|
const results = [];
|
|
355
376
|
for (const target of targets) {
|
|
356
377
|
const staleMsg = inspectStaleInstall(target, source);
|
|
357
|
-
const result = await copyTextIdempotent(
|
|
378
|
+
const result = await copyTextIdempotent(spec.step, source, target);
|
|
358
379
|
if (staleMsg && result.status === "written") {
|
|
359
380
|
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
360
381
|
}
|
|
@@ -647,16 +668,6 @@ async function mergeCodexHookConfig(projectRoot, _options = {}) {
|
|
|
647
668
|
[...HOOK_CONFIG_ARRAY_PATHS.codex]
|
|
648
669
|
);
|
|
649
670
|
}
|
|
650
|
-
async function mergeCursorHookConfig(projectRoot, _options = {}) {
|
|
651
|
-
const fragment = await readJsonTemplate(CURSOR_HOOK_CONFIG_TEMPLATE_REL);
|
|
652
|
-
const targetPath = join2(projectRoot, HOOK_CONFIG_TARGETS.cursor);
|
|
653
|
-
return mergeJsonIdempotent(
|
|
654
|
-
"cursor-hook-config",
|
|
655
|
-
targetPath,
|
|
656
|
-
fragment,
|
|
657
|
-
[...HOOK_CONFIG_ARRAY_PATHS.cursor]
|
|
658
|
-
);
|
|
659
|
-
}
|
|
660
671
|
function buildManagedBlockBody(targetRoot) {
|
|
661
672
|
const snapshotPath = fabricAgentsSnapshotPath(targetRoot);
|
|
662
673
|
const snapshot = readFileSync2(snapshotPath, "utf8");
|
|
@@ -673,13 +684,6 @@ function wrapInBootstrapMarkers(body) {
|
|
|
673
684
|
${body}
|
|
674
685
|
${BOOTSTRAP_MARKER_END}`;
|
|
675
686
|
}
|
|
676
|
-
function stripLegacyKnowledgeBaseSection(existing) {
|
|
677
|
-
const match = existing.match(LEGACY_KB_REGEX);
|
|
678
|
-
if (match === null) return existing;
|
|
679
|
-
const before = existing.slice(0, match.index ?? 0);
|
|
680
|
-
const after = existing.slice((match.index ?? 0) + match[0].length);
|
|
681
|
-
return `${before}${after.replace(/^\r?\n/, "")}`;
|
|
682
|
-
}
|
|
683
687
|
var CLAUDE_BOOTSTRAP_HEADER = "# Project Knowledge";
|
|
684
688
|
var CLAUDE_AGENTS_IMPORT_LINE = "@.fabric/AGENTS.md";
|
|
685
689
|
var CLAUDE_PROJECT_RULES_IMPORT_LINE = "@.fabric/project-rules.md";
|
|
@@ -702,7 +706,7 @@ async function writeClaudeBootstrapThinShell(targetRoot, _options = {}) {
|
|
|
702
706
|
};
|
|
703
707
|
}
|
|
704
708
|
}
|
|
705
|
-
let next =
|
|
709
|
+
let next = existing;
|
|
706
710
|
if (!projectRulesPresent) {
|
|
707
711
|
next = removeImportLine(next, CLAUDE_PROJECT_RULES_IMPORT_LINE);
|
|
708
712
|
}
|
|
@@ -777,23 +781,22 @@ async function writeCodexBootstrapManagedBlock(targetRoot, _options = {}) {
|
|
|
777
781
|
}
|
|
778
782
|
const body = buildManagedBlockBody(targetRoot);
|
|
779
783
|
const managedBlock = wrapInBootstrapMarkers(body);
|
|
780
|
-
const stripped = stripLegacyKnowledgeBaseSection(existing);
|
|
781
784
|
let next;
|
|
782
|
-
const match =
|
|
785
|
+
const match = existing.match(BOOTSTRAP_REGEX);
|
|
783
786
|
if (match !== null) {
|
|
784
|
-
const before =
|
|
785
|
-
const after =
|
|
787
|
+
const before = existing.slice(0, match.index ?? 0);
|
|
788
|
+
const after = existing.slice((match.index ?? 0) + match[0].length);
|
|
786
789
|
const cleaned = `${before}${after.replace(/^\r?\n/, "")}`;
|
|
787
790
|
const trailingNewline = cleaned.length === 0 || cleaned.endsWith("\n") ? "" : "\n";
|
|
788
791
|
next = `${cleaned}${trailingNewline}${cleaned.length === 0 ? "" : "\n"}${managedBlock}
|
|
789
792
|
`;
|
|
790
793
|
} else {
|
|
791
|
-
if (
|
|
794
|
+
if (existing.length === 0) {
|
|
792
795
|
next = `${managedBlock}
|
|
793
796
|
`;
|
|
794
797
|
} else {
|
|
795
|
-
const trailingNewline =
|
|
796
|
-
next = `${
|
|
798
|
+
const trailingNewline = existing.endsWith("\n") ? "" : "\n";
|
|
799
|
+
next = `${existing}${trailingNewline}
|
|
797
800
|
${managedBlock}
|
|
798
801
|
`;
|
|
799
802
|
}
|
|
@@ -814,55 +817,6 @@ ${managedBlock}
|
|
|
814
817
|
};
|
|
815
818
|
}
|
|
816
819
|
}
|
|
817
|
-
var CURSOR_RULE_FRONT_MATTER = "---\nalwaysApply: true\ndescription: Fabric Protocol bootstrap rules\n---\n\n";
|
|
818
|
-
var CURSOR_LEGACY_FLAT_FILE_REL = join2(".cursor", "rules");
|
|
819
|
-
var CURSOR_BOOTSTRAP_MDC_REL = join2(".cursor", "rules", "fabric-bootstrap.mdc");
|
|
820
|
-
async function writeCursorBootstrapManagedBlock(targetRoot, _options = {}) {
|
|
821
|
-
const step = "bootstrap-cursor";
|
|
822
|
-
const target = join2(targetRoot, CURSOR_BOOTSTRAP_MDC_REL);
|
|
823
|
-
const legacyFlatFile = join2(targetRoot, CURSOR_LEGACY_FLAT_FILE_REL);
|
|
824
|
-
try {
|
|
825
|
-
if (existsSync2(legacyFlatFile)) {
|
|
826
|
-
const stat = statSync(legacyFlatFile);
|
|
827
|
-
if (stat.isFile()) {
|
|
828
|
-
await rm(legacyFlatFile, { force: true });
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
} catch {
|
|
832
|
-
}
|
|
833
|
-
const body = buildManagedBlockBody(targetRoot);
|
|
834
|
-
const managedBlock = wrapInBootstrapMarkers(body);
|
|
835
|
-
const expected = `${CURSOR_RULE_FRONT_MATTER}${managedBlock}
|
|
836
|
-
`;
|
|
837
|
-
let existing = "";
|
|
838
|
-
if (existsSync2(target)) {
|
|
839
|
-
try {
|
|
840
|
-
existing = await readFile(target, "utf8");
|
|
841
|
-
} catch (error) {
|
|
842
|
-
return {
|
|
843
|
-
step,
|
|
844
|
-
path: target,
|
|
845
|
-
status: "error",
|
|
846
|
-
message: error instanceof Error ? error.message : String(error)
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
if (existing === expected) {
|
|
851
|
-
return { step, path: target, status: "skipped", message: "up-to-date" };
|
|
852
|
-
}
|
|
853
|
-
try {
|
|
854
|
-
await mkdir2(dirname2(target), { recursive: true });
|
|
855
|
-
await atomicWriteText2(target, expected);
|
|
856
|
-
return { step, path: target, status: "written" };
|
|
857
|
-
} catch (error) {
|
|
858
|
-
return {
|
|
859
|
-
step,
|
|
860
|
-
path: target,
|
|
861
|
-
status: "error",
|
|
862
|
-
message: error instanceof Error ? error.message : String(error)
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
820
|
async function copyTextIdempotent(step, source, target) {
|
|
867
821
|
if (existsSync2(target)) {
|
|
868
822
|
try {
|
|
@@ -881,6 +835,17 @@ var FABRIC_HOOK_SCRIPT_BASENAMES = /* @__PURE__ */ new Set([
|
|
|
881
835
|
"fabric-hint.cjs",
|
|
882
836
|
"knowledge-hint-broad.cjs",
|
|
883
837
|
"knowledge-hint-narrow.cjs",
|
|
838
|
+
// dual-sink W5-1: the strip set must enumerate the COMPLETE fabric-owned hook
|
|
839
|
+
// surface — same set as FABRIC_HOOK_COMMAND_PATHS. Otherwise a matcher change
|
|
840
|
+
// in the template (e.g. adding `apply_patch` to the Codex PreToolUse/PostToolUse
|
|
841
|
+
// matchers) silently fails to propagate on upgrade: stripStaleHookEntries
|
|
842
|
+
// leaves the un-listed entry in place, and the subsequent append-with-dedupe
|
|
843
|
+
// matches it by `command` and SKIPS the new-matcher fragment, preserving the
|
|
844
|
+
// stale matcher. Listing these three makes the canonical template entry the
|
|
845
|
+
// sole survivor on every re-install, so matcher edits actually sync.
|
|
846
|
+
"cite-policy-evict.cjs",
|
|
847
|
+
"post-tooluse-mutation.cjs",
|
|
848
|
+
"session-end-marker.cjs",
|
|
884
849
|
// rc.5 TASK-010 rename — old hook scripts that pre-upgrade workspaces
|
|
885
850
|
// may still have registered. Sweeping them prevents the double-fire
|
|
886
851
|
// documented in audit §2.6.
|
|
@@ -1019,7 +984,6 @@ export {
|
|
|
1019
984
|
HOOK_CONFIG_TARGETS,
|
|
1020
985
|
HOOK_CONFIG_ARRAY_PATHS,
|
|
1021
986
|
FABRIC_HOOK_COMMAND_PATHS,
|
|
1022
|
-
readFabricLanguagePreference,
|
|
1023
987
|
installFabricArchiveSkill,
|
|
1024
988
|
installFabricReviewSkill,
|
|
1025
989
|
installFabricImportSkill,
|
|
@@ -1027,6 +991,7 @@ export {
|
|
|
1027
991
|
installFabricStoreSkill,
|
|
1028
992
|
installFabricAuditSkill,
|
|
1029
993
|
installFabricConnectSkill,
|
|
994
|
+
installFabricRouterSkill,
|
|
1030
995
|
cleanupDeprecatedSkills,
|
|
1031
996
|
installSharedSkillLib,
|
|
1032
997
|
installArchiveHintHook,
|
|
@@ -1038,8 +1003,6 @@ export {
|
|
|
1038
1003
|
installHookLibs,
|
|
1039
1004
|
mergeClaudeCodeHookConfig,
|
|
1040
1005
|
mergeCodexHookConfig,
|
|
1041
|
-
mergeCursorHookConfig,
|
|
1042
1006
|
writeClaudeBootstrapThinShell,
|
|
1043
|
-
writeCodexBootstrapManagedBlock
|
|
1044
|
-
writeCursorBootstrapManagedBlock
|
|
1007
|
+
writeCodexBootstrapManagedBlock
|
|
1045
1008
|
};
|