@fenglimg/fabric-cli 2.1.0-rc.2 → 2.2.0-rc.10
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-27HK6H5Y.js +69 -0
- package/dist/{chunk-BATF4PEJ.js → chunk-2KBCTMID.js} +31 -8
- package/dist/chunk-3D7B2UAZ.js +149 -0
- package/dist/{chunk-MF3OTILQ.js → chunk-3IOLS5EK.js} +48 -42
- package/dist/{plan-context-hint-FC6P3WFE.js → chunk-722JU5BP.js} +52 -12
- package/dist/{chunk-F46ORPOA.js → chunk-7ZDXBOOU.js} +271 -166
- package/dist/{doctor-QVNPHLJK.js → chunk-E7HJUU34.js} +248 -72
- package/dist/chunk-EOT63RDH.js +36 -0
- package/dist/chunk-FNHDQTPC.js +16 -0
- package/dist/chunk-HORSMSZL.js +26 -0
- package/dist/chunk-NLNH64A3.js +43 -0
- package/dist/{chunk-WU6GAPKH.js → chunk-PTGQAZEW.js} +12 -4
- package/dist/chunk-QFIVFZRH.js +13 -0
- package/dist/chunk-QPAW6IYT.js +387 -0
- package/dist/{chunk-COI5VDFU.js → chunk-WA3DYGSY.js} +1 -2
- package/dist/{config-XJIPZNUP.js → config-A3LTECAY.js} +4 -3
- package/dist/context-UJCGYOT6.js +117 -0
- package/dist/doctor-MDTZWKBK.js +24 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +167 -16
- package/dist/info-7FKBTMVO.js +139 -0
- package/dist/install-v2-RINEA24K.js +3279 -0
- package/dist/{metrics-ACEQFPDU.js → metrics-HMFH4YHK.js} +22 -9
- package/dist/{onboard-coverage-MFCAEBDO.js → onboard-coverage-XSG77LL3.js} +48 -27
- package/dist/plan-context-hint-5TNGH3R4.js +12 -0
- package/dist/scope-explain-HLJZ2M33.js +48 -0
- package/dist/status-4R3TM4FJ.js +37 -0
- package/dist/store-HOCORVL3.js +563 -0
- package/dist/sync-DT5UJMMR.js +418 -0
- package/dist/{uninstall-TAXSUSKH.js → uninstall-IFN2KYBK.js} +128 -140
- package/dist/whoami-ITGEFWH4.js +49 -0
- package/package.json +7 -5
- package/templates/hooks/cite-policy-evict.cjs +412 -160
- package/templates/hooks/configs/README.md +14 -27
- package/templates/hooks/configs/claude-code.json +17 -2
- package/templates/hooks/configs/codex-hooks.json +15 -3
- package/templates/hooks/fabric-hint.cjs +573 -180
- package/templates/hooks/knowledge-hint-broad.cjs +648 -190
- package/templates/hooks/knowledge-hint-narrow.cjs +123 -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/injection-log.cjs +91 -0
- package/templates/hooks/lib/nudge-policy.cjs +117 -0
- package/templates/hooks/lib/state-store.cjs +90 -11
- package/templates/hooks/post-tooluse-mutation.cjs +386 -0
- package/templates/hooks/session-end-marker.cjs +140 -0
- package/templates/skills/fabric/SKILL.md +100 -0
- package/templates/skills/fabric-archive/SKILL.md +35 -24
- 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 +63 -0
- package/templates/skills/fabric-connect/SKILL.md +48 -0
- 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 +16 -5
- package/templates/skills/fabric-review/ref/cite-contract.md +56 -0
- 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 +44 -0
- package/templates/skills/fabric-sync/SKILL.md +1 -1
- package/templates/skills/lib/shared-policy.md +2 -2
- package/dist/chunk-HFQVXY6P.js +0 -86
- package/dist/chunk-L4Q55UC4.js +0 -52
- package/dist/chunk-LFIKMVY7.js +0 -27
- package/dist/chunk-PWLW3B57.js +0 -18
- package/dist/chunk-RYAFBNES.js +0 -33
- package/dist/chunk-T5RPGCCM.js +0 -40
- package/dist/chunk-WWNXR34K.js +0 -49
- package/dist/install-2HDO5FTQ.js +0 -2683
- package/dist/scope-explain-2F2R5URO.js +0 -33
- package/dist/status-GLQWLWH6.js +0 -23
- package/dist/store-XTSE5TY6.js +0 -105
- package/dist/sync-BJCWDPNC.js +0 -245
- package/dist/whoami-B6AEMSEV.js +0 -31
- package/templates/hooks/configs/cursor-hooks.json +0 -18
- 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,45 +25,61 @@ 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";
|
|
57
63
|
var SKILL_SYNC_TEMPLATE_REL = "skills/fabric-sync/SKILL.md";
|
|
64
|
+
var SKILL_STORE_TEMPLATE_REL = "skills/fabric-store/SKILL.md";
|
|
65
|
+
var SKILL_AUDIT_TEMPLATE_REL = "skills/fabric-audit/SKILL.md";
|
|
66
|
+
var SKILL_CONNECT_TEMPLATE_REL = "skills/fabric-connect/SKILL.md";
|
|
58
67
|
var HOOK_SCRIPT_TEMPLATE_REL = "hooks/fabric-hint.cjs";
|
|
59
68
|
var HOOK_BROAD_SCRIPT_TEMPLATE_REL = "hooks/knowledge-hint-broad.cjs";
|
|
60
69
|
var HOOK_NARROW_SCRIPT_TEMPLATE_REL = "hooks/knowledge-hint-narrow.cjs";
|
|
61
70
|
var HOOK_CITE_EVICT_SCRIPT_TEMPLATE_REL = "hooks/cite-policy-evict.cjs";
|
|
71
|
+
var HOOK_SESSION_END_SCRIPT_TEMPLATE_REL = "hooks/session-end-marker.cjs";
|
|
72
|
+
var HOOK_POST_TOOLUSE_SCRIPT_TEMPLATE_REL = "hooks/post-tooluse-mutation.cjs";
|
|
62
73
|
var HOOK_LIB_TEMPLATE_DIR_REL = "hooks/lib";
|
|
63
74
|
var CLAUDE_HOOK_CONFIG_TEMPLATE_REL = "hooks/configs/claude-code.json";
|
|
64
75
|
var CODEX_HOOK_CONFIG_TEMPLATE_REL = "hooks/configs/codex-hooks.json";
|
|
65
|
-
var CURSOR_HOOK_CONFIG_TEMPLATE_REL = "hooks/configs/cursor-hooks.json";
|
|
66
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
|
+
],
|
|
67
83
|
fabricArchive: [
|
|
68
84
|
".claude/skills/fabric-archive/SKILL.md",
|
|
69
85
|
".codex/skills/fabric-archive/SKILL.md"
|
|
@@ -81,8 +97,78 @@ var SKILL_DESTINATIONS = {
|
|
|
81
97
|
fabricSync: [
|
|
82
98
|
".claude/skills/fabric-sync/SKILL.md",
|
|
83
99
|
".codex/skills/fabric-sync/SKILL.md"
|
|
100
|
+
],
|
|
101
|
+
// v2.1 ADJ-NEWN-1/#4: fabric-store knowledge-store ops skill, same 2-client
|
|
102
|
+
// coverage as the sibling skills.
|
|
103
|
+
fabricStore: [
|
|
104
|
+
".claude/skills/fabric-store/SKILL.md",
|
|
105
|
+
".codex/skills/fabric-store/SKILL.md"
|
|
106
|
+
],
|
|
107
|
+
// v2.2 SK1-audit (W2-T5): fabric-audit semantic-deprecation skill, same
|
|
108
|
+
// 2-client coverage as the sibling skills.
|
|
109
|
+
fabricAudit: [
|
|
110
|
+
".claude/skills/fabric-audit/SKILL.md",
|
|
111
|
+
".codex/skills/fabric-audit/SKILL.md"
|
|
112
|
+
],
|
|
113
|
+
// v2.2 SK2-connect (W3-T2): fabric-connect knowledge-graph relation skill.
|
|
114
|
+
fabricConnect: [
|
|
115
|
+
".claude/skills/fabric-connect/SKILL.md",
|
|
116
|
+
".codex/skills/fabric-connect/SKILL.md"
|
|
84
117
|
]
|
|
85
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
|
+
};
|
|
86
172
|
var DEPRECATED_SKILL_DIRS = [
|
|
87
173
|
".claude/skills/fabric-init",
|
|
88
174
|
".codex/skills/fabric-init"
|
|
@@ -90,82 +176,86 @@ var DEPRECATED_SKILL_DIRS = [
|
|
|
90
176
|
var HOOK_SCRIPT_DESTINATIONS = {
|
|
91
177
|
fabricHint: [
|
|
92
178
|
".claude/hooks/fabric-hint.cjs",
|
|
93
|
-
".codex/hooks/fabric-hint.cjs"
|
|
94
|
-
".cursor/hooks/fabric-hint.cjs"
|
|
179
|
+
".codex/hooks/fabric-hint.cjs"
|
|
95
180
|
],
|
|
96
181
|
knowledgeHintBroad: [
|
|
97
182
|
".claude/hooks/knowledge-hint-broad.cjs",
|
|
98
|
-
".codex/hooks/knowledge-hint-broad.cjs"
|
|
99
|
-
".cursor/hooks/knowledge-hint-broad.cjs"
|
|
183
|
+
".codex/hooks/knowledge-hint-broad.cjs"
|
|
100
184
|
],
|
|
101
185
|
knowledgeHintNarrow: [
|
|
102
186
|
".claude/hooks/knowledge-hint-narrow.cjs",
|
|
103
|
-
".codex/hooks/knowledge-hint-narrow.cjs"
|
|
104
|
-
".cursor/hooks/knowledge-hint-narrow.cjs"
|
|
187
|
+
".codex/hooks/knowledge-hint-narrow.cjs"
|
|
105
188
|
],
|
|
106
189
|
// v2.0.0-rc.34 TASK-06: Claude Code — UserPromptSubmit cite-policy long-
|
|
107
190
|
// session evict sidecar.
|
|
108
|
-
// v2.0.0-rc.37 NEW-21: extended to Codex
|
|
109
|
-
//
|
|
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-
|
|
110
193
|
// evict.cjs runs in "SessionStart mode" (one-shot stderr emit per session
|
|
111
194
|
// boot, no turn-counter). Cadence is lower than Claude Code's per-prompt
|
|
112
|
-
// window but strictly better than 0 (rc.32 baseline measured Codex
|
|
195
|
+
// window but strictly better than 0 (rc.32 baseline measured Codex
|
|
113
196
|
// at 3.1% cite coverage when no cite-reminder surface existed).
|
|
114
197
|
citePolicyEvict: [
|
|
115
198
|
".claude/hooks/cite-policy-evict.cjs",
|
|
116
|
-
".codex/hooks/cite-policy-evict.cjs"
|
|
117
|
-
|
|
199
|
+
".codex/hooks/cite-policy-evict.cjs"
|
|
200
|
+
],
|
|
201
|
+
// lifecycle-refactor W2-T2: SessionEnd marker hook — both clients.
|
|
202
|
+
sessionEndMarker: [
|
|
203
|
+
".claude/hooks/session-end-marker.cjs",
|
|
204
|
+
".codex/hooks/session-end-marker.cjs"
|
|
205
|
+
],
|
|
206
|
+
// lifecycle-refactor W2-T3: PostToolUse mutation marker hook — both.
|
|
207
|
+
postTooluseMutation: [
|
|
208
|
+
".claude/hooks/post-tooluse-mutation.cjs",
|
|
209
|
+
".codex/hooks/post-tooluse-mutation.cjs"
|
|
118
210
|
]
|
|
119
211
|
};
|
|
120
212
|
var HOOK_LIB_DESTINATIONS = [
|
|
121
213
|
".claude/hooks/lib",
|
|
122
|
-
".codex/hooks/lib"
|
|
123
|
-
".cursor/hooks/lib"
|
|
214
|
+
".codex/hooks/lib"
|
|
124
215
|
];
|
|
125
216
|
var HOOK_CONFIG_TARGETS = {
|
|
126
217
|
claudeCode: ".claude/settings.json",
|
|
127
|
-
codex: ".codex/hooks.json"
|
|
128
|
-
cursor: ".cursor/hooks.json"
|
|
218
|
+
codex: ".codex/hooks.json"
|
|
129
219
|
};
|
|
130
220
|
var HOOK_CONFIG_ARRAY_PATHS = {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
221
|
+
// F2: "hooks.UserPromptSubmit" MUST be listed — the Claude Code template
|
|
222
|
+
// ships a UserPromptSubmit cite-policy hook, so without this path deepMerge
|
|
223
|
+
// array-REPLACEs (instead of append-with-dedupe) on re-install, silently
|
|
224
|
+
// clobbering any user-defined UserPromptSubmit hook.
|
|
225
|
+
// lifecycle-refactor W2-T2/T3: PostToolUse + SessionEnd arrays added so
|
|
226
|
+
// deepMerge append-with-dedupes them on re-install (omitting them would
|
|
227
|
+
// array-REPLACE, clobbering any user-defined entries in those slots).
|
|
228
|
+
claudeCode: [
|
|
229
|
+
"hooks.Stop",
|
|
230
|
+
"hooks.SessionStart",
|
|
231
|
+
"hooks.PreToolUse",
|
|
232
|
+
"hooks.UserPromptSubmit",
|
|
233
|
+
"hooks.PostToolUse",
|
|
234
|
+
"hooks.SessionEnd"
|
|
235
|
+
],
|
|
236
|
+
codex: ["events.Stop", "events.SessionStart", "events.PreToolUse", "events.PostToolUse", "events.SessionEnd"]
|
|
134
237
|
};
|
|
135
238
|
var FABRIC_HOOK_COMMAND_PATHS = {
|
|
136
239
|
claudeCode: {
|
|
137
240
|
fabricHint: "${CLAUDE_PROJECT_DIR}/.claude/hooks/fabric-hint.cjs",
|
|
138
241
|
knowledgeHintBroad: "${CLAUDE_PROJECT_DIR}/.claude/hooks/knowledge-hint-broad.cjs",
|
|
139
|
-
knowledgeHintNarrow: "${CLAUDE_PROJECT_DIR}/.claude/hooks/knowledge-hint-narrow.cjs"
|
|
242
|
+
knowledgeHintNarrow: "${CLAUDE_PROJECT_DIR}/.claude/hooks/knowledge-hint-narrow.cjs",
|
|
243
|
+
// F3: the UserPromptSubmit cite-policy-evict hook must be a known fabric
|
|
244
|
+
// command so uninstall prunes it (matches the literal in claude-code.json).
|
|
245
|
+
citePolicyEvict: "${CLAUDE_PROJECT_DIR}/.claude/hooks/cite-policy-evict.cjs",
|
|
246
|
+
// lifecycle-refactor W2-T2/T3: SessionEnd + PostToolUse marker hooks.
|
|
247
|
+
sessionEndMarker: "${CLAUDE_PROJECT_DIR}/.claude/hooks/session-end-marker.cjs",
|
|
248
|
+
postTooluseMutation: "${CLAUDE_PROJECT_DIR}/.claude/hooks/post-tooluse-mutation.cjs"
|
|
140
249
|
},
|
|
141
250
|
codex: {
|
|
142
251
|
fabricHint: '"$(git rev-parse --show-toplevel)/.codex/hooks/fabric-hint.cjs"',
|
|
143
252
|
knowledgeHintBroad: '"$(git rev-parse --show-toplevel)/.codex/hooks/knowledge-hint-broad.cjs"',
|
|
144
|
-
knowledgeHintNarrow: '"$(git rev-parse --show-toplevel)/.codex/hooks/knowledge-hint-narrow.cjs"'
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
knowledgeHintBroad: ".cursor/hooks/knowledge-hint-broad.cjs",
|
|
149
|
-
knowledgeHintNarrow: ".cursor/hooks/knowledge-hint-narrow.cjs"
|
|
253
|
+
knowledgeHintNarrow: '"$(git rev-parse --show-toplevel)/.codex/hooks/knowledge-hint-narrow.cjs"',
|
|
254
|
+
citePolicyEvict: '"$(git rev-parse --show-toplevel)/.codex/hooks/cite-policy-evict.cjs"',
|
|
255
|
+
sessionEndMarker: '"$(git rev-parse --show-toplevel)/.codex/hooks/session-end-marker.cjs"',
|
|
256
|
+
postTooluseMutation: '"$(git rev-parse --show-toplevel)/.codex/hooks/post-tooluse-mutation.cjs"'
|
|
150
257
|
}
|
|
151
258
|
};
|
|
152
|
-
function readFabricLanguagePreference(projectRoot) {
|
|
153
|
-
const configPath = join2(projectRoot, ".fabric", "fabric-config.json");
|
|
154
|
-
if (!existsSync2(configPath)) {
|
|
155
|
-
return "match-existing";
|
|
156
|
-
}
|
|
157
|
-
try {
|
|
158
|
-
const raw = readFileSync2(configPath, "utf8");
|
|
159
|
-
const parsed = JSON.parse(raw);
|
|
160
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
161
|
-
return "match-existing";
|
|
162
|
-
}
|
|
163
|
-
const value = parsed["fabric_language"];
|
|
164
|
-
return typeof value === "string" && value.length > 0 ? value : "match-existing";
|
|
165
|
-
} catch {
|
|
166
|
-
return "match-existing";
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
259
|
var SKILL_TOKEN_ERROR_TOKENS = 1e4;
|
|
170
260
|
var STALE_INSTALL_RATIO = 1.5;
|
|
171
261
|
function estimateSkillTokens(text) {
|
|
@@ -194,62 +284,98 @@ function inspectStaleInstall(target, source) {
|
|
|
194
284
|
}
|
|
195
285
|
return null;
|
|
196
286
|
}
|
|
197
|
-
async function
|
|
198
|
-
const source = await readTemplate(
|
|
199
|
-
validateSkillCanonicalSize(source,
|
|
200
|
-
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));
|
|
201
291
|
const results = [];
|
|
202
292
|
for (const target of targets) {
|
|
203
293
|
const staleMsg = inspectStaleInstall(target, source);
|
|
204
|
-
const result = await copyTextIdempotent(
|
|
294
|
+
const result = await copyTextIdempotent(spec.step, source, target);
|
|
205
295
|
if (staleMsg && result.status === "written") {
|
|
206
296
|
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
207
297
|
}
|
|
208
298
|
results.push(result);
|
|
209
299
|
}
|
|
210
|
-
|
|
300
|
+
if (spec.includeRefFiles) {
|
|
301
|
+
results.push(...await installSkillRefFiles(projectRoot, spec.slug));
|
|
302
|
+
}
|
|
211
303
|
return results;
|
|
212
304
|
}
|
|
305
|
+
async function installFabricArchiveSkill(projectRoot, _options = {}) {
|
|
306
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricArchive);
|
|
307
|
+
}
|
|
213
308
|
async function installFabricReviewSkill(projectRoot, _options = {}) {
|
|
214
|
-
|
|
215
|
-
validateSkillCanonicalSize(source, "fabric-review");
|
|
216
|
-
const targets = SKILL_DESTINATIONS.fabricReview.map((rel) => join2(projectRoot, rel));
|
|
217
|
-
const results = [];
|
|
218
|
-
for (const target of targets) {
|
|
219
|
-
const staleMsg = inspectStaleInstall(target, source);
|
|
220
|
-
const result = await copyTextIdempotent("skill-review", source, target);
|
|
221
|
-
if (staleMsg && result.status === "written") {
|
|
222
|
-
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
223
|
-
}
|
|
224
|
-
results.push(result);
|
|
225
|
-
}
|
|
226
|
-
results.push(...await installSkillRefFiles(projectRoot, "fabric-review"));
|
|
227
|
-
return results;
|
|
309
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricReview);
|
|
228
310
|
}
|
|
229
311
|
async function installFabricImportSkill(projectRoot, _options = {}) {
|
|
230
|
-
|
|
231
|
-
validateSkillCanonicalSize(source, "fabric-import");
|
|
232
|
-
const targets = SKILL_DESTINATIONS.fabricImport.map((rel) => join2(projectRoot, rel));
|
|
233
|
-
const results = [];
|
|
234
|
-
for (const target of targets) {
|
|
235
|
-
const staleMsg = inspectStaleInstall(target, source);
|
|
236
|
-
const result = await copyTextIdempotent("skill-import", source, target);
|
|
237
|
-
if (staleMsg && result.status === "written") {
|
|
238
|
-
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
239
|
-
}
|
|
240
|
-
results.push(result);
|
|
241
|
-
}
|
|
242
|
-
results.push(...await installSkillRefFiles(projectRoot, "fabric-import"));
|
|
243
|
-
return results;
|
|
312
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricImport);
|
|
244
313
|
}
|
|
245
314
|
async function installFabricSyncSkill(projectRoot, _options = {}) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
315
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricSync);
|
|
316
|
+
}
|
|
317
|
+
async function installFabricStoreSkill(projectRoot, _options = {}) {
|
|
318
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricStore);
|
|
319
|
+
}
|
|
320
|
+
async function installFabricAuditSkill(projectRoot, _options = {}) {
|
|
321
|
+
return installFabricSkill(projectRoot, FABRIC_SKILL_INSTALL_SPECS.fabricAudit);
|
|
322
|
+
}
|
|
323
|
+
async function installFabricConnectSkill(projectRoot, _options = {}) {
|
|
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));
|
|
249
375
|
const results = [];
|
|
250
376
|
for (const target of targets) {
|
|
251
377
|
const staleMsg = inspectStaleInstall(target, source);
|
|
252
|
-
const result = await copyTextIdempotent(
|
|
378
|
+
const result = await copyTextIdempotent(spec.step, source, target);
|
|
253
379
|
if (staleMsg && result.status === "written") {
|
|
254
380
|
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
255
381
|
}
|
|
@@ -443,6 +569,38 @@ async function installCitePolicyEvictHook(projectRoot, _options = {}) {
|
|
|
443
569
|
}
|
|
444
570
|
return results;
|
|
445
571
|
}
|
|
572
|
+
async function installSessionEndMarkerHook(projectRoot, _options = {}) {
|
|
573
|
+
const source = await readTemplate(HOOK_SESSION_END_SCRIPT_TEMPLATE_REL);
|
|
574
|
+
const targets = HOOK_SCRIPT_DESTINATIONS.sessionEndMarker.map((rel) => join2(projectRoot, rel));
|
|
575
|
+
const results = [];
|
|
576
|
+
for (const target of targets) {
|
|
577
|
+
const result = await copyTextIdempotent("hook-session-end-script", source, target);
|
|
578
|
+
if (result.status === "written" && process.platform !== "win32") {
|
|
579
|
+
try {
|
|
580
|
+
chmodSync(target, 493);
|
|
581
|
+
} catch {
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
results.push(result);
|
|
585
|
+
}
|
|
586
|
+
return results;
|
|
587
|
+
}
|
|
588
|
+
async function installPostTooluseMutationHook(projectRoot, _options = {}) {
|
|
589
|
+
const source = await readTemplate(HOOK_POST_TOOLUSE_SCRIPT_TEMPLATE_REL);
|
|
590
|
+
const targets = HOOK_SCRIPT_DESTINATIONS.postTooluseMutation.map((rel) => join2(projectRoot, rel));
|
|
591
|
+
const results = [];
|
|
592
|
+
for (const target of targets) {
|
|
593
|
+
const result = await copyTextIdempotent("hook-post-tooluse-script", source, target);
|
|
594
|
+
if (result.status === "written" && process.platform !== "win32") {
|
|
595
|
+
try {
|
|
596
|
+
chmodSync(target, 493);
|
|
597
|
+
} catch {
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
results.push(result);
|
|
601
|
+
}
|
|
602
|
+
return results;
|
|
603
|
+
}
|
|
446
604
|
async function installHookLibs(projectRoot, _options = {}) {
|
|
447
605
|
const libTemplateDir = findTemplatePath(HOOK_LIB_TEMPLATE_DIR_REL);
|
|
448
606
|
let libFiles;
|
|
@@ -510,16 +668,6 @@ async function mergeCodexHookConfig(projectRoot, _options = {}) {
|
|
|
510
668
|
[...HOOK_CONFIG_ARRAY_PATHS.codex]
|
|
511
669
|
);
|
|
512
670
|
}
|
|
513
|
-
async function mergeCursorHookConfig(projectRoot, _options = {}) {
|
|
514
|
-
const fragment = await readJsonTemplate(CURSOR_HOOK_CONFIG_TEMPLATE_REL);
|
|
515
|
-
const targetPath = join2(projectRoot, HOOK_CONFIG_TARGETS.cursor);
|
|
516
|
-
return mergeJsonIdempotent(
|
|
517
|
-
"cursor-hook-config",
|
|
518
|
-
targetPath,
|
|
519
|
-
fragment,
|
|
520
|
-
[...HOOK_CONFIG_ARRAY_PATHS.cursor]
|
|
521
|
-
);
|
|
522
|
-
}
|
|
523
671
|
function buildManagedBlockBody(targetRoot) {
|
|
524
672
|
const snapshotPath = fabricAgentsSnapshotPath(targetRoot);
|
|
525
673
|
const snapshot = readFileSync2(snapshotPath, "utf8");
|
|
@@ -536,13 +684,6 @@ function wrapInBootstrapMarkers(body) {
|
|
|
536
684
|
${body}
|
|
537
685
|
${BOOTSTRAP_MARKER_END}`;
|
|
538
686
|
}
|
|
539
|
-
function stripLegacyKnowledgeBaseSection(existing) {
|
|
540
|
-
const match = existing.match(LEGACY_KB_REGEX);
|
|
541
|
-
if (match === null) return existing;
|
|
542
|
-
const before = existing.slice(0, match.index ?? 0);
|
|
543
|
-
const after = existing.slice((match.index ?? 0) + match[0].length);
|
|
544
|
-
return `${before}${after.replace(/^\r?\n/, "")}`;
|
|
545
|
-
}
|
|
546
687
|
var CLAUDE_BOOTSTRAP_HEADER = "# Project Knowledge";
|
|
547
688
|
var CLAUDE_AGENTS_IMPORT_LINE = "@.fabric/AGENTS.md";
|
|
548
689
|
var CLAUDE_PROJECT_RULES_IMPORT_LINE = "@.fabric/project-rules.md";
|
|
@@ -565,7 +706,7 @@ async function writeClaudeBootstrapThinShell(targetRoot, _options = {}) {
|
|
|
565
706
|
};
|
|
566
707
|
}
|
|
567
708
|
}
|
|
568
|
-
let next =
|
|
709
|
+
let next = existing;
|
|
569
710
|
if (!projectRulesPresent) {
|
|
570
711
|
next = removeImportLine(next, CLAUDE_PROJECT_RULES_IMPORT_LINE);
|
|
571
712
|
}
|
|
@@ -640,23 +781,22 @@ async function writeCodexBootstrapManagedBlock(targetRoot, _options = {}) {
|
|
|
640
781
|
}
|
|
641
782
|
const body = buildManagedBlockBody(targetRoot);
|
|
642
783
|
const managedBlock = wrapInBootstrapMarkers(body);
|
|
643
|
-
const stripped = stripLegacyKnowledgeBaseSection(existing);
|
|
644
784
|
let next;
|
|
645
|
-
const match =
|
|
785
|
+
const match = existing.match(BOOTSTRAP_REGEX);
|
|
646
786
|
if (match !== null) {
|
|
647
|
-
const before =
|
|
648
|
-
const after =
|
|
787
|
+
const before = existing.slice(0, match.index ?? 0);
|
|
788
|
+
const after = existing.slice((match.index ?? 0) + match[0].length);
|
|
649
789
|
const cleaned = `${before}${after.replace(/^\r?\n/, "")}`;
|
|
650
790
|
const trailingNewline = cleaned.length === 0 || cleaned.endsWith("\n") ? "" : "\n";
|
|
651
791
|
next = `${cleaned}${trailingNewline}${cleaned.length === 0 ? "" : "\n"}${managedBlock}
|
|
652
792
|
`;
|
|
653
793
|
} else {
|
|
654
|
-
if (
|
|
794
|
+
if (existing.length === 0) {
|
|
655
795
|
next = `${managedBlock}
|
|
656
796
|
`;
|
|
657
797
|
} else {
|
|
658
|
-
const trailingNewline =
|
|
659
|
-
next = `${
|
|
798
|
+
const trailingNewline = existing.endsWith("\n") ? "" : "\n";
|
|
799
|
+
next = `${existing}${trailingNewline}
|
|
660
800
|
${managedBlock}
|
|
661
801
|
`;
|
|
662
802
|
}
|
|
@@ -677,55 +817,6 @@ ${managedBlock}
|
|
|
677
817
|
};
|
|
678
818
|
}
|
|
679
819
|
}
|
|
680
|
-
var CURSOR_RULE_FRONT_MATTER = "---\nalwaysApply: true\ndescription: Fabric Protocol bootstrap rules\n---\n\n";
|
|
681
|
-
var CURSOR_LEGACY_FLAT_FILE_REL = join2(".cursor", "rules");
|
|
682
|
-
var CURSOR_BOOTSTRAP_MDC_REL = join2(".cursor", "rules", "fabric-bootstrap.mdc");
|
|
683
|
-
async function writeCursorBootstrapManagedBlock(targetRoot, _options = {}) {
|
|
684
|
-
const step = "bootstrap-cursor";
|
|
685
|
-
const target = join2(targetRoot, CURSOR_BOOTSTRAP_MDC_REL);
|
|
686
|
-
const legacyFlatFile = join2(targetRoot, CURSOR_LEGACY_FLAT_FILE_REL);
|
|
687
|
-
try {
|
|
688
|
-
if (existsSync2(legacyFlatFile)) {
|
|
689
|
-
const stat = statSync(legacyFlatFile);
|
|
690
|
-
if (stat.isFile()) {
|
|
691
|
-
await rm(legacyFlatFile, { force: true });
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
} catch {
|
|
695
|
-
}
|
|
696
|
-
const body = buildManagedBlockBody(targetRoot);
|
|
697
|
-
const managedBlock = wrapInBootstrapMarkers(body);
|
|
698
|
-
const expected = `${CURSOR_RULE_FRONT_MATTER}${managedBlock}
|
|
699
|
-
`;
|
|
700
|
-
let existing = "";
|
|
701
|
-
if (existsSync2(target)) {
|
|
702
|
-
try {
|
|
703
|
-
existing = await readFile(target, "utf8");
|
|
704
|
-
} catch (error) {
|
|
705
|
-
return {
|
|
706
|
-
step,
|
|
707
|
-
path: target,
|
|
708
|
-
status: "error",
|
|
709
|
-
message: error instanceof Error ? error.message : String(error)
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
if (existing === expected) {
|
|
714
|
-
return { step, path: target, status: "skipped", message: "up-to-date" };
|
|
715
|
-
}
|
|
716
|
-
try {
|
|
717
|
-
await mkdir2(dirname2(target), { recursive: true });
|
|
718
|
-
await atomicWriteText2(target, expected);
|
|
719
|
-
return { step, path: target, status: "written" };
|
|
720
|
-
} catch (error) {
|
|
721
|
-
return {
|
|
722
|
-
step,
|
|
723
|
-
path: target,
|
|
724
|
-
status: "error",
|
|
725
|
-
message: error instanceof Error ? error.message : String(error)
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
820
|
async function copyTextIdempotent(step, source, target) {
|
|
730
821
|
if (existsSync2(target)) {
|
|
731
822
|
try {
|
|
@@ -744,6 +835,17 @@ var FABRIC_HOOK_SCRIPT_BASENAMES = /* @__PURE__ */ new Set([
|
|
|
744
835
|
"fabric-hint.cjs",
|
|
745
836
|
"knowledge-hint-broad.cjs",
|
|
746
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",
|
|
747
849
|
// rc.5 TASK-010 rename — old hook scripts that pre-upgrade workspaces
|
|
748
850
|
// may still have registered. Sweeping them prevents the double-fire
|
|
749
851
|
// documented in audit §2.6.
|
|
@@ -882,22 +984,25 @@ export {
|
|
|
882
984
|
HOOK_CONFIG_TARGETS,
|
|
883
985
|
HOOK_CONFIG_ARRAY_PATHS,
|
|
884
986
|
FABRIC_HOOK_COMMAND_PATHS,
|
|
885
|
-
readFabricLanguagePreference,
|
|
886
987
|
installFabricArchiveSkill,
|
|
887
988
|
installFabricReviewSkill,
|
|
888
989
|
installFabricImportSkill,
|
|
889
990
|
installFabricSyncSkill,
|
|
991
|
+
installFabricStoreSkill,
|
|
992
|
+
installFabricAuditSkill,
|
|
993
|
+
installFabricConnectSkill,
|
|
994
|
+
installFabricRouterSkill,
|
|
890
995
|
cleanupDeprecatedSkills,
|
|
891
996
|
installSharedSkillLib,
|
|
892
997
|
installArchiveHintHook,
|
|
893
998
|
installKnowledgeHintBroadHook,
|
|
894
999
|
installKnowledgeHintNarrowHook,
|
|
895
1000
|
installCitePolicyEvictHook,
|
|
1001
|
+
installSessionEndMarkerHook,
|
|
1002
|
+
installPostTooluseMutationHook,
|
|
896
1003
|
installHookLibs,
|
|
897
1004
|
mergeClaudeCodeHookConfig,
|
|
898
1005
|
mergeCodexHookConfig,
|
|
899
|
-
mergeCursorHookConfig,
|
|
900
1006
|
writeClaudeBootstrapThinShell,
|
|
901
|
-
writeCodexBootstrapManagedBlock
|
|
902
|
-
writeCursorBootstrapManagedBlock
|
|
1007
|
+
writeCodexBootstrapManagedBlock
|
|
903
1008
|
};
|