@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
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
paint
|
|
4
|
+
} from "./chunk-NLNH64A3.js";
|
|
5
|
+
import {
|
|
6
|
+
regenerateBindingsSnapshot
|
|
7
|
+
} from "./chunk-PTGQAZEW.js";
|
|
8
|
+
import "./chunk-EOT63RDH.js";
|
|
9
|
+
import "./chunk-QFIVFZRH.js";
|
|
10
|
+
import {
|
|
11
|
+
loadGlobalConfig,
|
|
12
|
+
resolveGlobalRoot
|
|
13
|
+
} from "./chunk-FNHDQTPC.js";
|
|
14
|
+
import {
|
|
15
|
+
getProjectTranslator
|
|
16
|
+
} from "./chunk-HORSMSZL.js";
|
|
17
|
+
|
|
18
|
+
// src/commands/sync.ts
|
|
19
|
+
import { defineCommand } from "citty";
|
|
20
|
+
|
|
21
|
+
// src/sync/run-sync.ts
|
|
22
|
+
import { execFileSync } from "child_process";
|
|
23
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
24
|
+
import { join } from "path";
|
|
25
|
+
import { GLOBAL_STATE_DIR, storeRelativePathForMount } from "@fenglimg/fabric-shared";
|
|
26
|
+
import { GenericIOError } from "@fenglimg/fabric-shared/errors";
|
|
27
|
+
|
|
28
|
+
// src/sync/state-machine.ts
|
|
29
|
+
function syncTransition(state, event) {
|
|
30
|
+
switch (state) {
|
|
31
|
+
case "pending":
|
|
32
|
+
if (event === "rebase_clean") return "synced";
|
|
33
|
+
if (event === "rebase_conflict") return "conflict";
|
|
34
|
+
if (event === "network_unavailable") return "offline";
|
|
35
|
+
break;
|
|
36
|
+
case "conflict":
|
|
37
|
+
if (event === "user_continue") return "synced";
|
|
38
|
+
if (event === "user_abort") return "aborted";
|
|
39
|
+
if (event === "network_unavailable") return "offline";
|
|
40
|
+
break;
|
|
41
|
+
case "offline":
|
|
42
|
+
if (event === "retry" || event === "rebase_clean") return "synced";
|
|
43
|
+
if (event === "rebase_conflict") return "conflict";
|
|
44
|
+
if (event === "network_unavailable") return "offline";
|
|
45
|
+
break;
|
|
46
|
+
case "synced":
|
|
47
|
+
case "aborted":
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`invalid sync transition: '${state}' --${event}-->`);
|
|
51
|
+
}
|
|
52
|
+
function planSync(stores) {
|
|
53
|
+
return { stores: stores.map((s) => ({ ...s, state: "pending" })) };
|
|
54
|
+
}
|
|
55
|
+
function applySyncEvent(session, alias, event) {
|
|
56
|
+
return {
|
|
57
|
+
stores: session.stores.map(
|
|
58
|
+
(s) => s.alias === alias ? { ...s, state: syncTransition(s.state, event) } : s
|
|
59
|
+
)
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function continueSync(session) {
|
|
63
|
+
const conflicted = session.stores.find((s) => s.state === "conflict");
|
|
64
|
+
if (conflicted === void 0) {
|
|
65
|
+
throw new Error("`sync --continue` with no conflicted store to resume");
|
|
66
|
+
}
|
|
67
|
+
return applySyncEvent(session, conflicted.alias, "user_continue");
|
|
68
|
+
}
|
|
69
|
+
function abortSync(session) {
|
|
70
|
+
const conflicted = session.stores.find((s) => s.state === "conflict");
|
|
71
|
+
if (conflicted === void 0) {
|
|
72
|
+
throw new Error("`sync --abort` with no conflicted store to abort");
|
|
73
|
+
}
|
|
74
|
+
return applySyncEvent(session, conflicted.alias, "user_abort");
|
|
75
|
+
}
|
|
76
|
+
function isSyncSettled(session) {
|
|
77
|
+
return session.stores.every((s) => s.state !== "pending" && s.state !== "conflict");
|
|
78
|
+
}
|
|
79
|
+
function deferredPushStores(session) {
|
|
80
|
+
return session.stores.filter((s) => s.state === "offline");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/sync/run-sync.ts
|
|
84
|
+
var NO_GLOBAL_CONFIG = "no global Fabric config \u2014 run `fabric install --global <url>` first";
|
|
85
|
+
var NO_SESSION = "no sync in progress \u2014 run `fabric sync` first";
|
|
86
|
+
var NO_CONFLICT = "no conflicted store to resume \u2014 sync is not paused";
|
|
87
|
+
function syncSessionPath(globalRoot) {
|
|
88
|
+
return join(globalRoot, GLOBAL_STATE_DIR, "sync-session.json");
|
|
89
|
+
}
|
|
90
|
+
function loadSession(globalRoot) {
|
|
91
|
+
const path = syncSessionPath(globalRoot);
|
|
92
|
+
if (!existsSync(path)) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const raw = readFileSync(path, "utf8");
|
|
96
|
+
try {
|
|
97
|
+
return JSON.parse(raw);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
const corruptedPath = `${path}.corrupted.${Date.now()}`;
|
|
100
|
+
try {
|
|
101
|
+
writeFileSync(corruptedPath, raw, "utf8");
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
throw new GenericIOError(
|
|
105
|
+
`sync-session.json is corrupt (forensic copy: ${corruptedPath}). Parse error: ${error instanceof Error ? error.message : String(error)}`,
|
|
106
|
+
{
|
|
107
|
+
actionHint: `Delete ${path} to start a fresh sync (any in-progress rebase must be resolved manually with git first).`,
|
|
108
|
+
details: { path, corruptedPath }
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function saveSession(globalRoot, session) {
|
|
114
|
+
const path = syncSessionPath(globalRoot);
|
|
115
|
+
mkdirSync(join(path, ".."), { recursive: true });
|
|
116
|
+
const tmpPath = `${path}.${process.pid}.tmp`;
|
|
117
|
+
writeFileSync(tmpPath, `${JSON.stringify(session, null, 2)}
|
|
118
|
+
`, "utf8");
|
|
119
|
+
renameSync(tmpPath, path);
|
|
120
|
+
}
|
|
121
|
+
function clearSession(globalRoot) {
|
|
122
|
+
rmSync(syncSessionPath(globalRoot), { force: true });
|
|
123
|
+
}
|
|
124
|
+
function defaultPull(storeDir) {
|
|
125
|
+
try {
|
|
126
|
+
execFileSync("git", ["pull", "--rebase"], {
|
|
127
|
+
cwd: storeDir,
|
|
128
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
129
|
+
});
|
|
130
|
+
return "clean";
|
|
131
|
+
} catch (error) {
|
|
132
|
+
const detail = `${gitErrText(error, "stdout")}${gitErrText(error, "stderr")}`;
|
|
133
|
+
if (/CONFLICT|could not apply|needs merge|rebase --continue/i.test(detail)) {
|
|
134
|
+
return "conflict";
|
|
135
|
+
}
|
|
136
|
+
if (/could not resolve host|could not read from remote|unable to access|connection|network is unreachable|timed out/i.test(
|
|
137
|
+
detail
|
|
138
|
+
)) {
|
|
139
|
+
return "offline";
|
|
140
|
+
}
|
|
141
|
+
const gitMessage = detail.trim().length > 0 ? detail.trim() : "unknown git error";
|
|
142
|
+
throw new GenericIOError(`git pull --rebase failed in ${storeDir}: ${gitMessage}`, {
|
|
143
|
+
actionHint: "resolve the git issue above (e.g. authentication, a dirty working tree, or a detached HEAD), then re-run `fabric sync`",
|
|
144
|
+
details: error
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function gitErrText(error, key) {
|
|
149
|
+
const value = error[key];
|
|
150
|
+
return typeof value === "string" || Buffer.isBuffer(value) ? String(value) : "";
|
|
151
|
+
}
|
|
152
|
+
function defaultPush(storeDir) {
|
|
153
|
+
try {
|
|
154
|
+
execFileSync("git", ["push"], {
|
|
155
|
+
cwd: storeDir,
|
|
156
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
157
|
+
});
|
|
158
|
+
return "clean";
|
|
159
|
+
} catch (error) {
|
|
160
|
+
const detail = `${gitErrText(error, "stdout")}${gitErrText(error, "stderr")}`;
|
|
161
|
+
if (/could not resolve host|could not read from remote|unable to access|connection|network is unreachable|timed out/i.test(
|
|
162
|
+
detail
|
|
163
|
+
)) {
|
|
164
|
+
return "offline";
|
|
165
|
+
}
|
|
166
|
+
const gitMessage = detail.trim().length > 0 ? detail.trim() : "unknown git error";
|
|
167
|
+
throw new GenericIOError(`git push failed in ${storeDir}: ${gitMessage}`, {
|
|
168
|
+
actionHint: "resolve the git issue above (e.g. authentication, no upstream branch, or a rejected non-fast-forward push), then re-run `fabric sync`",
|
|
169
|
+
details: error
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function defaultCommitDirty(storeDir) {
|
|
174
|
+
try {
|
|
175
|
+
execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
176
|
+
cwd: storeDir,
|
|
177
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
178
|
+
});
|
|
179
|
+
} catch {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
execFileSync("git", ["add", "-A"], { cwd: storeDir, stdio: ["ignore", "ignore", "pipe"] });
|
|
184
|
+
try {
|
|
185
|
+
execFileSync("git", ["diff", "--cached", "--quiet"], {
|
|
186
|
+
cwd: storeDir,
|
|
187
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
188
|
+
});
|
|
189
|
+
return;
|
|
190
|
+
} catch {
|
|
191
|
+
execFileSync("git", ["commit", "-m", "fabric: sync local knowledge changes"], {
|
|
192
|
+
cwd: storeDir,
|
|
193
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function runRebaseStep(storeDir, step) {
|
|
200
|
+
try {
|
|
201
|
+
execFileSync("git", ["rebase", `--${step}`], {
|
|
202
|
+
cwd: storeDir,
|
|
203
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
204
|
+
});
|
|
205
|
+
} catch (error) {
|
|
206
|
+
const detail = `${gitErrText(error, "stdout")}${gitErrText(error, "stderr")}`.trim();
|
|
207
|
+
const gitMessage = detail.length > 0 ? detail : "unknown git error";
|
|
208
|
+
throw new GenericIOError(`git rebase --${step} failed in ${storeDir}: ${gitMessage}`, {
|
|
209
|
+
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",
|
|
210
|
+
details: error
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function defaultRebaseContinue(storeDir) {
|
|
215
|
+
runRebaseStep(storeDir, "continue");
|
|
216
|
+
}
|
|
217
|
+
function defaultRebaseAbort(storeDir) {
|
|
218
|
+
runRebaseStep(storeDir, "abort");
|
|
219
|
+
}
|
|
220
|
+
var OUTCOME_EVENT = {
|
|
221
|
+
clean: "rebase_clean",
|
|
222
|
+
conflict: "rebase_conflict",
|
|
223
|
+
offline: "network_unavailable"
|
|
224
|
+
};
|
|
225
|
+
function walkPending(session, storeDirOf, pull, push, commit, pushableAliases) {
|
|
226
|
+
let next = session;
|
|
227
|
+
for (const store of session.stores) {
|
|
228
|
+
if (store.state !== "pending") {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const dir = storeDirOf(store);
|
|
232
|
+
commit(dir);
|
|
233
|
+
const pullOutcome = pull(dir);
|
|
234
|
+
if (pullOutcome !== "clean") {
|
|
235
|
+
next = applySyncEvent(next, store.alias, OUTCOME_EVENT[pullOutcome]);
|
|
236
|
+
if (pullOutcome === "conflict") {
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (!pushableAliases.has(store.alias)) {
|
|
242
|
+
next = applySyncEvent(next, store.alias, "rebase_clean");
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const pushOutcome = push(dir);
|
|
246
|
+
next = applySyncEvent(
|
|
247
|
+
next,
|
|
248
|
+
store.alias,
|
|
249
|
+
pushOutcome === "clean" ? "rebase_clean" : "network_unavailable"
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
return next;
|
|
253
|
+
}
|
|
254
|
+
function finalize(session, options, globalRoot) {
|
|
255
|
+
const settled = isSyncSettled(session);
|
|
256
|
+
let snapshotWritten = false;
|
|
257
|
+
if (settled) {
|
|
258
|
+
clearSession(globalRoot);
|
|
259
|
+
const snapshot = regenerateBindingsSnapshot(options.projectRoot, {
|
|
260
|
+
globalRoot,
|
|
261
|
+
now: options.now,
|
|
262
|
+
...options.writeScope === void 0 ? {} : { writeScope: options.writeScope }
|
|
263
|
+
});
|
|
264
|
+
snapshotWritten = snapshot !== null;
|
|
265
|
+
} else {
|
|
266
|
+
saveSession(globalRoot, session);
|
|
267
|
+
}
|
|
268
|
+
return { session, settled, deferred: deferredPushStores(session), snapshotWritten };
|
|
269
|
+
}
|
|
270
|
+
function runStartSync(options) {
|
|
271
|
+
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
272
|
+
const config = loadGlobalConfig(globalRoot);
|
|
273
|
+
if (config === null) {
|
|
274
|
+
throw new Error(NO_GLOBAL_CONFIG);
|
|
275
|
+
}
|
|
276
|
+
const syncable = config.stores.filter((store) => store.remote !== void 0);
|
|
277
|
+
const session = planSync(
|
|
278
|
+
syncable.map((store) => ({ alias: store.alias, store_uuid: store.store_uuid }))
|
|
279
|
+
);
|
|
280
|
+
const storeDirOf = makeStoreDirResolver(globalRoot, config.stores);
|
|
281
|
+
const pushableAliases = pushableAliasesOf(config);
|
|
282
|
+
const walked = walkPending(
|
|
283
|
+
session,
|
|
284
|
+
storeDirOf,
|
|
285
|
+
options.pull ?? defaultPull,
|
|
286
|
+
options.push ?? defaultPush,
|
|
287
|
+
options.commit ?? defaultCommitDirty,
|
|
288
|
+
pushableAliases
|
|
289
|
+
);
|
|
290
|
+
return finalize(walked, options, globalRoot);
|
|
291
|
+
}
|
|
292
|
+
function pushableAliasesOf(config) {
|
|
293
|
+
return new Set(
|
|
294
|
+
config.stores.filter((store) => store.remote !== void 0 && (store.writable ?? true)).map((store) => store.alias)
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
function makeStoreDirResolver(globalRoot, stores) {
|
|
298
|
+
return (status) => join(
|
|
299
|
+
globalRoot,
|
|
300
|
+
storeRelativePathForMount(
|
|
301
|
+
stores.find((store) => store.store_uuid === status.store_uuid) ?? {
|
|
302
|
+
store_uuid: status.store_uuid
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
function runContinueSync(options) {
|
|
308
|
+
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
309
|
+
const session = loadSession(globalRoot);
|
|
310
|
+
if (session === null) {
|
|
311
|
+
throw new GenericIOError(NO_SESSION, {
|
|
312
|
+
actionHint: "Run `fabric sync` to start a sync before `--continue`/`--abort`."
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
const conflicted = session.stores.find((store) => store.state === "conflict");
|
|
316
|
+
if (conflicted === void 0) {
|
|
317
|
+
throw new GenericIOError(NO_CONFLICT, {
|
|
318
|
+
actionHint: "The sync is not paused on a conflict; there is nothing to resume."
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
const resumeConfig = loadGlobalConfig(globalRoot) ?? { stores: [] };
|
|
322
|
+
const storeDirOf = makeStoreDirResolver(globalRoot, resumeConfig.stores);
|
|
323
|
+
(options.rebaseContinue ?? defaultRebaseContinue)(storeDirOf(conflicted));
|
|
324
|
+
const pushableAliases = pushableAliasesOf(resumeConfig);
|
|
325
|
+
const push = options.push ?? defaultPush;
|
|
326
|
+
let advanced;
|
|
327
|
+
if (pushableAliases.has(conflicted.alias)) {
|
|
328
|
+
const pushOutcome = push(storeDirOf(conflicted));
|
|
329
|
+
advanced = applySyncEvent(
|
|
330
|
+
session,
|
|
331
|
+
conflicted.alias,
|
|
332
|
+
pushOutcome === "clean" ? "user_continue" : "network_unavailable"
|
|
333
|
+
);
|
|
334
|
+
} else {
|
|
335
|
+
advanced = continueSync(session);
|
|
336
|
+
}
|
|
337
|
+
const resumed = walkPending(
|
|
338
|
+
advanced,
|
|
339
|
+
storeDirOf,
|
|
340
|
+
options.pull ?? defaultPull,
|
|
341
|
+
push,
|
|
342
|
+
options.commit ?? defaultCommitDirty,
|
|
343
|
+
pushableAliases
|
|
344
|
+
);
|
|
345
|
+
return finalize(resumed, options, globalRoot);
|
|
346
|
+
}
|
|
347
|
+
function runAbortSync(options) {
|
|
348
|
+
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
349
|
+
const session = loadSession(globalRoot);
|
|
350
|
+
if (session === null) {
|
|
351
|
+
throw new GenericIOError(NO_SESSION, {
|
|
352
|
+
actionHint: "Run `fabric sync` to start a sync before `--continue`/`--abort`."
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
const conflicted = session.stores.find((store) => store.state === "conflict");
|
|
356
|
+
if (conflicted === void 0) {
|
|
357
|
+
throw new GenericIOError(NO_CONFLICT, {
|
|
358
|
+
actionHint: "The sync is not paused on a conflict; there is nothing to resume."
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
const resumeConfig = loadGlobalConfig(globalRoot) ?? { stores: [] };
|
|
362
|
+
const storeDirOf = makeStoreDirResolver(globalRoot, resumeConfig.stores);
|
|
363
|
+
(options.rebaseAbort ?? defaultRebaseAbort)(storeDirOf(conflicted));
|
|
364
|
+
const pushableAliases = pushableAliasesOf(resumeConfig);
|
|
365
|
+
const resumed = walkPending(
|
|
366
|
+
abortSync(session),
|
|
367
|
+
storeDirOf,
|
|
368
|
+
options.pull ?? defaultPull,
|
|
369
|
+
options.push ?? defaultPush,
|
|
370
|
+
options.commit ?? defaultCommitDirty,
|
|
371
|
+
pushableAliases
|
|
372
|
+
);
|
|
373
|
+
return finalize(resumed, options, globalRoot);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/commands/sync.ts
|
|
377
|
+
function report(result, projectRoot) {
|
|
378
|
+
const t = getProjectTranslator(projectRoot);
|
|
379
|
+
for (const store of result.session.stores) {
|
|
380
|
+
console.log(`${store.alias} ${store.state}`);
|
|
381
|
+
}
|
|
382
|
+
if (result.deferred.length > 0) {
|
|
383
|
+
console.log(t("cli.sync.deferred", { count: String(result.deferred.length) }));
|
|
384
|
+
}
|
|
385
|
+
if (!result.settled) {
|
|
386
|
+
console.log(t("cli.sync.paused"));
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
var syncCommand = defineCommand({
|
|
390
|
+
meta: { name: "sync", description: "Pull --rebase + push every mounted store; resume conflicts" },
|
|
391
|
+
args: {
|
|
392
|
+
continue: { type: "boolean", description: "Resume after resolving a rebase conflict" },
|
|
393
|
+
abort: { type: "boolean", description: "Abort the conflicted store's rebase" }
|
|
394
|
+
},
|
|
395
|
+
run({ args }) {
|
|
396
|
+
const projectRoot = process.cwd();
|
|
397
|
+
if (args.continue === true && args.abort === true) {
|
|
398
|
+
console.error(paint.error("fabric sync: --continue and --abort cannot be used together"));
|
|
399
|
+
process.exitCode = 1;
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const options = { projectRoot, now: (/* @__PURE__ */ new Date()).toISOString() };
|
|
403
|
+
if (args.continue === true) {
|
|
404
|
+
report(runContinueSync(options), projectRoot);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (args.abort === true) {
|
|
408
|
+
report(runAbortSync(options), projectRoot);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
report(runStartSync(options), projectRoot);
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
var sync_default = syncCommand;
|
|
415
|
+
export {
|
|
416
|
+
sync_default as default,
|
|
417
|
+
syncCommand
|
|
418
|
+
};
|