@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.
Files changed (40) hide show
  1. package/dist/{chunk-PWLW3B57.js → chunk-2CY4BMTH.js} +5 -1
  2. package/dist/{chunk-D25XJ4BC.js → chunk-2R55HNVD.js} +105 -5
  3. package/dist/chunk-4R2CYEA4.js +116 -0
  4. package/dist/{chunk-BATF4PEJ.js → chunk-AOE6AYI7.js} +2 -2
  5. package/dist/{chunk-WWNXR34K.js → chunk-BO4XIZWZ.js} +8 -1
  6. package/dist/chunk-L4Q55UC4.js +52 -0
  7. package/dist/chunk-LFIKMVY7.js +27 -0
  8. package/dist/chunk-RYAFBNES.js +33 -0
  9. package/dist/chunk-T5RPGCCM.js +40 -0
  10. package/dist/chunk-WU6GAPKH.js +36 -0
  11. package/dist/{chunk-MF3OTILQ.js → chunk-XC5RUHLK.js} +29 -8
  12. package/dist/{config-XJIPZNUP.js → config-XYRBZJDU.js} +3 -3
  13. package/dist/{doctor-EJDSEJSS.js → doctor-YONYXDX6.js} +147 -24
  14. package/dist/index.js +58 -10
  15. package/dist/{install-EKWMFLUU.js → install-74ANPCCP.js} +320 -75
  16. package/dist/{metrics-ACEQFPDU.js → metrics-RER6NLFC.js} +22 -9
  17. package/dist/{onboard-coverage-MFCAEBDO.js → onboard-coverage-JWQWDZW7.js} +1 -1
  18. package/dist/scope-explain-CDIZESP5.js +37 -0
  19. package/dist/status-GLQWLWH6.js +23 -0
  20. package/dist/store-XB3ADT65.js +144 -0
  21. package/dist/sync-UJ4BBCZJ.js +251 -0
  22. package/dist/{uninstall-MH7ZIB6M.js → uninstall-C3QXKOO6.js} +47 -7
  23. package/dist/whoami-2MLO4Y37.js +36 -0
  24. package/package.json +3 -3
  25. package/templates/hooks/fabric-hint.cjs +139 -7
  26. package/templates/hooks/knowledge-hint-broad.cjs +204 -9
  27. package/templates/hooks/knowledge-hint-narrow.cjs +49 -4
  28. package/templates/hooks/lib/bindings-snapshot-reader.cjs +81 -0
  29. package/templates/hooks/lib/cite-contract-reminder.cjs +15 -9
  30. package/templates/hooks/lib/cite-line-parser.cjs +48 -26
  31. package/templates/hooks/lib/injection-log.cjs +91 -0
  32. package/templates/hooks/lib/state-store.cjs +30 -11
  33. package/templates/skills/fabric-archive/SKILL.md +4 -0
  34. package/templates/skills/fabric-audit/SKILL.md +53 -0
  35. package/templates/skills/fabric-connect/SKILL.md +48 -0
  36. package/templates/skills/fabric-import/SKILL.md +4 -0
  37. package/templates/skills/fabric-review/SKILL.md +6 -0
  38. package/templates/skills/fabric-review/ref/cite-contract.md +56 -0
  39. package/templates/skills/fabric-store/SKILL.md +44 -0
  40. package/templates/skills/fabric-sync/SKILL.md +46 -0
@@ -1,14 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ getProjectTranslator
4
+ } from "./chunk-2CY4BMTH.js";
2
5
 
3
6
  // src/commands/metrics.ts
4
7
  import { resolve } from "path";
5
8
  import { defineCommand } from "citty";
6
9
  import { readMetrics } from "@fenglimg/fabric-server";
7
- function parseSinceArg(raw) {
10
+ function parseSinceArg(raw, t) {
8
11
  if (raw === void 0 || raw.length === 0) return 0;
9
12
  const match = /^(\d+)([smhd]?)$/u.exec(raw);
10
13
  if (match === null) {
11
- throw new Error(`--since: invalid duration "${raw}" (expected e.g. 24h, 7d, 30m)`);
14
+ throw new Error(t("cli.metrics.invalid-since", { raw }));
12
15
  }
13
16
  const n = Number.parseInt(match[1], 10);
14
17
  const unit = match[2] ?? "s";
@@ -36,6 +39,8 @@ function aggregate(rows, sinceMs, now) {
36
39
  }
37
40
  }
38
41
  return {
42
+ // Stable token (NOT localized) so the --json contract is locale-independent;
43
+ // renderText localizes "all-time" at presentation time only.
39
44
  windowDescription: sinceMs > 0 ? formatDuration(sinceMs) : "all-time",
40
45
  rowCount: filtered.length,
41
46
  totals,
@@ -50,17 +55,24 @@ function formatDuration(ms) {
50
55
  if (ms >= 6e4) return `${Math.round(ms / 6e4)}m`;
51
56
  return `${Math.round(ms / 1e3)}s`;
52
57
  }
53
- function renderText(agg) {
58
+ function renderText(agg, t) {
54
59
  const lines = [];
55
- lines.push(`Fabric metrics \u2014 window: ${agg.windowDescription}`);
60
+ const windowDisplay = agg.windowDescription === "all-time" ? t("cli.metrics.window-all-time") : agg.windowDescription;
61
+ lines.push(t("cli.metrics.window", { window: windowDisplay }));
56
62
  if (agg.rangeStart && agg.rangeEnd) {
57
- lines.push(` rows: ${agg.rowCount} (${agg.rangeStart} \u2192 ${agg.rangeEnd})`);
63
+ lines.push(
64
+ t("cli.metrics.rows-range", {
65
+ count: String(agg.rowCount),
66
+ start: agg.rangeStart,
67
+ end: agg.rangeEnd
68
+ })
69
+ );
58
70
  } else {
59
- lines.push(` rows: ${agg.rowCount}`);
71
+ lines.push(t("cli.metrics.rows", { count: String(agg.rowCount) }));
60
72
  }
61
73
  lines.push("");
62
74
  if (Object.keys(agg.totals).length === 0) {
63
- lines.push(" (no counter activity in window \u2014 server may be idle or just started)");
75
+ lines.push(t("cli.metrics.no-activity"));
64
76
  return lines.join("\n");
65
77
  }
66
78
  lines.push(" counter total");
@@ -103,14 +115,15 @@ var metricsCommand = defineCommand({
103
115
  },
104
116
  async run({ args }) {
105
117
  const projectRoot = resolve(args.target ?? process.cwd());
106
- const sinceMs = parseSinceArg(args.since);
118
+ const t = getProjectTranslator(projectRoot);
119
+ const sinceMs = parseSinceArg(args.since, t);
107
120
  const rows = await readMetrics(projectRoot);
108
121
  const aggregated = aggregate(rows, sinceMs, /* @__PURE__ */ new Date());
109
122
  if (args.json === true) {
110
123
  process.stdout.write(`${JSON.stringify(aggregated)}
111
124
  `);
112
125
  } else {
113
- process.stdout.write(`${renderText(aggregated)}
126
+ process.stdout.write(`${renderText(aggregated, t)}
114
127
  `);
115
128
  }
116
129
  }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  t
4
- } from "./chunk-PWLW3B57.js";
4
+ } from "./chunk-2CY4BMTH.js";
5
5
 
6
6
  // src/commands/onboard-coverage.ts
7
7
  import { existsSync, readdirSync, readFileSync } from "fs";
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ scopeExplain
4
+ } from "./chunk-L4Q55UC4.js";
5
+ import {
6
+ getProjectTranslator
7
+ } from "./chunk-2CY4BMTH.js";
8
+ import "./chunk-LFIKMVY7.js";
9
+ import "./chunk-RYAFBNES.js";
10
+
11
+ // src/commands/scope-explain.ts
12
+ import { defineCommand } from "citty";
13
+ var scope_explain_default = defineCommand({
14
+ meta: {
15
+ name: "scope-explain",
16
+ description: "Explain the resolved read-set and write target for a scope"
17
+ },
18
+ args: {
19
+ scope: {
20
+ type: "positional",
21
+ required: true,
22
+ description: "Scope coordinate (e.g. team, project:x, personal)"
23
+ }
24
+ },
25
+ run({ args }) {
26
+ const projectRoot = process.cwd();
27
+ const result = scopeExplain(projectRoot, args.scope);
28
+ if (result === null) {
29
+ console.log(getProjectTranslator(projectRoot)("cli.cmd.no-global-config"));
30
+ return;
31
+ }
32
+ console.log(JSON.stringify(result, null, 2));
33
+ }
34
+ });
35
+ export {
36
+ scope_explain_default as default
37
+ };
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ projectStatus
4
+ } from "./chunk-T5RPGCCM.js";
5
+ import "./chunk-LFIKMVY7.js";
6
+ import "./chunk-RYAFBNES.js";
7
+
8
+ // src/commands/status.ts
9
+ import { defineCommand } from "citty";
10
+ var status_default = defineCommand({
11
+ meta: { name: "status", description: "Show this project's Fabric store status" },
12
+ run() {
13
+ const status = projectStatus(process.cwd());
14
+ console.log(`uid: ${status.uid ?? "(no global config)"}`);
15
+ console.log(`project_id: ${status.project_id ?? "(not a Fabric project)"}`);
16
+ console.log(`mounted stores: ${status.mounted.length > 0 ? status.mounted.join(", ") : "(none)"}`);
17
+ console.log(`required: ${status.required.length > 0 ? status.required.join(", ") : "(none)"}`);
18
+ console.log(`active write: ${status.active_write_store ?? "(none \u2014 personal scope only)"}`);
19
+ }
20
+ });
21
+ export {
22
+ status_default as default
23
+ };
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ assertStoreMountable,
4
+ storeAdd,
5
+ storeBind,
6
+ storeCreate,
7
+ storeExplain,
8
+ storeList,
9
+ storeRemove,
10
+ storeSwitchWrite
11
+ } from "./chunk-4R2CYEA4.js";
12
+ import {
13
+ regenerateBindingsSnapshot
14
+ } from "./chunk-WU6GAPKH.js";
15
+ import "./chunk-L4Q55UC4.js";
16
+ import {
17
+ getProjectTranslator
18
+ } from "./chunk-2CY4BMTH.js";
19
+ import "./chunk-LFIKMVY7.js";
20
+ import "./chunk-RYAFBNES.js";
21
+
22
+ // src/commands/store.ts
23
+ import { defineCommand } from "citty";
24
+ var listCommand = defineCommand({
25
+ meta: { name: "list", description: "List mounted knowledge stores" },
26
+ run() {
27
+ const t = getProjectTranslator();
28
+ const stores = storeList();
29
+ if (stores.length === 0) {
30
+ console.log(t("cli.store.none-mounted"));
31
+ return;
32
+ }
33
+ const localOnly = t("cli.shared.local-only");
34
+ for (const store of stores) {
35
+ console.log(`${store.alias} ${store.store_uuid} ${store.remote ?? localOnly}`);
36
+ }
37
+ }
38
+ });
39
+ var addCommand = defineCommand({
40
+ meta: { name: "add", description: "Mount a knowledge store into the global registry" },
41
+ args: {
42
+ uuid: { type: "string", required: true, description: "Intrinsic store UUID" },
43
+ alias: { type: "string", required: true, description: "Local alias for this store" },
44
+ remote: { type: "string", description: "Git remote locator (omit for local-only)" }
45
+ },
46
+ run({ args }) {
47
+ assertStoreMountable(args.uuid);
48
+ const store = args.remote === void 0 ? { store_uuid: args.uuid, alias: args.alias } : { store_uuid: args.uuid, alias: args.alias, remote: args.remote };
49
+ const next = storeAdd(store);
50
+ console.log(
51
+ getProjectTranslator()("cli.store.mounted", {
52
+ alias: args.alias,
53
+ count: String(next.stores.length)
54
+ })
55
+ );
56
+ }
57
+ });
58
+ var createCommand = defineCommand({
59
+ meta: { name: "create", description: "Create a brand-new local knowledge store and mount it" },
60
+ args: {
61
+ alias: { type: "string", required: true, description: "Local alias for the new store" },
62
+ remote: { type: "string", description: "Git remote to associate (push target; optional)" }
63
+ },
64
+ run({ args }) {
65
+ const result = storeCreate(args.alias, (/* @__PURE__ */ new Date()).toISOString(), {
66
+ ...args.remote === void 0 ? {} : { remote: args.remote }
67
+ });
68
+ const t = getProjectTranslator();
69
+ console.log(
70
+ t("cli.store.created", { alias: args.alias, uuid: result.store_uuid, dir: result.storeDir }) + (args.remote === void 0 ? `
71
+ ${t("cli.store.created-local-hint")}` : "")
72
+ );
73
+ }
74
+ });
75
+ var removeCommand = defineCommand({
76
+ meta: { name: "remove", description: "Detach a store from the registry (does NOT delete it)" },
77
+ args: {
78
+ alias: { type: "positional", required: true, description: "Alias to detach" }
79
+ },
80
+ run({ args }) {
81
+ const { detached } = storeRemove(args.alias);
82
+ const t = getProjectTranslator();
83
+ console.log(
84
+ detached === null ? t("cli.store.no-alias", { alias: args.alias }) : t("cli.store.detached", { alias: args.alias })
85
+ );
86
+ }
87
+ });
88
+ var explainCommand = defineCommand({
89
+ meta: { name: "explain", description: "Explain how a store alias resolves" },
90
+ args: {
91
+ alias: { type: "positional", required: true, description: "Alias to explain" }
92
+ },
93
+ run({ args }) {
94
+ const explanation = storeExplain(args.alias);
95
+ console.log(
96
+ explanation === null ? getProjectTranslator()("cli.store.no-alias", { alias: args.alias }) : JSON.stringify(explanation, null, 2)
97
+ );
98
+ }
99
+ });
100
+ var bindCommand = defineCommand({
101
+ meta: { name: "bind", description: "Declare a required store on this project's config" },
102
+ args: {
103
+ id: { type: "positional", required: true, description: "Store alias/UUID to require" },
104
+ remote: { type: "string", description: "Suggested remote for clone onboarding" }
105
+ },
106
+ run({ args }) {
107
+ const entry = args.remote === void 0 ? { id: args.id } : { id: args.id, suggested_remote: args.remote };
108
+ const projectRoot = process.cwd();
109
+ const next = storeBind(projectRoot, entry);
110
+ console.log(
111
+ getProjectTranslator(projectRoot)("cli.store.bound", {
112
+ id: args.id,
113
+ count: String(next.required_stores?.length ?? 0)
114
+ })
115
+ );
116
+ regenerateBindingsSnapshot(projectRoot, { now: (/* @__PURE__ */ new Date()).toISOString() });
117
+ }
118
+ });
119
+ var switchWriteCommand = defineCommand({
120
+ meta: { name: "switch-write", description: "Set the active write store for non-personal scopes" },
121
+ args: {
122
+ alias: { type: "positional", required: true, description: "Alias of the store to write to" }
123
+ },
124
+ run({ args }) {
125
+ const projectRoot = process.cwd();
126
+ storeSwitchWrite(projectRoot, args.alias);
127
+ console.log(getProjectTranslator(projectRoot)("cli.store.switch-write", { alias: args.alias }));
128
+ }
129
+ });
130
+ var store_default = defineCommand({
131
+ meta: { name: "store", description: "Manage mounted Fabric knowledge stores" },
132
+ subCommands: {
133
+ list: listCommand,
134
+ create: createCommand,
135
+ add: addCommand,
136
+ remove: removeCommand,
137
+ explain: explainCommand,
138
+ bind: bindCommand,
139
+ "switch-write": switchWriteCommand
140
+ }
141
+ });
142
+ export {
143
+ store_default as default
144
+ };
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ regenerateBindingsSnapshot
4
+ } from "./chunk-WU6GAPKH.js";
5
+ import "./chunk-L4Q55UC4.js";
6
+ import {
7
+ getProjectTranslator
8
+ } from "./chunk-2CY4BMTH.js";
9
+ import "./chunk-LFIKMVY7.js";
10
+ import {
11
+ loadGlobalConfig,
12
+ resolveGlobalRoot
13
+ } from "./chunk-RYAFBNES.js";
14
+
15
+ // src/commands/sync.ts
16
+ import { defineCommand } from "citty";
17
+
18
+ // src/sync/run-sync.ts
19
+ import { execFileSync } from "child_process";
20
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
21
+ import { join } from "path";
22
+ import { GLOBAL_STATE_DIR, storeRelativePath } from "@fenglimg/fabric-shared";
23
+ import { GenericIOError } from "@fenglimg/fabric-shared/errors";
24
+
25
+ // src/sync/state-machine.ts
26
+ function syncTransition(state, event) {
27
+ switch (state) {
28
+ case "pending":
29
+ if (event === "rebase_clean") return "synced";
30
+ if (event === "rebase_conflict") return "conflict";
31
+ if (event === "network_unavailable") return "offline";
32
+ break;
33
+ case "conflict":
34
+ if (event === "user_continue") return "synced";
35
+ if (event === "user_abort") return "aborted";
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
+ return JSON.parse(readFileSync(path, "utf8"));
92
+ }
93
+ function saveSession(globalRoot, session) {
94
+ const path = syncSessionPath(globalRoot);
95
+ mkdirSync(join(path, ".."), { recursive: true });
96
+ writeFileSync(path, `${JSON.stringify(session, null, 2)}
97
+ `, "utf8");
98
+ }
99
+ function clearSession(globalRoot) {
100
+ rmSync(syncSessionPath(globalRoot), { force: true });
101
+ }
102
+ function defaultPull(storeDir) {
103
+ try {
104
+ execFileSync("git", ["pull", "--rebase"], {
105
+ cwd: storeDir,
106
+ stdio: ["ignore", "pipe", "pipe"]
107
+ });
108
+ return "clean";
109
+ } catch (error) {
110
+ const detail = `${gitErrText(error, "stdout")}${gitErrText(error, "stderr")}`;
111
+ if (/CONFLICT|could not apply|needs merge|rebase --continue/i.test(detail)) {
112
+ return "conflict";
113
+ }
114
+ if (/could not resolve host|could not read from remote|unable to access|connection|network is unreachable|timed out/i.test(
115
+ detail
116
+ )) {
117
+ return "offline";
118
+ }
119
+ const gitMessage = detail.trim().length > 0 ? detail.trim() : "unknown git error";
120
+ throw new GenericIOError(`git pull --rebase failed in ${storeDir}: ${gitMessage}`, {
121
+ actionHint: "resolve the git issue above (e.g. authentication, a dirty working tree, or a detached HEAD), then re-run `fabric sync`",
122
+ details: error
123
+ });
124
+ }
125
+ }
126
+ function gitErrText(error, key) {
127
+ const value = error[key];
128
+ return typeof value === "string" || Buffer.isBuffer(value) ? String(value) : "";
129
+ }
130
+ function defaultRebaseContinue(storeDir) {
131
+ execFileSync("git", ["rebase", "--continue"], { cwd: storeDir, stdio: "ignore" });
132
+ }
133
+ function defaultRebaseAbort(storeDir) {
134
+ execFileSync("git", ["rebase", "--abort"], { cwd: storeDir, stdio: "ignore" });
135
+ }
136
+ var OUTCOME_EVENT = {
137
+ clean: "rebase_clean",
138
+ conflict: "rebase_conflict",
139
+ offline: "network_unavailable"
140
+ };
141
+ function walkPending(session, storeDirOf, pull) {
142
+ let next = session;
143
+ for (const store of session.stores) {
144
+ if (store.state !== "pending") {
145
+ continue;
146
+ }
147
+ const outcome = pull(storeDirOf(store));
148
+ next = applySyncEvent(next, store.alias, OUTCOME_EVENT[outcome]);
149
+ if (outcome === "conflict") {
150
+ break;
151
+ }
152
+ }
153
+ return next;
154
+ }
155
+ function finalize(session, options, globalRoot) {
156
+ const settled = isSyncSettled(session);
157
+ let snapshotWritten = false;
158
+ if (settled) {
159
+ clearSession(globalRoot);
160
+ const snapshot = regenerateBindingsSnapshot(options.projectRoot, {
161
+ globalRoot,
162
+ now: options.now,
163
+ ...options.writeScope === void 0 ? {} : { writeScope: options.writeScope }
164
+ });
165
+ snapshotWritten = snapshot !== null;
166
+ } else {
167
+ saveSession(globalRoot, session);
168
+ }
169
+ return { session, settled, deferred: deferredPushStores(session), snapshotWritten };
170
+ }
171
+ function runStartSync(options) {
172
+ const globalRoot = options.globalRoot ?? resolveGlobalRoot();
173
+ const config = loadGlobalConfig(globalRoot);
174
+ if (config === null) {
175
+ throw new Error(NO_GLOBAL_CONFIG);
176
+ }
177
+ const syncable = config.stores.filter((store) => store.remote !== void 0);
178
+ const session = planSync(
179
+ syncable.map((store) => ({ alias: store.alias, store_uuid: store.store_uuid }))
180
+ );
181
+ const storeDirOf = (status) => join(globalRoot, storeRelativePath(status.store_uuid));
182
+ const walked = walkPending(session, storeDirOf, options.pull ?? defaultPull);
183
+ return finalize(walked, options, globalRoot);
184
+ }
185
+ function runContinueSync(options) {
186
+ const globalRoot = options.globalRoot ?? resolveGlobalRoot();
187
+ const session = loadSession(globalRoot);
188
+ if (session === null) {
189
+ throw new Error(NO_SESSION);
190
+ }
191
+ const conflicted = session.stores.find((store) => store.state === "conflict");
192
+ if (conflicted === void 0) {
193
+ throw new Error(NO_CONFLICT);
194
+ }
195
+ const storeDirOf = (status) => join(globalRoot, storeRelativePath(status.store_uuid));
196
+ (options.rebaseContinue ?? defaultRebaseContinue)(storeDirOf(conflicted));
197
+ const resumed = walkPending(continueSync(session), storeDirOf, options.pull ?? defaultPull);
198
+ return finalize(resumed, options, globalRoot);
199
+ }
200
+ function runAbortSync(options) {
201
+ const globalRoot = options.globalRoot ?? resolveGlobalRoot();
202
+ const session = loadSession(globalRoot);
203
+ if (session === null) {
204
+ throw new Error(NO_SESSION);
205
+ }
206
+ const conflicted = session.stores.find((store) => store.state === "conflict");
207
+ if (conflicted === void 0) {
208
+ throw new Error(NO_CONFLICT);
209
+ }
210
+ const storeDirOf = (status) => join(globalRoot, storeRelativePath(status.store_uuid));
211
+ (options.rebaseAbort ?? defaultRebaseAbort)(storeDirOf(conflicted));
212
+ const resumed = walkPending(abortSync(session), storeDirOf, options.pull ?? defaultPull);
213
+ return finalize(resumed, options, globalRoot);
214
+ }
215
+
216
+ // src/commands/sync.ts
217
+ function report(result, projectRoot) {
218
+ const t = getProjectTranslator(projectRoot);
219
+ for (const store of result.session.stores) {
220
+ console.log(`${store.alias} ${store.state}`);
221
+ }
222
+ if (result.deferred.length > 0) {
223
+ console.log(t("cli.sync.deferred", { count: String(result.deferred.length) }));
224
+ }
225
+ if (!result.settled) {
226
+ console.log(t("cli.sync.paused"));
227
+ }
228
+ }
229
+ var sync_default = defineCommand({
230
+ meta: { name: "sync", description: "Pull --rebase + push every mounted store; resume conflicts" },
231
+ args: {
232
+ continue: { type: "boolean", description: "Resume after resolving a rebase conflict" },
233
+ abort: { type: "boolean", description: "Abort the conflicted store's rebase" }
234
+ },
235
+ run({ args }) {
236
+ const projectRoot = process.cwd();
237
+ const options = { projectRoot, now: (/* @__PURE__ */ new Date()).toISOString() };
238
+ if (args.continue === true) {
239
+ report(runContinueSync(options), projectRoot);
240
+ return;
241
+ }
242
+ if (args.abort === true) {
243
+ report(runAbortSync(options), projectRoot);
244
+ return;
245
+ }
246
+ report(runStartSync(options), projectRoot);
247
+ }
248
+ });
249
+ export {
250
+ sync_default as default
251
+ };
@@ -7,21 +7,21 @@ import {
7
7
  HOOK_SCRIPT_DESTINATIONS,
8
8
  SKILL_DESTINATIONS,
9
9
  fabricAgentsSnapshotPath
10
- } from "./chunk-D25XJ4BC.js";
11
- import {
12
- detectClientSupports,
13
- resolveClients
14
- } from "./chunk-MF3OTILQ.js";
10
+ } from "./chunk-2R55HNVD.js";
15
11
  import {
16
12
  paint
17
- } from "./chunk-WWNXR34K.js";
13
+ } from "./chunk-BO4XIZWZ.js";
18
14
  import {
19
15
  createDebugLogger,
20
16
  resolveDevMode
21
17
  } from "./chunk-COI5VDFU.js";
18
+ import {
19
+ detectClientSupports,
20
+ resolveClients
21
+ } from "./chunk-XC5RUHLK.js";
22
22
  import {
23
23
  t
24
- } from "./chunk-PWLW3B57.js";
24
+ } from "./chunk-2CY4BMTH.js";
25
25
 
26
26
  // src/commands/uninstall.ts
27
27
  import { existsSync as existsSync2, statSync } from "fs";
@@ -46,6 +46,15 @@ async function uninstallFabricReviewSkill(projectRoot) {
46
46
  async function uninstallFabricImportSkill(projectRoot) {
47
47
  return removeSkill("skill-import", SKILL_DESTINATIONS.fabricImport, projectRoot);
48
48
  }
49
+ async function uninstallFabricSyncSkill(projectRoot) {
50
+ return removeSkill("skill-sync", SKILL_DESTINATIONS.fabricSync, projectRoot);
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
+ }
49
58
  async function removeSkill(step, rels, projectRoot) {
50
59
  const results = [];
51
60
  for (const rel of rels) {
@@ -72,6 +81,13 @@ async function removeKnowledgeHintNarrowHook(projectRoot) {
72
81
  projectRoot
73
82
  );
74
83
  }
84
+ async function removeCitePolicyEvictHook(projectRoot) {
85
+ return removeHookScripts(
86
+ "hook-cite-policy-evict-script",
87
+ HOOK_SCRIPT_DESTINATIONS.citePolicyEvict,
88
+ projectRoot
89
+ );
90
+ }
75
91
  async function removeHookScripts(step, rels, projectRoot) {
76
92
  const results = [];
77
93
  for (const rel of rels) {
@@ -301,6 +317,30 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
301
317
  projectRoot,
302
318
  () => removeArchiveHintHook(projectRoot)
303
319
  );
320
+ await runAndCollect(
321
+ results,
322
+ "hook-cite-policy-evict-script",
323
+ projectRoot,
324
+ () => removeCitePolicyEvictHook(projectRoot)
325
+ );
326
+ await runAndCollect(
327
+ results,
328
+ "skill-connect",
329
+ projectRoot,
330
+ () => uninstallFabricConnectSkill(projectRoot)
331
+ );
332
+ await runAndCollect(
333
+ results,
334
+ "skill-audit",
335
+ projectRoot,
336
+ () => uninstallFabricAuditSkill(projectRoot)
337
+ );
338
+ await runAndCollect(
339
+ results,
340
+ "skill-sync",
341
+ projectRoot,
342
+ () => uninstallFabricSyncSkill(projectRoot)
343
+ );
304
344
  await runAndCollect(
305
345
  results,
306
346
  "skill-import",
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getProjectTranslator
4
+ } from "./chunk-2CY4BMTH.js";
5
+ import {
6
+ whoami
7
+ } from "./chunk-T5RPGCCM.js";
8
+ import "./chunk-LFIKMVY7.js";
9
+ import "./chunk-RYAFBNES.js";
10
+
11
+ // src/commands/whoami.ts
12
+ import { defineCommand } from "citty";
13
+ var whoami_default = defineCommand({
14
+ meta: { name: "whoami", description: "Show this machine's Fabric uid and mounted stores" },
15
+ run() {
16
+ const t = getProjectTranslator();
17
+ const info = whoami();
18
+ if (info === null) {
19
+ console.log(t("cli.cmd.no-global-config"));
20
+ return;
21
+ }
22
+ console.log(t("cli.whoami.uid", { uid: info.uid }));
23
+ if (info.stores.length === 0) {
24
+ console.log(t("cli.whoami.stores-none"));
25
+ return;
26
+ }
27
+ console.log(t("cli.whoami.stores-label"));
28
+ const localOnly = t("cli.shared.local-only");
29
+ for (const store of info.stores) {
30
+ console.log(` ${store.alias} ${store.store_uuid}${store.local_only ? ` ${localOnly}` : ""}`);
31
+ }
32
+ }
33
+ });
34
+ export {
35
+ whoami_default as default
36
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fenglimg/fabric-cli",
3
- "version": "2.0.1",
3
+ "version": "2.2.0-rc.1",
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.0.1",
49
- "@fenglimg/fabric-shared": "2.0.1"
48
+ "@fenglimg/fabric-server": "2.2.0-rc.1",
49
+ "@fenglimg/fabric-shared": "2.2.0-rc.1"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/node": "^22.15.0",