@fenglimg/fabric-cli 2.0.1 → 2.2.0-rc.1
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/dist/{chunk-PWLW3B57.js → chunk-2CY4BMTH.js} +5 -1
- package/dist/{chunk-D25XJ4BC.js → chunk-2R55HNVD.js} +105 -5
- package/dist/chunk-4R2CYEA4.js +116 -0
- package/dist/{chunk-BATF4PEJ.js → chunk-AOE6AYI7.js} +2 -2
- package/dist/{chunk-WWNXR34K.js → chunk-BO4XIZWZ.js} +8 -1
- package/dist/chunk-L4Q55UC4.js +52 -0
- package/dist/chunk-LFIKMVY7.js +27 -0
- package/dist/chunk-RYAFBNES.js +33 -0
- package/dist/chunk-T5RPGCCM.js +40 -0
- package/dist/chunk-WU6GAPKH.js +36 -0
- package/dist/{chunk-MF3OTILQ.js → chunk-XC5RUHLK.js} +29 -8
- package/dist/{config-XJIPZNUP.js → config-XYRBZJDU.js} +3 -3
- package/dist/{doctor-EJDSEJSS.js → doctor-YONYXDX6.js} +147 -24
- package/dist/index.js +58 -10
- package/dist/{install-EKWMFLUU.js → install-74ANPCCP.js} +320 -75
- package/dist/{metrics-ACEQFPDU.js → metrics-RER6NLFC.js} +22 -9
- package/dist/{onboard-coverage-MFCAEBDO.js → onboard-coverage-JWQWDZW7.js} +1 -1
- package/dist/scope-explain-CDIZESP5.js +37 -0
- package/dist/status-GLQWLWH6.js +23 -0
- package/dist/store-XB3ADT65.js +144 -0
- package/dist/sync-UJ4BBCZJ.js +251 -0
- package/dist/{uninstall-MH7ZIB6M.js → uninstall-C3QXKOO6.js} +47 -7
- package/dist/whoami-2MLO4Y37.js +36 -0
- package/package.json +3 -3
- package/templates/hooks/fabric-hint.cjs +139 -7
- package/templates/hooks/knowledge-hint-broad.cjs +204 -9
- package/templates/hooks/knowledge-hint-narrow.cjs +49 -4
- package/templates/hooks/lib/bindings-snapshot-reader.cjs +81 -0
- package/templates/hooks/lib/cite-contract-reminder.cjs +15 -9
- package/templates/hooks/lib/cite-line-parser.cjs +48 -26
- package/templates/hooks/lib/injection-log.cjs +91 -0
- package/templates/hooks/lib/state-store.cjs +30 -11
- package/templates/skills/fabric-archive/SKILL.md +4 -0
- package/templates/skills/fabric-audit/SKILL.md +53 -0
- package/templates/skills/fabric-connect/SKILL.md +48 -0
- package/templates/skills/fabric-import/SKILL.md +4 -0
- package/templates/skills/fabric-review/SKILL.md +6 -0
- package/templates/skills/fabric-review/ref/cite-contract.md +56 -0
- package/templates/skills/fabric-store/SKILL.md +44 -0
- package/templates/skills/fabric-sync/SKILL.md +46 -0
|
@@ -8,11 +8,15 @@ import {
|
|
|
8
8
|
} from "@fenglimg/fabric-shared";
|
|
9
9
|
var locale = detectNodeLocale();
|
|
10
10
|
var t = createTranslator(locale);
|
|
11
|
-
function
|
|
11
|
+
function getProjectTranslator(projectRoot = process.cwd()) {
|
|
12
12
|
return createTranslator(resolveFabricLocale(projectRoot));
|
|
13
13
|
}
|
|
14
|
+
function getDoctorTranslator(projectRoot) {
|
|
15
|
+
return getProjectTranslator(projectRoot);
|
|
16
|
+
}
|
|
14
17
|
|
|
15
18
|
export {
|
|
16
19
|
t,
|
|
20
|
+
getProjectTranslator,
|
|
17
21
|
getDoctorTranslator
|
|
18
22
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
deepMerge
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XC5RUHLK.js";
|
|
5
5
|
|
|
6
6
|
// src/install/write-bootstrap-snapshot.ts
|
|
7
7
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -54,6 +54,10 @@ import {
|
|
|
54
54
|
var SKILL_TEMPLATE_REL = "skills/fabric-archive/SKILL.md";
|
|
55
55
|
var SKILL_REVIEW_TEMPLATE_REL = "skills/fabric-review/SKILL.md";
|
|
56
56
|
var SKILL_IMPORT_TEMPLATE_REL = "skills/fabric-import/SKILL.md";
|
|
57
|
+
var SKILL_SYNC_TEMPLATE_REL = "skills/fabric-sync/SKILL.md";
|
|
58
|
+
var SKILL_STORE_TEMPLATE_REL = "skills/fabric-store/SKILL.md";
|
|
59
|
+
var SKILL_AUDIT_TEMPLATE_REL = "skills/fabric-audit/SKILL.md";
|
|
60
|
+
var SKILL_CONNECT_TEMPLATE_REL = "skills/fabric-connect/SKILL.md";
|
|
57
61
|
var HOOK_SCRIPT_TEMPLATE_REL = "hooks/fabric-hint.cjs";
|
|
58
62
|
var HOOK_BROAD_SCRIPT_TEMPLATE_REL = "hooks/knowledge-hint-broad.cjs";
|
|
59
63
|
var HOOK_NARROW_SCRIPT_TEMPLATE_REL = "hooks/knowledge-hint-narrow.cjs";
|
|
@@ -74,6 +78,29 @@ var SKILL_DESTINATIONS = {
|
|
|
74
78
|
fabricImport: [
|
|
75
79
|
".claude/skills/fabric-import/SKILL.md",
|
|
76
80
|
".codex/skills/fabric-import/SKILL.md"
|
|
81
|
+
],
|
|
82
|
+
// v2.1.0-rc.1 P4 (S46): fabric-sync mirrors the sibling skills' 2-client
|
|
83
|
+
// coverage (Claude Code + Codex CLI surface a Skills directory).
|
|
84
|
+
fabricSync: [
|
|
85
|
+
".claude/skills/fabric-sync/SKILL.md",
|
|
86
|
+
".codex/skills/fabric-sync/SKILL.md"
|
|
87
|
+
],
|
|
88
|
+
// v2.1 ADJ-NEWN-1/#4: fabric-store knowledge-store ops skill, same 2-client
|
|
89
|
+
// coverage as the sibling skills.
|
|
90
|
+
fabricStore: [
|
|
91
|
+
".claude/skills/fabric-store/SKILL.md",
|
|
92
|
+
".codex/skills/fabric-store/SKILL.md"
|
|
93
|
+
],
|
|
94
|
+
// v2.2 SK1-audit (W2-T5): fabric-audit semantic-deprecation skill, same
|
|
95
|
+
// 2-client coverage as the sibling skills.
|
|
96
|
+
fabricAudit: [
|
|
97
|
+
".claude/skills/fabric-audit/SKILL.md",
|
|
98
|
+
".codex/skills/fabric-audit/SKILL.md"
|
|
99
|
+
],
|
|
100
|
+
// v2.2 SK2-connect (W3-T2): fabric-connect knowledge-graph relation skill.
|
|
101
|
+
fabricConnect: [
|
|
102
|
+
".claude/skills/fabric-connect/SKILL.md",
|
|
103
|
+
".codex/skills/fabric-connect/SKILL.md"
|
|
77
104
|
]
|
|
78
105
|
};
|
|
79
106
|
var DEPRECATED_SKILL_DIRS = [
|
|
@@ -121,7 +148,11 @@ var HOOK_CONFIG_TARGETS = {
|
|
|
121
148
|
cursor: ".cursor/hooks.json"
|
|
122
149
|
};
|
|
123
150
|
var HOOK_CONFIG_ARRAY_PATHS = {
|
|
124
|
-
|
|
151
|
+
// F2: "hooks.UserPromptSubmit" MUST be listed — the Claude Code template
|
|
152
|
+
// ships a UserPromptSubmit cite-policy hook, so without this path deepMerge
|
|
153
|
+
// array-REPLACEs (instead of append-with-dedupe) on re-install, silently
|
|
154
|
+
// clobbering any user-defined UserPromptSubmit hook.
|
|
155
|
+
claudeCode: ["hooks.Stop", "hooks.SessionStart", "hooks.PreToolUse", "hooks.UserPromptSubmit"],
|
|
125
156
|
codex: ["events.Stop", "events.SessionStart", "events.PreToolUse"],
|
|
126
157
|
cursor: ["hooks.stop", "hooks.sessionStart", "hooks.preToolUse"]
|
|
127
158
|
};
|
|
@@ -129,17 +160,22 @@ var FABRIC_HOOK_COMMAND_PATHS = {
|
|
|
129
160
|
claudeCode: {
|
|
130
161
|
fabricHint: "${CLAUDE_PROJECT_DIR}/.claude/hooks/fabric-hint.cjs",
|
|
131
162
|
knowledgeHintBroad: "${CLAUDE_PROJECT_DIR}/.claude/hooks/knowledge-hint-broad.cjs",
|
|
132
|
-
knowledgeHintNarrow: "${CLAUDE_PROJECT_DIR}/.claude/hooks/knowledge-hint-narrow.cjs"
|
|
163
|
+
knowledgeHintNarrow: "${CLAUDE_PROJECT_DIR}/.claude/hooks/knowledge-hint-narrow.cjs",
|
|
164
|
+
// F3: the UserPromptSubmit cite-policy-evict hook must be a known fabric
|
|
165
|
+
// command so uninstall prunes it (matches the literal in claude-code.json).
|
|
166
|
+
citePolicyEvict: "${CLAUDE_PROJECT_DIR}/.claude/hooks/cite-policy-evict.cjs"
|
|
133
167
|
},
|
|
134
168
|
codex: {
|
|
135
169
|
fabricHint: '"$(git rev-parse --show-toplevel)/.codex/hooks/fabric-hint.cjs"',
|
|
136
170
|
knowledgeHintBroad: '"$(git rev-parse --show-toplevel)/.codex/hooks/knowledge-hint-broad.cjs"',
|
|
137
|
-
knowledgeHintNarrow: '"$(git rev-parse --show-toplevel)/.codex/hooks/knowledge-hint-narrow.cjs"'
|
|
171
|
+
knowledgeHintNarrow: '"$(git rev-parse --show-toplevel)/.codex/hooks/knowledge-hint-narrow.cjs"',
|
|
172
|
+
citePolicyEvict: '"$(git rev-parse --show-toplevel)/.codex/hooks/cite-policy-evict.cjs"'
|
|
138
173
|
},
|
|
139
174
|
cursor: {
|
|
140
175
|
fabricHint: ".cursor/hooks/fabric-hint.cjs",
|
|
141
176
|
knowledgeHintBroad: ".cursor/hooks/knowledge-hint-broad.cjs",
|
|
142
|
-
knowledgeHintNarrow: ".cursor/hooks/knowledge-hint-narrow.cjs"
|
|
177
|
+
knowledgeHintNarrow: ".cursor/hooks/knowledge-hint-narrow.cjs",
|
|
178
|
+
citePolicyEvict: ".cursor/hooks/cite-policy-evict.cjs"
|
|
143
179
|
}
|
|
144
180
|
};
|
|
145
181
|
function readFabricLanguagePreference(projectRoot) {
|
|
@@ -235,6 +271,66 @@ async function installFabricImportSkill(projectRoot, _options = {}) {
|
|
|
235
271
|
results.push(...await installSkillRefFiles(projectRoot, "fabric-import"));
|
|
236
272
|
return results;
|
|
237
273
|
}
|
|
274
|
+
async function installFabricSyncSkill(projectRoot, _options = {}) {
|
|
275
|
+
const source = await readTemplate(SKILL_SYNC_TEMPLATE_REL);
|
|
276
|
+
validateSkillCanonicalSize(source, "fabric-sync");
|
|
277
|
+
const targets = SKILL_DESTINATIONS.fabricSync.map((rel) => join2(projectRoot, rel));
|
|
278
|
+
const results = [];
|
|
279
|
+
for (const target of targets) {
|
|
280
|
+
const staleMsg = inspectStaleInstall(target, source);
|
|
281
|
+
const result = await copyTextIdempotent("skill-sync", source, target);
|
|
282
|
+
if (staleMsg && result.status === "written") {
|
|
283
|
+
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
284
|
+
}
|
|
285
|
+
results.push(result);
|
|
286
|
+
}
|
|
287
|
+
return results;
|
|
288
|
+
}
|
|
289
|
+
async function installFabricStoreSkill(projectRoot, _options = {}) {
|
|
290
|
+
const source = await readTemplate(SKILL_STORE_TEMPLATE_REL);
|
|
291
|
+
validateSkillCanonicalSize(source, "fabric-store");
|
|
292
|
+
const targets = SKILL_DESTINATIONS.fabricStore.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-store", source, target);
|
|
297
|
+
if (staleMsg && result.status === "written") {
|
|
298
|
+
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
299
|
+
}
|
|
300
|
+
results.push(result);
|
|
301
|
+
}
|
|
302
|
+
return results;
|
|
303
|
+
}
|
|
304
|
+
async function installFabricAuditSkill(projectRoot, _options = {}) {
|
|
305
|
+
const source = await readTemplate(SKILL_AUDIT_TEMPLATE_REL);
|
|
306
|
+
validateSkillCanonicalSize(source, "fabric-audit");
|
|
307
|
+
const targets = SKILL_DESTINATIONS.fabricAudit.map((rel) => join2(projectRoot, rel));
|
|
308
|
+
const results = [];
|
|
309
|
+
for (const target of targets) {
|
|
310
|
+
const staleMsg = inspectStaleInstall(target, source);
|
|
311
|
+
const result = await copyTextIdempotent("skill-audit", source, target);
|
|
312
|
+
if (staleMsg && result.status === "written") {
|
|
313
|
+
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
314
|
+
}
|
|
315
|
+
results.push(result);
|
|
316
|
+
}
|
|
317
|
+
return results;
|
|
318
|
+
}
|
|
319
|
+
async function installFabricConnectSkill(projectRoot, _options = {}) {
|
|
320
|
+
const source = await readTemplate(SKILL_CONNECT_TEMPLATE_REL);
|
|
321
|
+
validateSkillCanonicalSize(source, "fabric-connect");
|
|
322
|
+
const targets = SKILL_DESTINATIONS.fabricConnect.map((rel) => join2(projectRoot, rel));
|
|
323
|
+
const results = [];
|
|
324
|
+
for (const target of targets) {
|
|
325
|
+
const staleMsg = inspectStaleInstall(target, source);
|
|
326
|
+
const result = await copyTextIdempotent("skill-connect", source, target);
|
|
327
|
+
if (staleMsg && result.status === "written") {
|
|
328
|
+
result.message = result.message ? `${staleMsg}; ${result.message}` : staleMsg;
|
|
329
|
+
}
|
|
330
|
+
results.push(result);
|
|
331
|
+
}
|
|
332
|
+
return results;
|
|
333
|
+
}
|
|
238
334
|
async function cleanupDeprecatedSkills(projectRoot) {
|
|
239
335
|
const results = [];
|
|
240
336
|
for (const rel of DEPRECATED_SKILL_DIRS) {
|
|
@@ -864,6 +960,10 @@ export {
|
|
|
864
960
|
installFabricArchiveSkill,
|
|
865
961
|
installFabricReviewSkill,
|
|
866
962
|
installFabricImportSkill,
|
|
963
|
+
installFabricSyncSkill,
|
|
964
|
+
installFabricStoreSkill,
|
|
965
|
+
installFabricAuditSkill,
|
|
966
|
+
installFabricConnectSkill,
|
|
867
967
|
cleanupDeprecatedSkills,
|
|
868
968
|
installSharedSkillLib,
|
|
869
969
|
installArchiveHintHook,
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadProjectConfig,
|
|
4
|
+
saveProjectConfig
|
|
5
|
+
} from "./chunk-LFIKMVY7.js";
|
|
6
|
+
import {
|
|
7
|
+
loadGlobalConfig,
|
|
8
|
+
resolveGlobalRoot,
|
|
9
|
+
saveGlobalConfig
|
|
10
|
+
} from "./chunk-RYAFBNES.js";
|
|
11
|
+
|
|
12
|
+
// src/store/store-ops.ts
|
|
13
|
+
import { randomUUID } from "crypto";
|
|
14
|
+
import { existsSync } from "fs";
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
import {
|
|
17
|
+
addMountedStore,
|
|
18
|
+
bindRequiredStore,
|
|
19
|
+
detachMountedStore,
|
|
20
|
+
explainStore,
|
|
21
|
+
initStore,
|
|
22
|
+
storeRelativePath
|
|
23
|
+
} from "@fenglimg/fabric-shared";
|
|
24
|
+
var NO_GLOBAL_CONFIG = "no global Fabric config found \u2014 run `fabric install --global <url>` first";
|
|
25
|
+
function requireConfig(globalRoot) {
|
|
26
|
+
const config = loadGlobalConfig(globalRoot);
|
|
27
|
+
if (config === null) {
|
|
28
|
+
throw new Error(NO_GLOBAL_CONFIG);
|
|
29
|
+
}
|
|
30
|
+
return config;
|
|
31
|
+
}
|
|
32
|
+
function storeList(globalRoot = resolveGlobalRoot()) {
|
|
33
|
+
return requireConfig(globalRoot).stores;
|
|
34
|
+
}
|
|
35
|
+
function storeAdd(store, globalRoot = resolveGlobalRoot()) {
|
|
36
|
+
const next = addMountedStore(requireConfig(globalRoot), store);
|
|
37
|
+
saveGlobalConfig(next, globalRoot);
|
|
38
|
+
return next;
|
|
39
|
+
}
|
|
40
|
+
function storeCreate(alias, now, options = {}) {
|
|
41
|
+
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
42
|
+
const config = requireConfig(globalRoot);
|
|
43
|
+
const uuid = options.uuid ?? randomUUID();
|
|
44
|
+
const storeDir = join(globalRoot, storeRelativePath(uuid));
|
|
45
|
+
initStore(
|
|
46
|
+
storeDir,
|
|
47
|
+
{ store_uuid: uuid, created_at: now, canonical_alias: alias },
|
|
48
|
+
{ git: options.git }
|
|
49
|
+
);
|
|
50
|
+
const mounted = options.remote === void 0 ? { store_uuid: uuid, alias } : { store_uuid: uuid, alias, remote: options.remote };
|
|
51
|
+
const next = addMountedStore(config, mounted);
|
|
52
|
+
saveGlobalConfig(next, globalRoot);
|
|
53
|
+
return { config: next, store_uuid: uuid, storeDir };
|
|
54
|
+
}
|
|
55
|
+
function assertStoreMountable(uuid, globalRoot = resolveGlobalRoot()) {
|
|
56
|
+
const storeDir = join(globalRoot, storeRelativePath(uuid));
|
|
57
|
+
if (!existsSync(join(storeDir, "store.json"))) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`cannot mount store ${uuid}: no store tree at ${storeDir} \u2014 clone it first (\`fabric install --global --url <remote>\`) or create it locally, then re-run \`fabric store add\`. Refusing to register a phantom store.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function storeRemove(alias, globalRoot = resolveGlobalRoot()) {
|
|
64
|
+
const result = detachMountedStore(requireConfig(globalRoot), alias);
|
|
65
|
+
saveGlobalConfig(result.config, globalRoot);
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
function storeExplain(alias, globalRoot = resolveGlobalRoot()) {
|
|
69
|
+
return explainStore(requireConfig(globalRoot), alias);
|
|
70
|
+
}
|
|
71
|
+
var NO_PROJECT_CONFIG = "no project Fabric config \u2014 run `fabric install` in this repo first";
|
|
72
|
+
function requireProjectConfig(projectRoot) {
|
|
73
|
+
const config = loadProjectConfig(projectRoot);
|
|
74
|
+
if (config === null) {
|
|
75
|
+
throw new Error(NO_PROJECT_CONFIG);
|
|
76
|
+
}
|
|
77
|
+
return config;
|
|
78
|
+
}
|
|
79
|
+
function storeBind(projectRoot, entry) {
|
|
80
|
+
const config = requireProjectConfig(projectRoot);
|
|
81
|
+
const next = {
|
|
82
|
+
...config,
|
|
83
|
+
required_stores: bindRequiredStore(config.required_stores ?? [], entry)
|
|
84
|
+
};
|
|
85
|
+
saveProjectConfig(next, projectRoot);
|
|
86
|
+
return next;
|
|
87
|
+
}
|
|
88
|
+
function storeSwitchWrite(projectRoot, alias) {
|
|
89
|
+
const config = requireProjectConfig(projectRoot);
|
|
90
|
+
const next = { ...config, active_write_store: alias };
|
|
91
|
+
saveProjectConfig(next, projectRoot);
|
|
92
|
+
return next;
|
|
93
|
+
}
|
|
94
|
+
function missingRequiredStores(projectRoot, globalRoot = resolveGlobalRoot()) {
|
|
95
|
+
const project = loadProjectConfig(projectRoot);
|
|
96
|
+
if (project === null || project.required_stores === void 0) {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
const global = loadGlobalConfig(globalRoot);
|
|
100
|
+
const mounted = new Set(
|
|
101
|
+
(global?.stores ?? []).flatMap((s) => [s.alias, s.store_uuid])
|
|
102
|
+
);
|
|
103
|
+
return project.required_stores.filter((r) => !mounted.has(r.id));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export {
|
|
107
|
+
storeList,
|
|
108
|
+
storeAdd,
|
|
109
|
+
storeCreate,
|
|
110
|
+
assertStoreMountable,
|
|
111
|
+
storeRemove,
|
|
112
|
+
storeExplain,
|
|
113
|
+
storeBind,
|
|
114
|
+
storeSwitchWrite,
|
|
115
|
+
missingRequiredStores
|
|
116
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveClients
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XC5RUHLK.js";
|
|
5
5
|
import {
|
|
6
6
|
t
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2CY4BMTH.js";
|
|
8
8
|
|
|
9
9
|
// src/commands/config.ts
|
|
10
10
|
import { existsSync, statSync } from "fs";
|
|
@@ -4,7 +4,14 @@
|
|
|
4
4
|
import pc from "picocolors";
|
|
5
5
|
import stringWidth from "string-width";
|
|
6
6
|
function isColorEnabled() {
|
|
7
|
-
|
|
7
|
+
if (process.env.NO_COLOR) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
const force = process.env.FORCE_COLOR;
|
|
11
|
+
if (force !== void 0) {
|
|
12
|
+
return force !== "0" && force.toLowerCase() !== "false";
|
|
13
|
+
}
|
|
14
|
+
return Boolean(process.stdout.isTTY) && Boolean(process.stderr.isTTY);
|
|
8
15
|
}
|
|
9
16
|
function colorize(painter) {
|
|
10
17
|
return (value) => isColorEnabled() ? painter(value) : value;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadProjectConfig
|
|
4
|
+
} from "./chunk-LFIKMVY7.js";
|
|
5
|
+
import {
|
|
6
|
+
loadGlobalConfig,
|
|
7
|
+
resolveGlobalRoot
|
|
8
|
+
} from "./chunk-RYAFBNES.js";
|
|
9
|
+
|
|
10
|
+
// src/store/scope-explain.ts
|
|
11
|
+
import {
|
|
12
|
+
createStoreResolver
|
|
13
|
+
} from "@fenglimg/fabric-shared";
|
|
14
|
+
function buildResolveInput(projectRoot, globalRoot = resolveGlobalRoot()) {
|
|
15
|
+
const global = loadGlobalConfig(globalRoot);
|
|
16
|
+
if (global === null) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const project = loadProjectConfig(projectRoot);
|
|
20
|
+
return {
|
|
21
|
+
uid: global.uid,
|
|
22
|
+
mountedStores: global.stores.map((s) => ({
|
|
23
|
+
store_uuid: s.store_uuid,
|
|
24
|
+
alias: s.alias,
|
|
25
|
+
...s.remote === void 0 ? {} : { remote: s.remote },
|
|
26
|
+
writable: s.writable ?? true,
|
|
27
|
+
personal: s.personal ?? false
|
|
28
|
+
})),
|
|
29
|
+
requiredStores: (project?.required_stores ?? []).map((r) => ({
|
|
30
|
+
id: r.id,
|
|
31
|
+
...r.suggested_remote === void 0 ? {} : { suggested_remote: r.suggested_remote }
|
|
32
|
+
})),
|
|
33
|
+
...project?.active_write_store === void 0 ? {} : { activeWriteAlias: project.active_write_store }
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function scopeExplain(projectRoot, scope, globalRoot = resolveGlobalRoot()) {
|
|
37
|
+
const input = buildResolveInput(projectRoot, globalRoot);
|
|
38
|
+
if (input === null) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const resolver = createStoreResolver();
|
|
42
|
+
return {
|
|
43
|
+
scope,
|
|
44
|
+
readSet: resolver.resolveReadSet(input),
|
|
45
|
+
writeTarget: resolver.resolveWriteTarget(input, scope).target
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
buildResolveInput,
|
|
51
|
+
scopeExplain
|
|
52
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/store/project-config-io.ts
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { fabricConfigSchema } from "@fenglimg/fabric-shared";
|
|
7
|
+
function projectConfigPath(projectRoot) {
|
|
8
|
+
return join(projectRoot, ".fabric", "fabric-config.json");
|
|
9
|
+
}
|
|
10
|
+
function loadProjectConfig(projectRoot) {
|
|
11
|
+
const path = projectConfigPath(projectRoot);
|
|
12
|
+
if (!existsSync(path)) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return fabricConfigSchema.parse(JSON.parse(readFileSync(path, "utf8")));
|
|
16
|
+
}
|
|
17
|
+
function saveProjectConfig(config, projectRoot) {
|
|
18
|
+
const validated = fabricConfigSchema.parse(config);
|
|
19
|
+
mkdirSync(join(projectRoot, ".fabric"), { recursive: true });
|
|
20
|
+
writeFileSync(projectConfigPath(projectRoot), `${JSON.stringify(validated, null, 2)}
|
|
21
|
+
`, "utf8");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
loadProjectConfig,
|
|
26
|
+
saveProjectConfig
|
|
27
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/store/global-config-io.ts
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { globalConfigSchema } from "@fenglimg/fabric-shared";
|
|
8
|
+
function resolveGlobalRoot() {
|
|
9
|
+
return join(process.env.FABRIC_HOME ?? homedir(), ".fabric");
|
|
10
|
+
}
|
|
11
|
+
function globalConfigPath(globalRoot = resolveGlobalRoot()) {
|
|
12
|
+
return join(globalRoot, "fabric-global.json");
|
|
13
|
+
}
|
|
14
|
+
function loadGlobalConfig(globalRoot = resolveGlobalRoot()) {
|
|
15
|
+
const path = globalConfigPath(globalRoot);
|
|
16
|
+
if (!existsSync(path)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return globalConfigSchema.parse(JSON.parse(readFileSync(path, "utf8")));
|
|
20
|
+
}
|
|
21
|
+
function saveGlobalConfig(config, globalRoot = resolveGlobalRoot()) {
|
|
22
|
+
const validated = globalConfigSchema.parse(config);
|
|
23
|
+
mkdirSync(globalRoot, { recursive: true });
|
|
24
|
+
writeFileSync(globalConfigPath(globalRoot), `${JSON.stringify(validated, null, 2)}
|
|
25
|
+
`, "utf8");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
resolveGlobalRoot,
|
|
30
|
+
globalConfigPath,
|
|
31
|
+
loadGlobalConfig,
|
|
32
|
+
saveGlobalConfig
|
|
33
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadProjectConfig
|
|
4
|
+
} from "./chunk-LFIKMVY7.js";
|
|
5
|
+
import {
|
|
6
|
+
loadGlobalConfig,
|
|
7
|
+
resolveGlobalRoot
|
|
8
|
+
} from "./chunk-RYAFBNES.js";
|
|
9
|
+
|
|
10
|
+
// src/store/info-ops.ts
|
|
11
|
+
function whoami(globalRoot = resolveGlobalRoot()) {
|
|
12
|
+
const config = loadGlobalConfig(globalRoot);
|
|
13
|
+
if (config === null) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
uid: config.uid,
|
|
18
|
+
stores: config.stores.map((s) => ({
|
|
19
|
+
alias: s.alias,
|
|
20
|
+
store_uuid: s.store_uuid,
|
|
21
|
+
local_only: s.remote === void 0
|
|
22
|
+
}))
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function projectStatus(projectRoot, globalRoot = resolveGlobalRoot()) {
|
|
26
|
+
const global = loadGlobalConfig(globalRoot);
|
|
27
|
+
const project = loadProjectConfig(projectRoot);
|
|
28
|
+
return {
|
|
29
|
+
uid: global?.uid ?? null,
|
|
30
|
+
mounted: (global?.stores ?? []).map((s) => s.alias),
|
|
31
|
+
project_id: project?.project_id ?? null,
|
|
32
|
+
required: (project?.required_stores ?? []).map((r) => r.id),
|
|
33
|
+
active_write_store: project?.active_write_store ?? null
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
whoami,
|
|
39
|
+
projectStatus
|
|
40
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
buildResolveInput
|
|
4
|
+
} from "./chunk-L4Q55UC4.js";
|
|
5
|
+
import {
|
|
6
|
+
loadProjectConfig
|
|
7
|
+
} from "./chunk-LFIKMVY7.js";
|
|
8
|
+
import {
|
|
9
|
+
resolveGlobalRoot
|
|
10
|
+
} from "./chunk-RYAFBNES.js";
|
|
11
|
+
|
|
12
|
+
// src/store/bindings-io.ts
|
|
13
|
+
import { writeBindingsSnapshot } from "@fenglimg/fabric-shared";
|
|
14
|
+
var DEFAULT_WRITE_SCOPE = "team";
|
|
15
|
+
function regenerateBindingsSnapshot(projectRoot, options) {
|
|
16
|
+
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
17
|
+
const resolveInput = buildResolveInput(projectRoot, globalRoot);
|
|
18
|
+
if (resolveInput === null) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const project = loadProjectConfig(projectRoot);
|
|
22
|
+
if (project?.project_id === void 0) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return writeBindingsSnapshot({
|
|
26
|
+
globalRoot,
|
|
27
|
+
projectId: project.project_id,
|
|
28
|
+
resolveInput,
|
|
29
|
+
writeScope: options.writeScope ?? DEFAULT_WRITE_SCOPE,
|
|
30
|
+
now: options.now
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
regenerateBindingsSnapshot
|
|
36
|
+
};
|
|
@@ -307,7 +307,7 @@ function serializeTomlInlineTable(values) {
|
|
|
307
307
|
const entries = Object.entries(values).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key} = ${escapeTomlString(value)}`);
|
|
308
308
|
return `{ ${entries.join(", ")} }`;
|
|
309
309
|
}
|
|
310
|
-
function serializeCodexServerBlock(serverName, serverEntry) {
|
|
310
|
+
function serializeCodexServerBlock(serverName, serverEntry, preservedUserLines = []) {
|
|
311
311
|
const lines = [
|
|
312
312
|
`[mcp_servers.${serverName}]`,
|
|
313
313
|
`command = ${escapeTomlString(serverEntry.command)}`,
|
|
@@ -316,9 +316,26 @@ function serializeCodexServerBlock(serverName, serverEntry) {
|
|
|
316
316
|
if (serverEntry.env !== void 0 && Object.keys(serverEntry.env).length > 0) {
|
|
317
317
|
lines.push(`env = ${serializeTomlInlineTable(serverEntry.env)}`);
|
|
318
318
|
}
|
|
319
|
+
lines.push(...preservedUserLines);
|
|
319
320
|
return `${lines.join("\n")}
|
|
320
321
|
`;
|
|
321
322
|
}
|
|
323
|
+
var CODEX_MANAGED_BLOCK_KEYS = /* @__PURE__ */ new Set(["command", "args", "env"]);
|
|
324
|
+
function extractCodexBlockUserLines(blockText) {
|
|
325
|
+
const out = [];
|
|
326
|
+
const lines = blockText.split("\n");
|
|
327
|
+
for (const line of lines) {
|
|
328
|
+
const trimmed = line.trim();
|
|
329
|
+
if (trimmed.length === 0) continue;
|
|
330
|
+
if (/^\[/.test(trimmed)) continue;
|
|
331
|
+
const keyMatch = /^([A-Za-z0-9_-]+)\s*=/.exec(trimmed);
|
|
332
|
+
if (keyMatch !== null && CODEX_MANAGED_BLOCK_KEYS.has(keyMatch[1])) {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
out.push(trimmed);
|
|
336
|
+
}
|
|
337
|
+
return out;
|
|
338
|
+
}
|
|
322
339
|
function trimTrailingBlankLines(value) {
|
|
323
340
|
return value.replace(/\s+$/u, "");
|
|
324
341
|
}
|
|
@@ -341,13 +358,16 @@ function removeCodexServerBlock(rawConfig, serverName) {
|
|
|
341
358
|
return { text, changed };
|
|
342
359
|
}
|
|
343
360
|
function upsertCodexServerBlock(rawConfig, serverName, serverEntry) {
|
|
344
|
-
const block = serializeCodexServerBlock(serverName, serverEntry);
|
|
345
361
|
const normalized = rawConfig.replace(/\r\n/g, "\n");
|
|
346
|
-
const
|
|
362
|
+
const escaped = serverName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
363
|
+
const legacyPattern = new RegExp(String.raw`\n?\[mcp\.servers\.${escaped}\]\n[\s\S]*?(?=\n\[[^\n]+\]\n|$)`, "g");
|
|
347
364
|
const currentPattern = new RegExp(
|
|
348
|
-
String.raw`\n?\[mcp_servers\.${
|
|
365
|
+
String.raw`\n?\[mcp_servers\.${escaped}\]\n[\s\S]*?(?=\n\[[^\n]+\]\n|$)`,
|
|
349
366
|
"g"
|
|
350
367
|
);
|
|
368
|
+
const existingMatch = normalized.match(currentPattern);
|
|
369
|
+
const preservedUserLines = existingMatch !== null && existingMatch.length > 0 ? extractCodexBlockUserLines(existingMatch[0]) : [];
|
|
370
|
+
const block = serializeCodexServerBlock(serverName, serverEntry, preservedUserLines);
|
|
351
371
|
const withoutLegacy = normalized.replace(legacyPattern, "");
|
|
352
372
|
const withoutExisting = withoutLegacy.replace(currentPattern, "");
|
|
353
373
|
const trimmed = trimTrailingBlankLines(withoutExisting);
|
|
@@ -528,10 +548,11 @@ function detectClientSupports(workspaceRoot, fabricConfig = {}) {
|
|
|
528
548
|
},
|
|
529
549
|
installedCapabilities: {
|
|
530
550
|
hook: existsSync4(join4(workspaceRoot, ".codex", "hooks.json")),
|
|
531
|
-
//
|
|
532
|
-
//
|
|
533
|
-
//
|
|
534
|
-
|
|
551
|
+
// F6: the v2 skills (fabric-archive/review/import/…) DO install to
|
|
552
|
+
// `.codex/skills/` now, so probe that directory instead of the stale
|
|
553
|
+
// hardcoded `false` (which made `fabric install` always re-report Codex
|
|
554
|
+
// skills as uninstalled even right after installing them).
|
|
555
|
+
skill: existsSync4(join4(workspaceRoot, ".codex", "skills"))
|
|
535
556
|
}
|
|
536
557
|
}
|
|
537
558
|
];
|
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
configCmd,
|
|
4
4
|
config_default,
|
|
5
5
|
installMcpClients
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-AOE6AYI7.js";
|
|
7
|
+
import "./chunk-XC5RUHLK.js";
|
|
8
|
+
import "./chunk-2CY4BMTH.js";
|
|
9
9
|
export {
|
|
10
10
|
configCmd,
|
|
11
11
|
config_default as default,
|