@fenglimg/fabric-cli 2.2.0-rc.4 → 2.2.0-rc.9
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-5JG4QJLO.js → chunk-27HK6H5Y.js} +10 -5
- package/dist/{chunk-F6ITRM7T.js → chunk-2KBCTMID.js} +29 -6
- package/dist/chunk-3D7B2UAZ.js +149 -0
- package/dist/{chunk-XC5RUHLK.js → chunk-3IOLS5EK.js} +23 -38
- package/dist/{chunk-XHHCRDIR.js → chunk-7ZDXBOOU.js} +174 -211
- package/dist/{doctor-U5W4CX5I.js → chunk-E7HJUU34.js} +103 -51
- package/dist/{chunk-XCBVSGCS.js → chunk-FNHDQTPC.js} +1 -10
- package/dist/{chunk-2CY4BMTH.js → chunk-HORSMSZL.js} +9 -5
- package/dist/{chunk-BO4XIZWZ.js → chunk-NLNH64A3.js} +5 -18
- package/dist/{chunk-H3FE6VIK.js → chunk-PTGQAZEW.js} +13 -3
- package/dist/chunk-QFIVFZRH.js +13 -0
- package/dist/{chunk-5SSNE5GM.js → chunk-QPAW6IYT.js} +125 -39
- package/dist/{chunk-COI5VDFU.js → chunk-WA3DYGSY.js} +1 -2
- package/dist/{plan-context-hint-CHVZGOZ5.js → chunk-YM4XATJF.js} +29 -4
- package/dist/{config-VJMXCLXW.js → config-A3LTECAY.js} +4 -3
- package/dist/context-7NUKXDB6.js +117 -0
- package/dist/doctor-MDTZWKBK.js +24 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +131 -21
- package/dist/info-7FKBTMVO.js +139 -0
- package/dist/install-v2-I6PJ6IFT.js +3279 -0
- package/dist/{metrics-RER6NLFC.js → metrics-HMFH4YHK.js} +1 -1
- package/dist/{onboard-coverage-JWQWDZW7.js → onboard-coverage-XSG77LL3.js} +48 -27
- package/dist/plan-context-hint-G75R4P4J.js +12 -0
- package/dist/{scope-explain-BWRWBCCP.js → scope-explain-HLJZ2M33.js} +3 -2
- package/dist/{status-7UFLWRX7.js → status-4R3TM4FJ.js} +8 -5
- package/dist/{store-ZEZMQVG7.js → store-HOCORVL3.js} +96 -350
- package/dist/{sync-EA5HZMXM.js → sync-DT5UJMMR.js} +36 -13
- package/dist/{uninstall-F75MPKQC.js → uninstall-IFN2KYBK.js} +71 -140
- package/dist/{whoami-3FRWYGML.js → whoami-ITGEFWH4.js} +9 -7
- package/package.json +7 -5
- package/templates/hooks/cite-policy-evict.cjs +5 -5
- package/templates/hooks/configs/README.md +14 -27
- package/templates/hooks/configs/claude-code.json +1 -1
- package/templates/hooks/configs/codex-hooks.json +3 -3
- package/templates/hooks/fabric-hint.cjs +326 -161
- package/templates/hooks/knowledge-hint-broad.cjs +431 -271
- package/templates/hooks/knowledge-hint-narrow.cjs +64 -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/nudge-policy.cjs +117 -0
- package/templates/hooks/lib/state-store.cjs +60 -0
- package/templates/hooks/post-tooluse-mutation.cjs +112 -11
- package/templates/skills/fabric/SKILL.md +100 -0
- package/templates/skills/fabric-archive/SKILL.md +29 -26
- 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 +13 -3
- package/templates/skills/fabric-connect/SKILL.md +3 -3
- 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 +5 -5
- package/templates/skills/fabric-review/ref/cite-contract.md +1 -1
- 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 +1 -1
- package/templates/skills/fabric-sync/SKILL.md +1 -1
- package/templates/skills/lib/shared-policy.md +2 -2
- package/dist/install-7XJ64WSC.js +0 -2743
- package/templates/hooks/configs/cursor-hooks.json +0 -30
- package/templates/hooks/lib/cite-contract-reminder.cjs +0 -179
- package/templates/hooks/lib/summary-fallback.cjs +0 -210
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
paint
|
|
4
|
+
} from "./chunk-NLNH64A3.js";
|
|
2
5
|
import {
|
|
3
6
|
regenerateBindingsSnapshot
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PTGQAZEW.js";
|
|
5
8
|
import "./chunk-EOT63RDH.js";
|
|
6
|
-
import
|
|
7
|
-
getProjectTranslator
|
|
8
|
-
} from "./chunk-2CY4BMTH.js";
|
|
9
|
+
import "./chunk-QFIVFZRH.js";
|
|
9
10
|
import {
|
|
10
11
|
loadGlobalConfig,
|
|
11
12
|
resolveGlobalRoot
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-FNHDQTPC.js";
|
|
14
|
+
import {
|
|
15
|
+
getProjectTranslator
|
|
16
|
+
} from "./chunk-HORSMSZL.js";
|
|
13
17
|
|
|
14
18
|
// src/commands/sync.ts
|
|
15
19
|
import { defineCommand } from "citty";
|
|
@@ -18,7 +22,7 @@ import { defineCommand } from "citty";
|
|
|
18
22
|
import { execFileSync } from "child_process";
|
|
19
23
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
20
24
|
import { join } from "path";
|
|
21
|
-
import { GLOBAL_STATE_DIR,
|
|
25
|
+
import { GLOBAL_STATE_DIR, storeRelativePathForMount } from "@fenglimg/fabric-shared";
|
|
22
26
|
import { GenericIOError } from "@fenglimg/fabric-shared/errors";
|
|
23
27
|
|
|
24
28
|
// src/sync/state-machine.ts
|
|
@@ -273,7 +277,7 @@ function runStartSync(options) {
|
|
|
273
277
|
const session = planSync(
|
|
274
278
|
syncable.map((store) => ({ alias: store.alias, store_uuid: store.store_uuid }))
|
|
275
279
|
);
|
|
276
|
-
const storeDirOf = (
|
|
280
|
+
const storeDirOf = makeStoreDirResolver(globalRoot, config.stores);
|
|
277
281
|
const pushableAliases = pushableAliasesOf(config);
|
|
278
282
|
const walked = walkPending(
|
|
279
283
|
session,
|
|
@@ -290,6 +294,16 @@ function pushableAliasesOf(config) {
|
|
|
290
294
|
config.stores.filter((store) => store.remote !== void 0 && (store.writable ?? true)).map((store) => store.alias)
|
|
291
295
|
);
|
|
292
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
|
+
}
|
|
293
307
|
function runContinueSync(options) {
|
|
294
308
|
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
295
309
|
const session = loadSession(globalRoot);
|
|
@@ -304,9 +318,10 @@ function runContinueSync(options) {
|
|
|
304
318
|
actionHint: "The sync is not paused on a conflict; there is nothing to resume."
|
|
305
319
|
});
|
|
306
320
|
}
|
|
307
|
-
const
|
|
321
|
+
const resumeConfig = loadGlobalConfig(globalRoot) ?? { stores: [] };
|
|
322
|
+
const storeDirOf = makeStoreDirResolver(globalRoot, resumeConfig.stores);
|
|
308
323
|
(options.rebaseContinue ?? defaultRebaseContinue)(storeDirOf(conflicted));
|
|
309
|
-
const pushableAliases = pushableAliasesOf(
|
|
324
|
+
const pushableAliases = pushableAliasesOf(resumeConfig);
|
|
310
325
|
const push = options.push ?? defaultPush;
|
|
311
326
|
let advanced;
|
|
312
327
|
if (pushableAliases.has(conflicted.alias)) {
|
|
@@ -343,9 +358,10 @@ function runAbortSync(options) {
|
|
|
343
358
|
actionHint: "The sync is not paused on a conflict; there is nothing to resume."
|
|
344
359
|
});
|
|
345
360
|
}
|
|
346
|
-
const
|
|
361
|
+
const resumeConfig = loadGlobalConfig(globalRoot) ?? { stores: [] };
|
|
362
|
+
const storeDirOf = makeStoreDirResolver(globalRoot, resumeConfig.stores);
|
|
347
363
|
(options.rebaseAbort ?? defaultRebaseAbort)(storeDirOf(conflicted));
|
|
348
|
-
const pushableAliases = pushableAliasesOf(
|
|
364
|
+
const pushableAliases = pushableAliasesOf(resumeConfig);
|
|
349
365
|
const resumed = walkPending(
|
|
350
366
|
abortSync(session),
|
|
351
367
|
storeDirOf,
|
|
@@ -370,7 +386,7 @@ function report(result, projectRoot) {
|
|
|
370
386
|
console.log(t("cli.sync.paused"));
|
|
371
387
|
}
|
|
372
388
|
}
|
|
373
|
-
var
|
|
389
|
+
var syncCommand = defineCommand({
|
|
374
390
|
meta: { name: "sync", description: "Pull --rebase + push every mounted store; resume conflicts" },
|
|
375
391
|
args: {
|
|
376
392
|
continue: { type: "boolean", description: "Resume after resolving a rebase conflict" },
|
|
@@ -378,6 +394,11 @@ var sync_default = defineCommand({
|
|
|
378
394
|
},
|
|
379
395
|
run({ args }) {
|
|
380
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
|
+
}
|
|
381
402
|
const options = { projectRoot, now: (/* @__PURE__ */ new Date()).toISOString() };
|
|
382
403
|
if (args.continue === true) {
|
|
383
404
|
report(runContinueSync(options), projectRoot);
|
|
@@ -390,6 +411,8 @@ var sync_default = defineCommand({
|
|
|
390
411
|
report(runStartSync(options), projectRoot);
|
|
391
412
|
}
|
|
392
413
|
});
|
|
414
|
+
var sync_default = syncCommand;
|
|
393
415
|
export {
|
|
394
|
-
sync_default as default
|
|
416
|
+
sync_default as default,
|
|
417
|
+
syncCommand
|
|
395
418
|
};
|
|
@@ -7,28 +7,28 @@ import {
|
|
|
7
7
|
HOOK_SCRIPT_DESTINATIONS,
|
|
8
8
|
SKILL_DESTINATIONS,
|
|
9
9
|
fabricAgentsSnapshotPath
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import {
|
|
12
|
-
paint
|
|
13
|
-
} from "./chunk-BO4XIZWZ.js";
|
|
10
|
+
} from "./chunk-7ZDXBOOU.js";
|
|
14
11
|
import {
|
|
15
12
|
createDebugLogger,
|
|
16
13
|
resolveDevMode
|
|
17
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-WA3DYGSY.js";
|
|
18
15
|
import {
|
|
19
16
|
detectClientSupports,
|
|
20
17
|
resolveClients
|
|
21
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-3IOLS5EK.js";
|
|
19
|
+
import {
|
|
20
|
+
paint
|
|
21
|
+
} from "./chunk-NLNH64A3.js";
|
|
22
22
|
import {
|
|
23
23
|
t
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-HORSMSZL.js";
|
|
25
25
|
|
|
26
26
|
// src/commands/uninstall.ts
|
|
27
27
|
import { existsSync as existsSync2, statSync } from "fs";
|
|
28
28
|
import { rm as rm2 } from "fs/promises";
|
|
29
29
|
import { homedir } from "os";
|
|
30
30
|
import { isAbsolute, join as join2, relative, resolve, sep } from "path";
|
|
31
|
-
import { cancel, confirm,
|
|
31
|
+
import { cancel, confirm, intro, isCancel, multiselect, note, outro } from "@clack/prompts";
|
|
32
32
|
import { defineCommand } from "citty";
|
|
33
33
|
|
|
34
34
|
// src/install/uninstall-skills-and-hooks.ts
|
|
@@ -37,6 +37,9 @@ import { readdir, readFile, rm, rmdir } from "fs/promises";
|
|
|
37
37
|
import { dirname, join } from "path";
|
|
38
38
|
import { atomicWriteJson, atomicWriteText } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
39
39
|
import { BOOTSTRAP_REGEX } from "@fenglimg/fabric-shared/templates/bootstrap-canonical";
|
|
40
|
+
async function uninstallFabricRouterSkill(projectRoot) {
|
|
41
|
+
return removeSkill("skill-router", SKILL_DESTINATIONS.fabricRouter, projectRoot);
|
|
42
|
+
}
|
|
40
43
|
async function uninstallFabricArchiveSkill(projectRoot) {
|
|
41
44
|
return removeSkill("skill", SKILL_DESTINATIONS.fabricArchive, projectRoot);
|
|
42
45
|
}
|
|
@@ -158,25 +161,10 @@ async function unmergeCodexHookConfig(projectRoot) {
|
|
|
158
161
|
extractCommands: extractFlatCommands
|
|
159
162
|
});
|
|
160
163
|
}
|
|
161
|
-
async function unmergeCursorHookConfig(projectRoot) {
|
|
162
|
-
return unmergeHookConfig({
|
|
163
|
-
step: "cursor-hook-config",
|
|
164
|
-
projectRoot,
|
|
165
|
-
configRel: HOOK_CONFIG_TARGETS.cursor,
|
|
166
|
-
arrayPaths: [...HOOK_CONFIG_ARRAY_PATHS.cursor],
|
|
167
|
-
fabricCommands: Object.values(FABRIC_HOOK_COMMAND_PATHS.cursor),
|
|
168
|
-
extractCommands: extractFlatCommands
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
164
|
async function stripFabricBootstrapBlocks(projectRoot) {
|
|
172
165
|
const results = [];
|
|
173
166
|
results.push(await stripClaudeBootstrapImports(projectRoot));
|
|
174
|
-
results.push(await stripManagedBlock(projectRoot, "AGENTS.md"
|
|
175
|
-
results.push(
|
|
176
|
-
await stripManagedBlock(projectRoot, join(".cursor", "rules", "fabric-bootstrap.mdc"), {
|
|
177
|
-
deleteWhenEmpty: true
|
|
178
|
-
})
|
|
179
|
-
);
|
|
167
|
+
results.push(await stripManagedBlock(projectRoot, "AGENTS.md"));
|
|
180
168
|
return results;
|
|
181
169
|
}
|
|
182
170
|
async function stripClaudeBootstrapImports(projectRoot) {
|
|
@@ -221,8 +209,8 @@ async function stripClaudeBootstrapImports(projectRoot) {
|
|
|
221
209
|
};
|
|
222
210
|
}
|
|
223
211
|
}
|
|
224
|
-
async function stripManagedBlock(projectRoot, relPath
|
|
225
|
-
const step =
|
|
212
|
+
async function stripManagedBlock(projectRoot, relPath) {
|
|
213
|
+
const step = "bootstrap-codex";
|
|
226
214
|
const target = join(projectRoot, relPath);
|
|
227
215
|
if (!existsSync(target)) {
|
|
228
216
|
return { step, path: target, status: "skipped", message: "absent" };
|
|
@@ -245,19 +233,6 @@ async function stripManagedBlock(projectRoot, relPath, options) {
|
|
|
245
233
|
const before = existing.slice(0, match.index ?? 0);
|
|
246
234
|
const after = existing.slice((match.index ?? 0) + match[0].length);
|
|
247
235
|
const filtered = `${before}${after.replace(/^\r?\n/, "")}`;
|
|
248
|
-
if (options.deleteWhenEmpty && isFrontMatterOnly(filtered)) {
|
|
249
|
-
try {
|
|
250
|
-
await rm(target, { force: true });
|
|
251
|
-
return { step, path: target, status: "removed", message: "front-matter-only" };
|
|
252
|
-
} catch (error) {
|
|
253
|
-
return {
|
|
254
|
-
step,
|
|
255
|
-
path: target,
|
|
256
|
-
status: "error",
|
|
257
|
-
message: error instanceof Error ? error.message : String(error)
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
236
|
try {
|
|
262
237
|
await atomicWriteText(target, filtered);
|
|
263
238
|
return { step, path: target, status: "removed" };
|
|
@@ -270,12 +245,6 @@ async function stripManagedBlock(projectRoot, relPath, options) {
|
|
|
270
245
|
};
|
|
271
246
|
}
|
|
272
247
|
}
|
|
273
|
-
function isFrontMatterOnly(content) {
|
|
274
|
-
const trimmed = content.replace(/^\s+/, "");
|
|
275
|
-
const match = trimmed.match(/^---\n[\s\S]*?\n---\s*$/);
|
|
276
|
-
if (match === null) return trimmed.length === 0;
|
|
277
|
-
return true;
|
|
278
|
-
}
|
|
279
248
|
async function deleteFabricAgentsSnapshot(projectRoot) {
|
|
280
249
|
const target = fabricAgentsSnapshotPath(projectRoot);
|
|
281
250
|
return rmIfExists("bootstrap-snapshot", target);
|
|
@@ -294,12 +263,6 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
|
|
|
294
263
|
projectRoot,
|
|
295
264
|
() => deleteFabricAgentsSnapshot(projectRoot)
|
|
296
265
|
);
|
|
297
|
-
await runAndCollectOne(
|
|
298
|
-
results,
|
|
299
|
-
"cursor-hook-config",
|
|
300
|
-
projectRoot,
|
|
301
|
-
() => unmergeCursorHookConfig(projectRoot)
|
|
302
|
-
);
|
|
303
266
|
await runAndCollectOne(
|
|
304
267
|
results,
|
|
305
268
|
"codex-hook-config",
|
|
@@ -385,6 +348,12 @@ async function uninstallBootstrapStage(projectRoot, _opts = {}) {
|
|
|
385
348
|
projectRoot,
|
|
386
349
|
() => uninstallFabricArchiveSkill(projectRoot)
|
|
387
350
|
);
|
|
351
|
+
await runAndCollect(
|
|
352
|
+
results,
|
|
353
|
+
"skill-router",
|
|
354
|
+
projectRoot,
|
|
355
|
+
() => uninstallFabricRouterSkill(projectRoot)
|
|
356
|
+
);
|
|
388
357
|
return results;
|
|
389
358
|
}
|
|
390
359
|
async function runAndCollect(results, step, projectRoot, fn) {
|
|
@@ -584,15 +553,6 @@ function jsonEqual(a, b) {
|
|
|
584
553
|
}
|
|
585
554
|
|
|
586
555
|
// src/commands/uninstall.ts
|
|
587
|
-
var UNINSTALL_WIZARD_GROUP_CANCELLED = /* @__PURE__ */ Symbol("uninstall-wizard-group-cancelled");
|
|
588
|
-
var KNOWLEDGE_SUBDIRS = [
|
|
589
|
-
"decisions",
|
|
590
|
-
"pitfalls",
|
|
591
|
-
"guidelines",
|
|
592
|
-
"models",
|
|
593
|
-
"processes",
|
|
594
|
-
"pending"
|
|
595
|
-
];
|
|
596
556
|
var FABRIC_STATE_FILES = ["agents.meta.json", "events.jsonl", "forensic.json"];
|
|
597
557
|
var uninstallCommand = defineCommand({
|
|
598
558
|
meta: {
|
|
@@ -707,21 +667,17 @@ async function buildUninstallExecutionPlan(target, options = {}) {
|
|
|
707
667
|
function buildUninstallFabricPlan(target, options = {}) {
|
|
708
668
|
const absTarget = normalizeTarget(target);
|
|
709
669
|
const fabricDir = join2(absTarget, ".fabric");
|
|
710
|
-
const
|
|
670
|
+
const globalStoresDir = join2(resolvePersonalFabricRoot(), ".fabric", "stores");
|
|
711
671
|
const entries = [];
|
|
712
672
|
for (const name of FABRIC_STATE_FILES) {
|
|
713
673
|
const p = join2(fabricDir, name);
|
|
714
674
|
entries.push({ path: p, kind: "state-file", absent: !existsSync2(p) });
|
|
715
675
|
}
|
|
716
|
-
|
|
717
|
-
const gk = join2(fabricDir, "knowledge", sub, ".gitkeep");
|
|
718
|
-
entries.push({ path: gk, kind: "gitkeep", absent: !existsSync2(gk) });
|
|
719
|
-
}
|
|
720
|
-
const safeEntries = entries.filter((entry) => !isInsidePersonalRoot(entry.path, personalKnowledgeDir));
|
|
676
|
+
const safeEntries = entries.filter((entry) => !isInsideGlobalStoresRoot(entry.path, globalStoresDir));
|
|
721
677
|
return {
|
|
722
678
|
target: absTarget,
|
|
723
679
|
fabricDir,
|
|
724
|
-
|
|
680
|
+
globalStoresDir,
|
|
725
681
|
options,
|
|
726
682
|
entries: safeEntries
|
|
727
683
|
};
|
|
@@ -756,8 +712,6 @@ function scaffoldStepLabel(kind) {
|
|
|
756
712
|
switch (kind) {
|
|
757
713
|
case "state-file":
|
|
758
714
|
return "scaffold-state";
|
|
759
|
-
case "gitkeep":
|
|
760
|
-
return "scaffold-gitkeep";
|
|
761
715
|
}
|
|
762
716
|
}
|
|
763
717
|
async function uninstallMcpClients(target, options = {}) {
|
|
@@ -831,12 +785,17 @@ async function uninstallMcpClients(target, options = {}) {
|
|
|
831
785
|
}
|
|
832
786
|
async function executeUninstallExecutionPlan(plan) {
|
|
833
787
|
const stageResults = [];
|
|
788
|
+
const totalStages = plan.stages.length;
|
|
789
|
+
console.log(t("cli.uninstall.plan.phase-banner", { total: String(totalStages) }));
|
|
790
|
+
let stepNum = 0;
|
|
834
791
|
for (const stage of plan.stages) {
|
|
792
|
+
stepNum += 1;
|
|
835
793
|
if (stage.skipped) {
|
|
794
|
+
console.log(formatUninstallStageHeader(stage.name, stepNum, totalStages, true));
|
|
836
795
|
stageResults.push({ name: stage.name, disposition: "skipped", steps: [] });
|
|
837
796
|
continue;
|
|
838
797
|
}
|
|
839
|
-
console.log(formatUninstallStageHeader(stage.name));
|
|
798
|
+
console.log(formatUninstallStageHeader(stage.name, stepNum, totalStages));
|
|
840
799
|
try {
|
|
841
800
|
const steps = await executeUninstallStage(plan, stage.name);
|
|
842
801
|
const disposition = steps.some((s) => s.status === "error") ? "failed" : "ran";
|
|
@@ -905,66 +864,36 @@ function createDefaultUninstallWizardAdapter() {
|
|
|
905
864
|
return {
|
|
906
865
|
async run(context) {
|
|
907
866
|
intro(t("cli.uninstall.wizard.intro"));
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
target: context.target
|
|
911
|
-
}),
|
|
912
|
-
t("cli.uninstall.wizard.overview.title")
|
|
867
|
+
const available = UNINSTALL_STAGE_KEYS.filter(
|
|
868
|
+
(key) => !context.lockedStages.includes(key)
|
|
913
869
|
);
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
870
|
+
const initialValues = available.filter((key) => !isStageSkipped(context.options, key));
|
|
871
|
+
const picked = await multiselect({
|
|
872
|
+
message: t("cli.uninstall.wizard.select.prompt", { target: context.target }),
|
|
873
|
+
options: available.map((key) => ({
|
|
874
|
+
value: key,
|
|
875
|
+
label: t(`cli.uninstall.wizard.select.${key}.label`),
|
|
876
|
+
hint: t(`cli.uninstall.wizard.select.${key}.hint`)
|
|
877
|
+
})),
|
|
878
|
+
initialValues,
|
|
879
|
+
required: false
|
|
919
880
|
});
|
|
920
|
-
if (isCancel(
|
|
881
|
+
if (isCancel(picked)) {
|
|
921
882
|
emitUninstallWizardCancellation();
|
|
922
883
|
return null;
|
|
923
884
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
message: t("cli.uninstall.wizard.stage.scaffold", {
|
|
931
|
-
defaultValue: formatPromptDefault(!context.options.skipScaffold)
|
|
932
|
-
}),
|
|
933
|
-
initialValue: !context.options.skipScaffold
|
|
934
|
-
}),
|
|
935
|
-
bootstrap: async () => context.lockedStages.includes("bootstrap") ? false : confirmInGroup({
|
|
936
|
-
message: t("cli.uninstall.wizard.stage.bootstrap", {
|
|
937
|
-
defaultValue: formatPromptDefault(!context.options.skipBootstrap)
|
|
938
|
-
}),
|
|
939
|
-
initialValue: !context.options.skipBootstrap
|
|
940
|
-
}),
|
|
941
|
-
mcp: async () => context.lockedStages.includes("mcp") ? false : confirmInGroup({
|
|
942
|
-
message: t("cli.uninstall.wizard.stage.mcp", {
|
|
943
|
-
defaultValue: formatPromptDefault(!context.options.skipMcp)
|
|
944
|
-
}),
|
|
945
|
-
initialValue: !context.options.skipMcp
|
|
946
|
-
})
|
|
947
|
-
},
|
|
948
|
-
{
|
|
949
|
-
onCancel() {
|
|
950
|
-
throw UNINSTALL_WIZARD_GROUP_CANCELLED;
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
);
|
|
954
|
-
} catch (error) {
|
|
955
|
-
if (error === UNINSTALL_WIZARD_GROUP_CANCELLED) {
|
|
956
|
-
emitUninstallWizardCancellation();
|
|
957
|
-
return null;
|
|
958
|
-
}
|
|
959
|
-
throw error;
|
|
960
|
-
}
|
|
885
|
+
const selected = new Set(picked);
|
|
886
|
+
const selection = {
|
|
887
|
+
scaffold: selected.has("scaffold"),
|
|
888
|
+
bootstrap: selected.has("bootstrap"),
|
|
889
|
+
mcp: selected.has("mcp")
|
|
890
|
+
};
|
|
961
891
|
const previewOptions = {
|
|
962
892
|
...context.options,
|
|
963
|
-
skipScaffold: !
|
|
964
|
-
skipBootstrap: !
|
|
965
|
-
skipMcp: !
|
|
893
|
+
skipScaffold: !selection.scaffold,
|
|
894
|
+
skipBootstrap: !selection.bootstrap,
|
|
895
|
+
skipMcp: !selection.mcp
|
|
966
896
|
};
|
|
967
|
-
log.step(t("cli.uninstall.wizard.step.review"));
|
|
968
897
|
printUninstallPlanSummary(context.target, previewOptions, context.supports);
|
|
969
898
|
const confirmed = await confirm({
|
|
970
899
|
message: t("cli.uninstall.wizard.execute.confirm"),
|
|
@@ -975,20 +904,24 @@ function createDefaultUninstallWizardAdapter() {
|
|
|
975
904
|
return null;
|
|
976
905
|
}
|
|
977
906
|
outro(t("cli.uninstall.wizard.outro"));
|
|
978
|
-
return
|
|
907
|
+
return selection;
|
|
979
908
|
}
|
|
980
909
|
};
|
|
981
910
|
}
|
|
911
|
+
var UNINSTALL_STAGE_KEYS = ["scaffold", "bootstrap", "mcp"];
|
|
912
|
+
function isStageSkipped(options, key) {
|
|
913
|
+
switch (key) {
|
|
914
|
+
case "scaffold":
|
|
915
|
+
return Boolean(options.skipScaffold);
|
|
916
|
+
case "bootstrap":
|
|
917
|
+
return Boolean(options.skipBootstrap);
|
|
918
|
+
case "mcp":
|
|
919
|
+
return Boolean(options.skipMcp);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
982
922
|
function emitUninstallWizardCancellation() {
|
|
983
923
|
cancel(t("cli.uninstall.wizard.cancelled"));
|
|
984
924
|
}
|
|
985
|
-
async function confirmInGroup(options) {
|
|
986
|
-
const result = await confirm(options);
|
|
987
|
-
if (isCancel(result)) {
|
|
988
|
-
throw UNINSTALL_WIZARD_GROUP_CANCELLED;
|
|
989
|
-
}
|
|
990
|
-
return result;
|
|
991
|
-
}
|
|
992
925
|
async function confirmDestructive(plan) {
|
|
993
926
|
printUninstallPlanSummary(plan.target, plan.options, plan.supports);
|
|
994
927
|
const answer = await confirm({
|
|
@@ -1035,8 +968,7 @@ function printUninstallPlanSummary(target, options, supports) {
|
|
|
1035
968
|
})
|
|
1036
969
|
);
|
|
1037
970
|
console.log(t("cli.uninstall.plan.preserves"));
|
|
1038
|
-
console.log(` -
|
|
1039
|
-
console.log(` - ~/.fabric/knowledge/ ${paint.muted(t("cli.uninstall.plan.preserves.personal"))}`);
|
|
971
|
+
console.log(` - ~/.fabric/stores/ ${paint.muted(t("cli.uninstall.plan.preserves.stores"))}`);
|
|
1040
972
|
}
|
|
1041
973
|
function printUninstallSummary(result) {
|
|
1042
974
|
const removed = result.stageResults.flatMap(
|
|
@@ -1064,8 +996,10 @@ function printUninstallSummary(result) {
|
|
|
1064
996
|
}
|
|
1065
997
|
}
|
|
1066
998
|
}
|
|
1067
|
-
function formatUninstallStageHeader(stageName) {
|
|
1068
|
-
|
|
999
|
+
function formatUninstallStageHeader(stageName, stepNum, total, skipped = false) {
|
|
1000
|
+
const label = t(`cli.uninstall.stages.${stageName}`);
|
|
1001
|
+
const head = `[${stepNum}/${total}] ${label}`;
|
|
1002
|
+
return skipped ? paint.muted(`${head} (${t("cli.shared.skipped")})`) : head;
|
|
1069
1003
|
}
|
|
1070
1004
|
function formatUninstallStageResult(stageName, steps) {
|
|
1071
1005
|
const removedCount = steps.filter((s) => s.status === "removed").length;
|
|
@@ -1082,9 +1016,9 @@ function formatUninstallStageFailure(stage, error) {
|
|
|
1082
1016
|
function resolvePersonalFabricRoot() {
|
|
1083
1017
|
return process.env.FABRIC_HOME ?? homedir();
|
|
1084
1018
|
}
|
|
1085
|
-
function
|
|
1019
|
+
function isInsideGlobalStoresRoot(candidate, globalStoresDir) {
|
|
1086
1020
|
const candidateAbs = resolve(candidate);
|
|
1087
|
-
const rootAbs = resolve(
|
|
1021
|
+
const rootAbs = resolve(globalStoresDir);
|
|
1088
1022
|
if (candidateAbs === rootAbs) {
|
|
1089
1023
|
return true;
|
|
1090
1024
|
}
|
|
@@ -1102,9 +1036,6 @@ function assertExistingDirectory(target) {
|
|
|
1102
1036
|
function isInteractiveUninstall() {
|
|
1103
1037
|
return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY) && Boolean(process.stderr.isTTY);
|
|
1104
1038
|
}
|
|
1105
|
-
function formatPromptDefault(value) {
|
|
1106
|
-
return value ? "Y/n" : "y/N";
|
|
1107
|
-
}
|
|
1108
1039
|
function yesNoLabel(value) {
|
|
1109
1040
|
return value ? t("cli.shared.yes") : t("cli.shared.no");
|
|
1110
1041
|
}
|
|
@@ -1120,7 +1051,7 @@ export {
|
|
|
1120
1051
|
uninstall_default as default,
|
|
1121
1052
|
executeUninstallExecutionPlan,
|
|
1122
1053
|
executeUninstallFabricPlan,
|
|
1123
|
-
|
|
1054
|
+
isInsideGlobalStoresRoot,
|
|
1124
1055
|
resolveUninstallExecutionPlanWithWizard,
|
|
1125
1056
|
runUninstallCommand,
|
|
1126
1057
|
shouldUseUninstallWizard,
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
getProjectTranslator
|
|
4
|
-
} from "./chunk-2CY4BMTH.js";
|
|
5
2
|
import {
|
|
6
3
|
warnUnknownFlags,
|
|
7
4
|
whoami
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-27HK6H5Y.js";
|
|
6
|
+
import "./chunk-QPAW6IYT.js";
|
|
7
|
+
import "./chunk-QFIVFZRH.js";
|
|
8
|
+
import "./chunk-FNHDQTPC.js";
|
|
9
|
+
import {
|
|
10
|
+
getProjectTranslator
|
|
11
|
+
} from "./chunk-HORSMSZL.js";
|
|
11
12
|
|
|
12
13
|
// src/commands/whoami.ts
|
|
13
14
|
import { defineCommand } from "citty";
|
|
14
15
|
var whoami_default = defineCommand({
|
|
15
|
-
meta: { name: "whoami", description: "
|
|
16
|
+
meta: { name: "whoami", description: "[DEPRECATED] Use 'fabric info --global' instead" },
|
|
16
17
|
args: {
|
|
17
18
|
// F27: `--json` machine-readable output (was silently ignored — the command
|
|
18
19
|
// declared no args, so citty swallowed the flag and still printed text).
|
|
@@ -20,6 +21,7 @@ var whoami_default = defineCommand({
|
|
|
20
21
|
},
|
|
21
22
|
run({ args }) {
|
|
22
23
|
warnUnknownFlags(["json"]);
|
|
24
|
+
console.error("\u26A0\uFE0F DEPRECATED: 'fabric whoami' is deprecated. Use 'fabric info --global' instead.");
|
|
23
25
|
const info = whoami();
|
|
24
26
|
if (args.json === true) {
|
|
25
27
|
console.log(JSON.stringify(info, null, 2));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fenglimg/fabric-cli",
|
|
3
|
-
"version": "2.2.0-rc.
|
|
4
|
-
"description": "Fabric CLI — installs the MCP server + skills + hooks for Claude Code
|
|
3
|
+
"version": "2.2.0-rc.9",
|
|
4
|
+
"description": "Fabric CLI — installs the MCP server + skills + hooks for Claude Code and Codex CLI; runs doctor / knowledge maintenance.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "wangzhichao <fenglimg90@gmail.com>",
|
|
7
7
|
"homepage": "https://github.com/fenglimg/fabric-v2#readme",
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"mcp",
|
|
19
19
|
"cli",
|
|
20
20
|
"claude-code",
|
|
21
|
-
"cursor",
|
|
22
21
|
"codex-cli",
|
|
23
22
|
"ai-knowledge-management"
|
|
24
23
|
],
|
|
@@ -40,16 +39,19 @@
|
|
|
40
39
|
"dependencies": {
|
|
41
40
|
"@clack/prompts": "^1.2.0",
|
|
42
41
|
"citty": "^0.2.2",
|
|
42
|
+
"ink": "^4.4.1",
|
|
43
43
|
"picocolors": "^1.1.1",
|
|
44
|
+
"react": "^18.3.1",
|
|
44
45
|
"string-width": "^7.2.0",
|
|
45
46
|
"tree-sitter-javascript": "^0.25.0",
|
|
46
47
|
"tree-sitter-typescript": "^0.23.2",
|
|
47
48
|
"web-tree-sitter": "^0.26.8",
|
|
48
|
-
"@fenglimg/fabric-
|
|
49
|
-
"@fenglimg/fabric-
|
|
49
|
+
"@fenglimg/fabric-server": "2.2.0-rc.9",
|
|
50
|
+
"@fenglimg/fabric-shared": "2.2.0-rc.9"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
53
|
"@types/node": "^22.15.0",
|
|
54
|
+
"@types/react": "^18.3.12",
|
|
53
55
|
"tsup": "^8.5.0",
|
|
54
56
|
"typescript": "^5.8.3"
|
|
55
57
|
},
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* The legacy hand-written cite path is still honored (back-compat).
|
|
28
28
|
* - otherwise → soft nudge: "改前先 fab_recall(paths)". NUDGE, never a gate
|
|
29
29
|
* (KT-DEC-0007): Claude Code receives it as a PreToolUse additionalContext
|
|
30
|
-
* envelope on stdout; Codex
|
|
30
|
+
* envelope on stdout; Codex as stderr. The edit always proceeds.
|
|
31
31
|
*
|
|
32
32
|
* Config (.fabric/fabric-config.json):
|
|
33
33
|
* - `cite_recall_nudge` (boolean, default true) — master switch. Set false to
|
|
@@ -48,16 +48,16 @@
|
|
|
48
48
|
* its own malfunction.
|
|
49
49
|
*
|
|
50
50
|
* Cross-client: PreToolUse(Edit|Write|MultiEdit) is registered on all three
|
|
51
|
-
* clients (Claude Code / Codex CLI
|
|
51
|
+
* clients (Claude Code / Codex CLI) — see hooks/configs/*.json. This
|
|
52
52
|
* is strictly better parity than the rc.34 hook, which was Claude-Code-only
|
|
53
|
-
* for the per-turn window and SessionStart-only for Codex
|
|
53
|
+
* for the per-turn window and SessionStart-only for Codex.
|
|
54
54
|
*/
|
|
55
55
|
|
|
56
56
|
const { readFileSync } = require("node:fs");
|
|
57
57
|
const { isAbsolute, join, relative } = require("node:path");
|
|
58
58
|
|
|
59
59
|
// Shared config read + client-aware emit (Claude Code stdout envelope vs
|
|
60
|
-
// Codex
|
|
60
|
+
// Codex stderr). The installer copies every lib/*.cjs alongside the hook.
|
|
61
61
|
const { readConfigNumber } = require("./lib/config-cache.cjs");
|
|
62
62
|
const { isClaudeCode, readStdinJson, emitContext } = require("./lib/client-adapter.cjs");
|
|
63
63
|
|
|
@@ -445,7 +445,7 @@ async function main(env, stdio) {
|
|
|
445
445
|
}
|
|
446
446
|
|
|
447
447
|
// No recall, no manual cite → soft nudge. Claude Code: PreToolUse stdout
|
|
448
|
-
// additionalContext envelope. Codex
|
|
448
|
+
// additionalContext envelope. Codex: stderr. Never a gate.
|
|
449
449
|
const streams = (env && env.stdio) || stdio || {};
|
|
450
450
|
const onClaudeCode = isClaudeCode() || (env && env.forceClaudeCode === true);
|
|
451
451
|
emitContext(renderNudge(nudgePaths), {
|