@fenglimg/fabric-cli 2.1.0-rc.2 → 2.2.0-rc.3
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-5LQIHYFC.js +64 -0
- package/dist/chunk-5ZUMLCD5.js +248 -0
- package/dist/{chunk-WWNXR34K.js → chunk-BO4XIZWZ.js} +8 -1
- package/dist/chunk-EOT63RDH.js +36 -0
- package/dist/{chunk-BATF4PEJ.js → chunk-F6ITRM7T.js} +4 -4
- package/dist/{chunk-WU6GAPKH.js → chunk-H3FE6VIK.js} +3 -5
- package/dist/{chunk-MF3OTILQ.js → chunk-XC5RUHLK.js} +29 -8
- package/dist/chunk-XCBVSGCS.js +25 -0
- package/dist/{chunk-F46ORPOA.js → chunk-XHHCRDIR.js} +149 -7
- package/dist/{config-XJIPZNUP.js → config-VJMXCLXW.js} +3 -3
- package/dist/{doctor-QVNPHLJK.js → doctor-J4O3X54I.js} +154 -30
- package/dist/index.js +57 -16
- package/dist/{install-2HDO5FTQ.js → install-BULNDUIM.js} +241 -108
- package/dist/{metrics-ACEQFPDU.js → metrics-RER6NLFC.js} +22 -9
- package/dist/{onboard-coverage-MFCAEBDO.js → onboard-coverage-JWQWDZW7.js} +1 -1
- package/dist/{plan-context-hint-FC6P3WFE.js → plan-context-hint-CHVZGOZ5.js} +21 -8
- package/dist/{scope-explain-2F2R5URO.js → scope-explain-BWRWBCCP.js} +19 -5
- package/dist/{status-GLQWLWH6.js → status-PANEGKU2.js} +17 -6
- package/dist/store-66NK2FTQ.js +443 -0
- package/dist/sync-EA5HZMXM.js +395 -0
- package/dist/{uninstall-TAXSUSKH.js → uninstall-F75MPKQC.js} +61 -4
- package/dist/whoami-66YKY5DZ.js +47 -0
- package/package.json +3 -3
- package/templates/hooks/cite-policy-evict.cjs +412 -160
- package/templates/hooks/configs/claude-code.json +17 -2
- package/templates/hooks/configs/codex-hooks.json +14 -2
- package/templates/hooks/configs/cursor-hooks.json +14 -2
- package/templates/hooks/fabric-hint.cjs +247 -19
- package/templates/hooks/knowledge-hint-broad.cjs +176 -10
- package/templates/hooks/knowledge-hint-narrow.cjs +64 -5
- package/templates/hooks/lib/injection-log.cjs +91 -0
- package/templates/hooks/lib/state-store.cjs +30 -11
- package/templates/hooks/post-tooluse-mutation.cjs +285 -0
- package/templates/hooks/session-end-marker.cjs +140 -0
- package/templates/skills/fabric-archive/SKILL.md +7 -1
- package/templates/skills/fabric-audit/SKILL.md +53 -0
- package/templates/skills/fabric-connect/SKILL.md +48 -0
- package/templates/skills/fabric-review/SKILL.md +2 -0
- package/templates/skills/fabric-review/ref/cite-contract.md +56 -0
- package/templates/skills/fabric-store/SKILL.md +44 -0
- package/dist/chunk-HFQVXY6P.js +0 -86
- package/dist/chunk-L4Q55UC4.js +0 -52
- package/dist/chunk-LFIKMVY7.js +0 -27
- package/dist/chunk-RYAFBNES.js +0 -33
- package/dist/chunk-T5RPGCCM.js +0 -40
- package/dist/store-XTSE5TY6.js +0 -105
- package/dist/sync-BJCWDPNC.js +0 -245
- package/dist/whoami-B6AEMSEV.js +0 -31
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
regenerateBindingsSnapshot
|
|
4
|
+
} from "./chunk-H3FE6VIK.js";
|
|
5
|
+
import "./chunk-EOT63RDH.js";
|
|
6
|
+
import {
|
|
7
|
+
getProjectTranslator
|
|
8
|
+
} from "./chunk-2CY4BMTH.js";
|
|
9
|
+
import {
|
|
10
|
+
loadGlobalConfig,
|
|
11
|
+
resolveGlobalRoot
|
|
12
|
+
} from "./chunk-XCBVSGCS.js";
|
|
13
|
+
|
|
14
|
+
// src/commands/sync.ts
|
|
15
|
+
import { defineCommand } from "citty";
|
|
16
|
+
|
|
17
|
+
// src/sync/run-sync.ts
|
|
18
|
+
import { execFileSync } from "child_process";
|
|
19
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
import { GLOBAL_STATE_DIR, storeRelativePath } from "@fenglimg/fabric-shared";
|
|
22
|
+
import { GenericIOError } from "@fenglimg/fabric-shared/errors";
|
|
23
|
+
|
|
24
|
+
// src/sync/state-machine.ts
|
|
25
|
+
function syncTransition(state, event) {
|
|
26
|
+
switch (state) {
|
|
27
|
+
case "pending":
|
|
28
|
+
if (event === "rebase_clean") return "synced";
|
|
29
|
+
if (event === "rebase_conflict") return "conflict";
|
|
30
|
+
if (event === "network_unavailable") return "offline";
|
|
31
|
+
break;
|
|
32
|
+
case "conflict":
|
|
33
|
+
if (event === "user_continue") return "synced";
|
|
34
|
+
if (event === "user_abort") return "aborted";
|
|
35
|
+
if (event === "network_unavailable") return "offline";
|
|
36
|
+
break;
|
|
37
|
+
case "offline":
|
|
38
|
+
if (event === "retry" || event === "rebase_clean") return "synced";
|
|
39
|
+
if (event === "rebase_conflict") return "conflict";
|
|
40
|
+
if (event === "network_unavailable") return "offline";
|
|
41
|
+
break;
|
|
42
|
+
case "synced":
|
|
43
|
+
case "aborted":
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
throw new Error(`invalid sync transition: '${state}' --${event}-->`);
|
|
47
|
+
}
|
|
48
|
+
function planSync(stores) {
|
|
49
|
+
return { stores: stores.map((s) => ({ ...s, state: "pending" })) };
|
|
50
|
+
}
|
|
51
|
+
function applySyncEvent(session, alias, event) {
|
|
52
|
+
return {
|
|
53
|
+
stores: session.stores.map(
|
|
54
|
+
(s) => s.alias === alias ? { ...s, state: syncTransition(s.state, event) } : s
|
|
55
|
+
)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function continueSync(session) {
|
|
59
|
+
const conflicted = session.stores.find((s) => s.state === "conflict");
|
|
60
|
+
if (conflicted === void 0) {
|
|
61
|
+
throw new Error("`sync --continue` with no conflicted store to resume");
|
|
62
|
+
}
|
|
63
|
+
return applySyncEvent(session, conflicted.alias, "user_continue");
|
|
64
|
+
}
|
|
65
|
+
function abortSync(session) {
|
|
66
|
+
const conflicted = session.stores.find((s) => s.state === "conflict");
|
|
67
|
+
if (conflicted === void 0) {
|
|
68
|
+
throw new Error("`sync --abort` with no conflicted store to abort");
|
|
69
|
+
}
|
|
70
|
+
return applySyncEvent(session, conflicted.alias, "user_abort");
|
|
71
|
+
}
|
|
72
|
+
function isSyncSettled(session) {
|
|
73
|
+
return session.stores.every((s) => s.state !== "pending" && s.state !== "conflict");
|
|
74
|
+
}
|
|
75
|
+
function deferredPushStores(session) {
|
|
76
|
+
return session.stores.filter((s) => s.state === "offline");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/sync/run-sync.ts
|
|
80
|
+
var NO_GLOBAL_CONFIG = "no global Fabric config \u2014 run `fabric install --global <url>` first";
|
|
81
|
+
var NO_SESSION = "no sync in progress \u2014 run `fabric sync` first";
|
|
82
|
+
var NO_CONFLICT = "no conflicted store to resume \u2014 sync is not paused";
|
|
83
|
+
function syncSessionPath(globalRoot) {
|
|
84
|
+
return join(globalRoot, GLOBAL_STATE_DIR, "sync-session.json");
|
|
85
|
+
}
|
|
86
|
+
function loadSession(globalRoot) {
|
|
87
|
+
const path = syncSessionPath(globalRoot);
|
|
88
|
+
if (!existsSync(path)) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const raw = readFileSync(path, "utf8");
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(raw);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
const corruptedPath = `${path}.corrupted.${Date.now()}`;
|
|
96
|
+
try {
|
|
97
|
+
writeFileSync(corruptedPath, raw, "utf8");
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
throw new GenericIOError(
|
|
101
|
+
`sync-session.json is corrupt (forensic copy: ${corruptedPath}). Parse error: ${error instanceof Error ? error.message : String(error)}`,
|
|
102
|
+
{
|
|
103
|
+
actionHint: `Delete ${path} to start a fresh sync (any in-progress rebase must be resolved manually with git first).`,
|
|
104
|
+
details: { path, corruptedPath }
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function saveSession(globalRoot, session) {
|
|
110
|
+
const path = syncSessionPath(globalRoot);
|
|
111
|
+
mkdirSync(join(path, ".."), { recursive: true });
|
|
112
|
+
const tmpPath = `${path}.${process.pid}.tmp`;
|
|
113
|
+
writeFileSync(tmpPath, `${JSON.stringify(session, null, 2)}
|
|
114
|
+
`, "utf8");
|
|
115
|
+
renameSync(tmpPath, path);
|
|
116
|
+
}
|
|
117
|
+
function clearSession(globalRoot) {
|
|
118
|
+
rmSync(syncSessionPath(globalRoot), { force: true });
|
|
119
|
+
}
|
|
120
|
+
function defaultPull(storeDir) {
|
|
121
|
+
try {
|
|
122
|
+
execFileSync("git", ["pull", "--rebase"], {
|
|
123
|
+
cwd: storeDir,
|
|
124
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
125
|
+
});
|
|
126
|
+
return "clean";
|
|
127
|
+
} catch (error) {
|
|
128
|
+
const detail = `${gitErrText(error, "stdout")}${gitErrText(error, "stderr")}`;
|
|
129
|
+
if (/CONFLICT|could not apply|needs merge|rebase --continue/i.test(detail)) {
|
|
130
|
+
return "conflict";
|
|
131
|
+
}
|
|
132
|
+
if (/could not resolve host|could not read from remote|unable to access|connection|network is unreachable|timed out/i.test(
|
|
133
|
+
detail
|
|
134
|
+
)) {
|
|
135
|
+
return "offline";
|
|
136
|
+
}
|
|
137
|
+
const gitMessage = detail.trim().length > 0 ? detail.trim() : "unknown git error";
|
|
138
|
+
throw new GenericIOError(`git pull --rebase failed in ${storeDir}: ${gitMessage}`, {
|
|
139
|
+
actionHint: "resolve the git issue above (e.g. authentication, a dirty working tree, or a detached HEAD), then re-run `fabric sync`",
|
|
140
|
+
details: error
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function gitErrText(error, key) {
|
|
145
|
+
const value = error[key];
|
|
146
|
+
return typeof value === "string" || Buffer.isBuffer(value) ? String(value) : "";
|
|
147
|
+
}
|
|
148
|
+
function defaultPush(storeDir) {
|
|
149
|
+
try {
|
|
150
|
+
execFileSync("git", ["push"], {
|
|
151
|
+
cwd: storeDir,
|
|
152
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
153
|
+
});
|
|
154
|
+
return "clean";
|
|
155
|
+
} catch (error) {
|
|
156
|
+
const detail = `${gitErrText(error, "stdout")}${gitErrText(error, "stderr")}`;
|
|
157
|
+
if (/could not resolve host|could not read from remote|unable to access|connection|network is unreachable|timed out/i.test(
|
|
158
|
+
detail
|
|
159
|
+
)) {
|
|
160
|
+
return "offline";
|
|
161
|
+
}
|
|
162
|
+
const gitMessage = detail.trim().length > 0 ? detail.trim() : "unknown git error";
|
|
163
|
+
throw new GenericIOError(`git push failed in ${storeDir}: ${gitMessage}`, {
|
|
164
|
+
actionHint: "resolve the git issue above (e.g. authentication, no upstream branch, or a rejected non-fast-forward push), then re-run `fabric sync`",
|
|
165
|
+
details: error
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function defaultCommitDirty(storeDir) {
|
|
170
|
+
try {
|
|
171
|
+
execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
172
|
+
cwd: storeDir,
|
|
173
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
174
|
+
});
|
|
175
|
+
} catch {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
execFileSync("git", ["add", "-A"], { cwd: storeDir, stdio: ["ignore", "ignore", "pipe"] });
|
|
180
|
+
try {
|
|
181
|
+
execFileSync("git", ["diff", "--cached", "--quiet"], {
|
|
182
|
+
cwd: storeDir,
|
|
183
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
184
|
+
});
|
|
185
|
+
return;
|
|
186
|
+
} catch {
|
|
187
|
+
execFileSync("git", ["commit", "-m", "fabric: sync local knowledge changes"], {
|
|
188
|
+
cwd: storeDir,
|
|
189
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function runRebaseStep(storeDir, step) {
|
|
196
|
+
try {
|
|
197
|
+
execFileSync("git", ["rebase", `--${step}`], {
|
|
198
|
+
cwd: storeDir,
|
|
199
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
200
|
+
});
|
|
201
|
+
} catch (error) {
|
|
202
|
+
const detail = `${gitErrText(error, "stdout")}${gitErrText(error, "stderr")}`.trim();
|
|
203
|
+
const gitMessage = detail.length > 0 ? detail : "unknown git error";
|
|
204
|
+
throw new GenericIOError(`git rebase --${step} failed in ${storeDir}: ${gitMessage}`, {
|
|
205
|
+
actionHint: step === "continue" ? "resolve the remaining conflicts (git status) and stage them, then re-run `fabric sync --continue`; or run `fabric sync --abort` to discard the rebase" : "inspect the store with `git status`; if no rebase is in progress the session may already be resolved \u2014 delete sync-session.json to reset",
|
|
206
|
+
details: error
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function defaultRebaseContinue(storeDir) {
|
|
211
|
+
runRebaseStep(storeDir, "continue");
|
|
212
|
+
}
|
|
213
|
+
function defaultRebaseAbort(storeDir) {
|
|
214
|
+
runRebaseStep(storeDir, "abort");
|
|
215
|
+
}
|
|
216
|
+
var OUTCOME_EVENT = {
|
|
217
|
+
clean: "rebase_clean",
|
|
218
|
+
conflict: "rebase_conflict",
|
|
219
|
+
offline: "network_unavailable"
|
|
220
|
+
};
|
|
221
|
+
function walkPending(session, storeDirOf, pull, push, commit, pushableAliases) {
|
|
222
|
+
let next = session;
|
|
223
|
+
for (const store of session.stores) {
|
|
224
|
+
if (store.state !== "pending") {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const dir = storeDirOf(store);
|
|
228
|
+
commit(dir);
|
|
229
|
+
const pullOutcome = pull(dir);
|
|
230
|
+
if (pullOutcome !== "clean") {
|
|
231
|
+
next = applySyncEvent(next, store.alias, OUTCOME_EVENT[pullOutcome]);
|
|
232
|
+
if (pullOutcome === "conflict") {
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (!pushableAliases.has(store.alias)) {
|
|
238
|
+
next = applySyncEvent(next, store.alias, "rebase_clean");
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const pushOutcome = push(dir);
|
|
242
|
+
next = applySyncEvent(
|
|
243
|
+
next,
|
|
244
|
+
store.alias,
|
|
245
|
+
pushOutcome === "clean" ? "rebase_clean" : "network_unavailable"
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
return next;
|
|
249
|
+
}
|
|
250
|
+
function finalize(session, options, globalRoot) {
|
|
251
|
+
const settled = isSyncSettled(session);
|
|
252
|
+
let snapshotWritten = false;
|
|
253
|
+
if (settled) {
|
|
254
|
+
clearSession(globalRoot);
|
|
255
|
+
const snapshot = regenerateBindingsSnapshot(options.projectRoot, {
|
|
256
|
+
globalRoot,
|
|
257
|
+
now: options.now,
|
|
258
|
+
...options.writeScope === void 0 ? {} : { writeScope: options.writeScope }
|
|
259
|
+
});
|
|
260
|
+
snapshotWritten = snapshot !== null;
|
|
261
|
+
} else {
|
|
262
|
+
saveSession(globalRoot, session);
|
|
263
|
+
}
|
|
264
|
+
return { session, settled, deferred: deferredPushStores(session), snapshotWritten };
|
|
265
|
+
}
|
|
266
|
+
function runStartSync(options) {
|
|
267
|
+
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
268
|
+
const config = loadGlobalConfig(globalRoot);
|
|
269
|
+
if (config === null) {
|
|
270
|
+
throw new Error(NO_GLOBAL_CONFIG);
|
|
271
|
+
}
|
|
272
|
+
const syncable = config.stores.filter((store) => store.remote !== void 0);
|
|
273
|
+
const session = planSync(
|
|
274
|
+
syncable.map((store) => ({ alias: store.alias, store_uuid: store.store_uuid }))
|
|
275
|
+
);
|
|
276
|
+
const storeDirOf = (status) => join(globalRoot, storeRelativePath(status.store_uuid));
|
|
277
|
+
const pushableAliases = pushableAliasesOf(config);
|
|
278
|
+
const walked = walkPending(
|
|
279
|
+
session,
|
|
280
|
+
storeDirOf,
|
|
281
|
+
options.pull ?? defaultPull,
|
|
282
|
+
options.push ?? defaultPush,
|
|
283
|
+
options.commit ?? defaultCommitDirty,
|
|
284
|
+
pushableAliases
|
|
285
|
+
);
|
|
286
|
+
return finalize(walked, options, globalRoot);
|
|
287
|
+
}
|
|
288
|
+
function pushableAliasesOf(config) {
|
|
289
|
+
return new Set(
|
|
290
|
+
config.stores.filter((store) => store.remote !== void 0 && (store.writable ?? true)).map((store) => store.alias)
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
function runContinueSync(options) {
|
|
294
|
+
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
295
|
+
const session = loadSession(globalRoot);
|
|
296
|
+
if (session === null) {
|
|
297
|
+
throw new GenericIOError(NO_SESSION, {
|
|
298
|
+
actionHint: "Run `fabric sync` to start a sync before `--continue`/`--abort`."
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
const conflicted = session.stores.find((store) => store.state === "conflict");
|
|
302
|
+
if (conflicted === void 0) {
|
|
303
|
+
throw new GenericIOError(NO_CONFLICT, {
|
|
304
|
+
actionHint: "The sync is not paused on a conflict; there is nothing to resume."
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
const storeDirOf = (status) => join(globalRoot, storeRelativePath(status.store_uuid));
|
|
308
|
+
(options.rebaseContinue ?? defaultRebaseContinue)(storeDirOf(conflicted));
|
|
309
|
+
const pushableAliases = pushableAliasesOf(loadGlobalConfig(globalRoot) ?? { stores: [] });
|
|
310
|
+
const push = options.push ?? defaultPush;
|
|
311
|
+
let advanced;
|
|
312
|
+
if (pushableAliases.has(conflicted.alias)) {
|
|
313
|
+
const pushOutcome = push(storeDirOf(conflicted));
|
|
314
|
+
advanced = applySyncEvent(
|
|
315
|
+
session,
|
|
316
|
+
conflicted.alias,
|
|
317
|
+
pushOutcome === "clean" ? "user_continue" : "network_unavailable"
|
|
318
|
+
);
|
|
319
|
+
} else {
|
|
320
|
+
advanced = continueSync(session);
|
|
321
|
+
}
|
|
322
|
+
const resumed = walkPending(
|
|
323
|
+
advanced,
|
|
324
|
+
storeDirOf,
|
|
325
|
+
options.pull ?? defaultPull,
|
|
326
|
+
push,
|
|
327
|
+
options.commit ?? defaultCommitDirty,
|
|
328
|
+
pushableAliases
|
|
329
|
+
);
|
|
330
|
+
return finalize(resumed, options, globalRoot);
|
|
331
|
+
}
|
|
332
|
+
function runAbortSync(options) {
|
|
333
|
+
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
334
|
+
const session = loadSession(globalRoot);
|
|
335
|
+
if (session === null) {
|
|
336
|
+
throw new GenericIOError(NO_SESSION, {
|
|
337
|
+
actionHint: "Run `fabric sync` to start a sync before `--continue`/`--abort`."
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const conflicted = session.stores.find((store) => store.state === "conflict");
|
|
341
|
+
if (conflicted === void 0) {
|
|
342
|
+
throw new GenericIOError(NO_CONFLICT, {
|
|
343
|
+
actionHint: "The sync is not paused on a conflict; there is nothing to resume."
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
const storeDirOf = (status) => join(globalRoot, storeRelativePath(status.store_uuid));
|
|
347
|
+
(options.rebaseAbort ?? defaultRebaseAbort)(storeDirOf(conflicted));
|
|
348
|
+
const pushableAliases = pushableAliasesOf(loadGlobalConfig(globalRoot) ?? { stores: [] });
|
|
349
|
+
const resumed = walkPending(
|
|
350
|
+
abortSync(session),
|
|
351
|
+
storeDirOf,
|
|
352
|
+
options.pull ?? defaultPull,
|
|
353
|
+
options.push ?? defaultPush,
|
|
354
|
+
options.commit ?? defaultCommitDirty,
|
|
355
|
+
pushableAliases
|
|
356
|
+
);
|
|
357
|
+
return finalize(resumed, options, globalRoot);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/commands/sync.ts
|
|
361
|
+
function report(result, projectRoot) {
|
|
362
|
+
const t = getProjectTranslator(projectRoot);
|
|
363
|
+
for (const store of result.session.stores) {
|
|
364
|
+
console.log(`${store.alias} ${store.state}`);
|
|
365
|
+
}
|
|
366
|
+
if (result.deferred.length > 0) {
|
|
367
|
+
console.log(t("cli.sync.deferred", { count: String(result.deferred.length) }));
|
|
368
|
+
}
|
|
369
|
+
if (!result.settled) {
|
|
370
|
+
console.log(t("cli.sync.paused"));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
var sync_default = defineCommand({
|
|
374
|
+
meta: { name: "sync", description: "Pull --rebase + push every mounted store; resume conflicts" },
|
|
375
|
+
args: {
|
|
376
|
+
continue: { type: "boolean", description: "Resume after resolving a rebase conflict" },
|
|
377
|
+
abort: { type: "boolean", description: "Abort the conflicted store's rebase" }
|
|
378
|
+
},
|
|
379
|
+
run({ args }) {
|
|
380
|
+
const projectRoot = process.cwd();
|
|
381
|
+
const options = { projectRoot, now: (/* @__PURE__ */ new Date()).toISOString() };
|
|
382
|
+
if (args.continue === true) {
|
|
383
|
+
report(runContinueSync(options), projectRoot);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (args.abort === true) {
|
|
387
|
+
report(runAbortSync(options), projectRoot);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
report(runStartSync(options), projectRoot);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
export {
|
|
394
|
+
sync_default as default
|
|
395
|
+
};
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
HOOK_SCRIPT_DESTINATIONS,
|
|
8
8
|
SKILL_DESTINATIONS,
|
|
9
9
|
fabricAgentsSnapshotPath
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-XHHCRDIR.js";
|
|
11
11
|
import {
|
|
12
12
|
paint
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-BO4XIZWZ.js";
|
|
14
14
|
import {
|
|
15
15
|
createDebugLogger,
|
|
16
16
|
resolveDevMode
|
|
@@ -18,10 +18,10 @@ import {
|
|
|
18
18
|
import {
|
|
19
19
|
detectClientSupports,
|
|
20
20
|
resolveClients
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-XC5RUHLK.js";
|
|
22
22
|
import {
|
|
23
23
|
t
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-2CY4BMTH.js";
|
|
25
25
|
|
|
26
26
|
// src/commands/uninstall.ts
|
|
27
27
|
import { existsSync as existsSync2, statSync } from "fs";
|
|
@@ -49,6 +49,12 @@ async function uninstallFabricImportSkill(projectRoot) {
|
|
|
49
49
|
async function uninstallFabricSyncSkill(projectRoot) {
|
|
50
50
|
return removeSkill("skill-sync", SKILL_DESTINATIONS.fabricSync, projectRoot);
|
|
51
51
|
}
|
|
52
|
+
async function uninstallFabricAuditSkill(projectRoot) {
|
|
53
|
+
return removeSkill("skill-audit", SKILL_DESTINATIONS.fabricAudit, projectRoot);
|
|
54
|
+
}
|
|
55
|
+
async function uninstallFabricConnectSkill(projectRoot) {
|
|
56
|
+
return removeSkill("skill-connect", SKILL_DESTINATIONS.fabricConnect, projectRoot);
|
|
57
|
+
}
|
|
52
58
|
async function removeSkill(step, rels, projectRoot) {
|
|
53
59
|
const results = [];
|
|
54
60
|
for (const rel of rels) {
|
|
@@ -75,6 +81,27 @@ async function removeKnowledgeHintNarrowHook(projectRoot) {
|
|
|
75
81
|
projectRoot
|
|
76
82
|
);
|
|
77
83
|
}
|
|
84
|
+
async function removeCitePolicyEvictHook(projectRoot) {
|
|
85
|
+
return removeHookScripts(
|
|
86
|
+
"hook-cite-policy-evict-script",
|
|
87
|
+
HOOK_SCRIPT_DESTINATIONS.citePolicyEvict,
|
|
88
|
+
projectRoot
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
async function removeSessionEndMarkerHook(projectRoot) {
|
|
92
|
+
return removeHookScripts(
|
|
93
|
+
"hook-session-end-script",
|
|
94
|
+
HOOK_SCRIPT_DESTINATIONS.sessionEndMarker,
|
|
95
|
+
projectRoot
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
async function removePostTooluseMutationHook(projectRoot) {
|
|
99
|
+
return removeHookScripts(
|
|
100
|
+
"hook-post-tooluse-script",
|
|
101
|
+
HOOK_SCRIPT_DESTINATIONS.postTooluseMutation,
|
|
102
|
+
projectRoot
|
|
103
|
+
);
|
|
104
|
+
}
|
|
78
105
|
async function removeHookScripts(step, rels, projectRoot) {
|
|
79
106
|
const results = [];
|
|
80
107
|
for (const rel of rels) {
|
|
@@ -304,6 +331,36 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
|
|
|
304
331
|
projectRoot,
|
|
305
332
|
() => removeArchiveHintHook(projectRoot)
|
|
306
333
|
);
|
|
334
|
+
await runAndCollect(
|
|
335
|
+
results,
|
|
336
|
+
"hook-cite-policy-evict-script",
|
|
337
|
+
projectRoot,
|
|
338
|
+
() => removeCitePolicyEvictHook(projectRoot)
|
|
339
|
+
);
|
|
340
|
+
await runAndCollect(
|
|
341
|
+
results,
|
|
342
|
+
"hook-session-end-script",
|
|
343
|
+
projectRoot,
|
|
344
|
+
() => removeSessionEndMarkerHook(projectRoot)
|
|
345
|
+
);
|
|
346
|
+
await runAndCollect(
|
|
347
|
+
results,
|
|
348
|
+
"hook-post-tooluse-script",
|
|
349
|
+
projectRoot,
|
|
350
|
+
() => removePostTooluseMutationHook(projectRoot)
|
|
351
|
+
);
|
|
352
|
+
await runAndCollect(
|
|
353
|
+
results,
|
|
354
|
+
"skill-connect",
|
|
355
|
+
projectRoot,
|
|
356
|
+
() => uninstallFabricConnectSkill(projectRoot)
|
|
357
|
+
);
|
|
358
|
+
await runAndCollect(
|
|
359
|
+
results,
|
|
360
|
+
"skill-audit",
|
|
361
|
+
projectRoot,
|
|
362
|
+
() => uninstallFabricAuditSkill(projectRoot)
|
|
363
|
+
);
|
|
307
364
|
await runAndCollect(
|
|
308
365
|
results,
|
|
309
366
|
"skill-sync",
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getProjectTranslator
|
|
4
|
+
} from "./chunk-2CY4BMTH.js";
|
|
5
|
+
import {
|
|
6
|
+
warnUnknownFlags,
|
|
7
|
+
whoami
|
|
8
|
+
} from "./chunk-5LQIHYFC.js";
|
|
9
|
+
import "./chunk-5ZUMLCD5.js";
|
|
10
|
+
import "./chunk-XCBVSGCS.js";
|
|
11
|
+
|
|
12
|
+
// src/commands/whoami.ts
|
|
13
|
+
import { defineCommand } from "citty";
|
|
14
|
+
var whoami_default = defineCommand({
|
|
15
|
+
meta: { name: "whoami", description: "Show this machine's Fabric uid and mounted stores" },
|
|
16
|
+
args: {
|
|
17
|
+
// F27: `--json` machine-readable output (was silently ignored — the command
|
|
18
|
+
// declared no args, so citty swallowed the flag and still printed text).
|
|
19
|
+
json: { type: "boolean", description: "Emit machine-readable JSON instead of text" }
|
|
20
|
+
},
|
|
21
|
+
run({ args }) {
|
|
22
|
+
warnUnknownFlags(["json"]);
|
|
23
|
+
const info = whoami();
|
|
24
|
+
if (args.json === true) {
|
|
25
|
+
console.log(JSON.stringify(info, null, 2));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const t = getProjectTranslator();
|
|
29
|
+
if (info === null) {
|
|
30
|
+
console.log(t("cli.cmd.no-global-config"));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
console.log(t("cli.whoami.uid", { uid: info.uid }));
|
|
34
|
+
if (info.stores.length === 0) {
|
|
35
|
+
console.log(t("cli.whoami.stores-none"));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
console.log(t("cli.whoami.stores-label"));
|
|
39
|
+
const localOnly = t("cli.shared.local-only");
|
|
40
|
+
for (const store of info.stores) {
|
|
41
|
+
console.log(` ${store.alias} ${store.store_uuid}${store.local_only ? ` ${localOnly}` : ""}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
export {
|
|
46
|
+
whoami_default as default
|
|
47
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fenglimg/fabric-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0-rc.3",
|
|
4
4
|
"description": "Fabric CLI — installs the MCP server + skills + hooks for Claude Code, Cursor, and Codex CLI; runs doctor / knowledge maintenance.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "wangzhichao <fenglimg90@gmail.com>",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"tree-sitter-javascript": "^0.25.0",
|
|
46
46
|
"tree-sitter-typescript": "^0.23.2",
|
|
47
47
|
"web-tree-sitter": "^0.26.8",
|
|
48
|
-
"@fenglimg/fabric-server": "2.
|
|
49
|
-
"@fenglimg/fabric-shared": "2.
|
|
48
|
+
"@fenglimg/fabric-server": "2.2.0-rc.3",
|
|
49
|
+
"@fenglimg/fabric-shared": "2.2.0-rc.3"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/node": "^22.15.0",
|