@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
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
scopeExplain
|
|
4
|
-
} from "./chunk-L4Q55UC4.js";
|
|
5
|
-
import "./chunk-LFIKMVY7.js";
|
|
6
|
-
import "./chunk-RYAFBNES.js";
|
|
7
|
-
|
|
8
|
-
// src/commands/scope-explain.ts
|
|
9
|
-
import { defineCommand } from "citty";
|
|
10
|
-
var scope_explain_default = defineCommand({
|
|
11
|
-
meta: {
|
|
12
|
-
name: "scope-explain",
|
|
13
|
-
description: "Explain the resolved read-set and write target for a scope"
|
|
14
|
-
},
|
|
15
|
-
args: {
|
|
16
|
-
scope: {
|
|
17
|
-
type: "positional",
|
|
18
|
-
required: true,
|
|
19
|
-
description: "Scope coordinate (e.g. team, project:x, personal)"
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
run({ args }) {
|
|
23
|
-
const result = scopeExplain(process.cwd(), args.scope);
|
|
24
|
-
if (result === null) {
|
|
25
|
-
console.log("no global Fabric config \u2014 run `fabric install --global <url>` first");
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
console.log(JSON.stringify(result, null, 2));
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
export {
|
|
32
|
-
scope_explain_default as default
|
|
33
|
-
};
|
package/dist/status-GLQWLWH6.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
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
|
-
};
|
package/dist/store-XTSE5TY6.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
storeAdd,
|
|
4
|
-
storeBind,
|
|
5
|
-
storeExplain,
|
|
6
|
-
storeList,
|
|
7
|
-
storeRemove,
|
|
8
|
-
storeSwitchWrite
|
|
9
|
-
} from "./chunk-HFQVXY6P.js";
|
|
10
|
-
import {
|
|
11
|
-
regenerateBindingsSnapshot
|
|
12
|
-
} from "./chunk-WU6GAPKH.js";
|
|
13
|
-
import "./chunk-L4Q55UC4.js";
|
|
14
|
-
import "./chunk-LFIKMVY7.js";
|
|
15
|
-
import "./chunk-RYAFBNES.js";
|
|
16
|
-
|
|
17
|
-
// src/commands/store.ts
|
|
18
|
-
import { defineCommand } from "citty";
|
|
19
|
-
var listCommand = defineCommand({
|
|
20
|
-
meta: { name: "list", description: "List mounted knowledge stores" },
|
|
21
|
-
run() {
|
|
22
|
-
const stores = storeList();
|
|
23
|
-
if (stores.length === 0) {
|
|
24
|
-
console.log("(no stores mounted)");
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
for (const store of stores) {
|
|
28
|
-
console.log(`${store.alias} ${store.store_uuid} ${store.remote ?? "(local-only)"}`);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
var addCommand = defineCommand({
|
|
33
|
-
meta: { name: "add", description: "Mount a knowledge store into the global registry" },
|
|
34
|
-
args: {
|
|
35
|
-
uuid: { type: "string", required: true, description: "Intrinsic store UUID" },
|
|
36
|
-
alias: { type: "string", required: true, description: "Local alias for this store" },
|
|
37
|
-
remote: { type: "string", description: "Git remote locator (omit for local-only)" }
|
|
38
|
-
},
|
|
39
|
-
run({ args }) {
|
|
40
|
-
const store = args.remote === void 0 ? { store_uuid: args.uuid, alias: args.alias } : { store_uuid: args.uuid, alias: args.alias, remote: args.remote };
|
|
41
|
-
const next = storeAdd(store);
|
|
42
|
-
console.log(`mounted '${args.alias}' (${next.stores.length} store(s) total)`);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
var removeCommand = defineCommand({
|
|
46
|
-
meta: { name: "remove", description: "Detach a store from the registry (does NOT delete it)" },
|
|
47
|
-
args: {
|
|
48
|
-
alias: { type: "positional", required: true, description: "Alias to detach" }
|
|
49
|
-
},
|
|
50
|
-
run({ args }) {
|
|
51
|
-
const { detached } = storeRemove(args.alias);
|
|
52
|
-
console.log(
|
|
53
|
-
detached === null ? `no store aliased '${args.alias}'` : `detached '${args.alias}' \u2014 on-disk store tree left intact (detach \u2260 delete)`
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
var explainCommand = defineCommand({
|
|
58
|
-
meta: { name: "explain", description: "Explain how a store alias resolves" },
|
|
59
|
-
args: {
|
|
60
|
-
alias: { type: "positional", required: true, description: "Alias to explain" }
|
|
61
|
-
},
|
|
62
|
-
run({ args }) {
|
|
63
|
-
const explanation = storeExplain(args.alias);
|
|
64
|
-
console.log(
|
|
65
|
-
explanation === null ? `no store aliased '${args.alias}'` : JSON.stringify(explanation, null, 2)
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
var bindCommand = defineCommand({
|
|
70
|
-
meta: { name: "bind", description: "Declare a required store on this project's config" },
|
|
71
|
-
args: {
|
|
72
|
-
id: { type: "positional", required: true, description: "Store alias/UUID to require" },
|
|
73
|
-
remote: { type: "string", description: "Suggested remote for clone onboarding" }
|
|
74
|
-
},
|
|
75
|
-
run({ args }) {
|
|
76
|
-
const entry = args.remote === void 0 ? { id: args.id } : { id: args.id, suggested_remote: args.remote };
|
|
77
|
-
const next = storeBind(process.cwd(), entry);
|
|
78
|
-
console.log(`bound required store '${args.id}' (${next.required_stores?.length ?? 0} required)`);
|
|
79
|
-
regenerateBindingsSnapshot(process.cwd(), { now: (/* @__PURE__ */ new Date()).toISOString() });
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
var switchWriteCommand = defineCommand({
|
|
83
|
-
meta: { name: "switch-write", description: "Set the active write store for non-personal scopes" },
|
|
84
|
-
args: {
|
|
85
|
-
alias: { type: "positional", required: true, description: "Alias of the store to write to" }
|
|
86
|
-
},
|
|
87
|
-
run({ args }) {
|
|
88
|
-
storeSwitchWrite(process.cwd(), args.alias);
|
|
89
|
-
console.log(`active write store set to '${args.alias}' for this project`);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
var store_default = defineCommand({
|
|
93
|
-
meta: { name: "store", description: "Manage mounted Fabric knowledge stores" },
|
|
94
|
-
subCommands: {
|
|
95
|
-
list: listCommand,
|
|
96
|
-
add: addCommand,
|
|
97
|
-
remove: removeCommand,
|
|
98
|
-
explain: explainCommand,
|
|
99
|
-
bind: bindCommand,
|
|
100
|
-
"switch-write": switchWriteCommand
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
export {
|
|
104
|
-
store_default as default
|
|
105
|
-
};
|
package/dist/sync-BJCWDPNC.js
DELETED
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
regenerateBindingsSnapshot
|
|
4
|
-
} from "./chunk-WU6GAPKH.js";
|
|
5
|
-
import "./chunk-L4Q55UC4.js";
|
|
6
|
-
import "./chunk-LFIKMVY7.js";
|
|
7
|
-
import {
|
|
8
|
-
loadGlobalConfig,
|
|
9
|
-
resolveGlobalRoot
|
|
10
|
-
} from "./chunk-RYAFBNES.js";
|
|
11
|
-
|
|
12
|
-
// src/commands/sync.ts
|
|
13
|
-
import { defineCommand } from "citty";
|
|
14
|
-
|
|
15
|
-
// src/sync/run-sync.ts
|
|
16
|
-
import { execFileSync } from "child_process";
|
|
17
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
18
|
-
import { join } from "path";
|
|
19
|
-
import { GLOBAL_STATE_DIR, storeRelativePath } from "@fenglimg/fabric-shared";
|
|
20
|
-
|
|
21
|
-
// src/sync/state-machine.ts
|
|
22
|
-
function syncTransition(state, event) {
|
|
23
|
-
switch (state) {
|
|
24
|
-
case "pending":
|
|
25
|
-
if (event === "rebase_clean") return "synced";
|
|
26
|
-
if (event === "rebase_conflict") return "conflict";
|
|
27
|
-
if (event === "network_unavailable") return "offline";
|
|
28
|
-
break;
|
|
29
|
-
case "conflict":
|
|
30
|
-
if (event === "user_continue") return "synced";
|
|
31
|
-
if (event === "user_abort") return "aborted";
|
|
32
|
-
break;
|
|
33
|
-
case "offline":
|
|
34
|
-
if (event === "retry" || event === "rebase_clean") return "synced";
|
|
35
|
-
if (event === "rebase_conflict") return "conflict";
|
|
36
|
-
if (event === "network_unavailable") return "offline";
|
|
37
|
-
break;
|
|
38
|
-
case "synced":
|
|
39
|
-
case "aborted":
|
|
40
|
-
break;
|
|
41
|
-
}
|
|
42
|
-
throw new Error(`invalid sync transition: '${state}' --${event}-->`);
|
|
43
|
-
}
|
|
44
|
-
function planSync(stores) {
|
|
45
|
-
return { stores: stores.map((s) => ({ ...s, state: "pending" })) };
|
|
46
|
-
}
|
|
47
|
-
function applySyncEvent(session, alias, event) {
|
|
48
|
-
return {
|
|
49
|
-
stores: session.stores.map(
|
|
50
|
-
(s) => s.alias === alias ? { ...s, state: syncTransition(s.state, event) } : s
|
|
51
|
-
)
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function continueSync(session) {
|
|
55
|
-
const conflicted = session.stores.find((s) => s.state === "conflict");
|
|
56
|
-
if (conflicted === void 0) {
|
|
57
|
-
throw new Error("`sync --continue` with no conflicted store to resume");
|
|
58
|
-
}
|
|
59
|
-
return applySyncEvent(session, conflicted.alias, "user_continue");
|
|
60
|
-
}
|
|
61
|
-
function abortSync(session) {
|
|
62
|
-
const conflicted = session.stores.find((s) => s.state === "conflict");
|
|
63
|
-
if (conflicted === void 0) {
|
|
64
|
-
throw new Error("`sync --abort` with no conflicted store to abort");
|
|
65
|
-
}
|
|
66
|
-
return applySyncEvent(session, conflicted.alias, "user_abort");
|
|
67
|
-
}
|
|
68
|
-
function isSyncSettled(session) {
|
|
69
|
-
return session.stores.every((s) => s.state !== "pending" && s.state !== "conflict");
|
|
70
|
-
}
|
|
71
|
-
function deferredPushStores(session) {
|
|
72
|
-
return session.stores.filter((s) => s.state === "offline");
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// src/sync/run-sync.ts
|
|
76
|
-
var NO_GLOBAL_CONFIG = "no global Fabric config \u2014 run `fabric install --global <url>` first";
|
|
77
|
-
var NO_SESSION = "no sync in progress \u2014 run `fabric sync` first";
|
|
78
|
-
var NO_CONFLICT = "no conflicted store to resume \u2014 sync is not paused";
|
|
79
|
-
function syncSessionPath(globalRoot) {
|
|
80
|
-
return join(globalRoot, GLOBAL_STATE_DIR, "sync-session.json");
|
|
81
|
-
}
|
|
82
|
-
function loadSession(globalRoot) {
|
|
83
|
-
const path = syncSessionPath(globalRoot);
|
|
84
|
-
if (!existsSync(path)) {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
return JSON.parse(readFileSync(path, "utf8"));
|
|
88
|
-
}
|
|
89
|
-
function saveSession(globalRoot, session) {
|
|
90
|
-
const path = syncSessionPath(globalRoot);
|
|
91
|
-
mkdirSync(join(path, ".."), { recursive: true });
|
|
92
|
-
writeFileSync(path, `${JSON.stringify(session, null, 2)}
|
|
93
|
-
`, "utf8");
|
|
94
|
-
}
|
|
95
|
-
function clearSession(globalRoot) {
|
|
96
|
-
rmSync(syncSessionPath(globalRoot), { force: true });
|
|
97
|
-
}
|
|
98
|
-
function defaultPull(storeDir) {
|
|
99
|
-
try {
|
|
100
|
-
execFileSync("git", ["pull", "--rebase"], {
|
|
101
|
-
cwd: storeDir,
|
|
102
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
103
|
-
});
|
|
104
|
-
return "clean";
|
|
105
|
-
} catch (error) {
|
|
106
|
-
const detail = `${gitErrText(error, "stdout")}${gitErrText(error, "stderr")}`;
|
|
107
|
-
if (/CONFLICT|could not apply|needs merge|rebase --continue/i.test(detail)) {
|
|
108
|
-
return "conflict";
|
|
109
|
-
}
|
|
110
|
-
if (/could not resolve host|could not read from remote|unable to access|connection|network is unreachable|timed out/i.test(
|
|
111
|
-
detail
|
|
112
|
-
)) {
|
|
113
|
-
return "offline";
|
|
114
|
-
}
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
function gitErrText(error, key) {
|
|
119
|
-
const value = error[key];
|
|
120
|
-
return typeof value === "string" || Buffer.isBuffer(value) ? String(value) : "";
|
|
121
|
-
}
|
|
122
|
-
function defaultRebaseContinue(storeDir) {
|
|
123
|
-
execFileSync("git", ["rebase", "--continue"], { cwd: storeDir, stdio: "ignore" });
|
|
124
|
-
}
|
|
125
|
-
function defaultRebaseAbort(storeDir) {
|
|
126
|
-
execFileSync("git", ["rebase", "--abort"], { cwd: storeDir, stdio: "ignore" });
|
|
127
|
-
}
|
|
128
|
-
var OUTCOME_EVENT = {
|
|
129
|
-
clean: "rebase_clean",
|
|
130
|
-
conflict: "rebase_conflict",
|
|
131
|
-
offline: "network_unavailable"
|
|
132
|
-
};
|
|
133
|
-
function walkPending(session, storeDirOf, pull) {
|
|
134
|
-
let next = session;
|
|
135
|
-
for (const store of session.stores) {
|
|
136
|
-
if (store.state !== "pending") {
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
const outcome = pull(storeDirOf(store));
|
|
140
|
-
next = applySyncEvent(next, store.alias, OUTCOME_EVENT[outcome]);
|
|
141
|
-
if (outcome === "conflict") {
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return next;
|
|
146
|
-
}
|
|
147
|
-
function finalize(session, options, globalRoot) {
|
|
148
|
-
const settled = isSyncSettled(session);
|
|
149
|
-
let snapshotWritten = false;
|
|
150
|
-
if (settled) {
|
|
151
|
-
clearSession(globalRoot);
|
|
152
|
-
const snapshot = regenerateBindingsSnapshot(options.projectRoot, {
|
|
153
|
-
globalRoot,
|
|
154
|
-
now: options.now,
|
|
155
|
-
...options.writeScope === void 0 ? {} : { writeScope: options.writeScope }
|
|
156
|
-
});
|
|
157
|
-
snapshotWritten = snapshot !== null;
|
|
158
|
-
} else {
|
|
159
|
-
saveSession(globalRoot, session);
|
|
160
|
-
}
|
|
161
|
-
return { session, settled, deferred: deferredPushStores(session), snapshotWritten };
|
|
162
|
-
}
|
|
163
|
-
function runStartSync(options) {
|
|
164
|
-
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
165
|
-
const config = loadGlobalConfig(globalRoot);
|
|
166
|
-
if (config === null) {
|
|
167
|
-
throw new Error(NO_GLOBAL_CONFIG);
|
|
168
|
-
}
|
|
169
|
-
const syncable = config.stores.filter((store) => store.remote !== void 0);
|
|
170
|
-
const session = planSync(
|
|
171
|
-
syncable.map((store) => ({ alias: store.alias, store_uuid: store.store_uuid }))
|
|
172
|
-
);
|
|
173
|
-
const storeDirOf = (status) => join(globalRoot, storeRelativePath(status.store_uuid));
|
|
174
|
-
const walked = walkPending(session, storeDirOf, options.pull ?? defaultPull);
|
|
175
|
-
return finalize(walked, options, globalRoot);
|
|
176
|
-
}
|
|
177
|
-
function runContinueSync(options) {
|
|
178
|
-
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
179
|
-
const session = loadSession(globalRoot);
|
|
180
|
-
if (session === null) {
|
|
181
|
-
throw new Error(NO_SESSION);
|
|
182
|
-
}
|
|
183
|
-
const conflicted = session.stores.find((store) => store.state === "conflict");
|
|
184
|
-
if (conflicted === void 0) {
|
|
185
|
-
throw new Error(NO_CONFLICT);
|
|
186
|
-
}
|
|
187
|
-
const storeDirOf = (status) => join(globalRoot, storeRelativePath(status.store_uuid));
|
|
188
|
-
(options.rebaseContinue ?? defaultRebaseContinue)(storeDirOf(conflicted));
|
|
189
|
-
const resumed = walkPending(continueSync(session), storeDirOf, options.pull ?? defaultPull);
|
|
190
|
-
return finalize(resumed, options, globalRoot);
|
|
191
|
-
}
|
|
192
|
-
function runAbortSync(options) {
|
|
193
|
-
const globalRoot = options.globalRoot ?? resolveGlobalRoot();
|
|
194
|
-
const session = loadSession(globalRoot);
|
|
195
|
-
if (session === null) {
|
|
196
|
-
throw new Error(NO_SESSION);
|
|
197
|
-
}
|
|
198
|
-
const conflicted = session.stores.find((store) => store.state === "conflict");
|
|
199
|
-
if (conflicted === void 0) {
|
|
200
|
-
throw new Error(NO_CONFLICT);
|
|
201
|
-
}
|
|
202
|
-
const storeDirOf = (status) => join(globalRoot, storeRelativePath(status.store_uuid));
|
|
203
|
-
(options.rebaseAbort ?? defaultRebaseAbort)(storeDirOf(conflicted));
|
|
204
|
-
const resumed = walkPending(abortSync(session), storeDirOf, options.pull ?? defaultPull);
|
|
205
|
-
return finalize(resumed, options, globalRoot);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// src/commands/sync.ts
|
|
209
|
-
function report(result) {
|
|
210
|
-
for (const store of result.session.stores) {
|
|
211
|
-
console.log(`${store.alias} ${store.state}`);
|
|
212
|
-
}
|
|
213
|
-
if (result.deferred.length > 0) {
|
|
214
|
-
console.log(
|
|
215
|
-
`${result.deferred.length} store(s) offline \u2014 push deferred; re-run \`fabric sync\` when online`
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
if (!result.settled) {
|
|
219
|
-
console.log(
|
|
220
|
-
"sync paused on a conflict \u2014 resolve it, then run `fabric sync --continue` (or `--abort`)"
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
var sync_default = defineCommand({
|
|
225
|
-
meta: { name: "sync", description: "Pull --rebase + push every mounted store; resume conflicts" },
|
|
226
|
-
args: {
|
|
227
|
-
continue: { type: "boolean", description: "Resume after resolving a rebase conflict" },
|
|
228
|
-
abort: { type: "boolean", description: "Abort the conflicted store's rebase" }
|
|
229
|
-
},
|
|
230
|
-
run({ args }) {
|
|
231
|
-
const options = { projectRoot: process.cwd(), now: (/* @__PURE__ */ new Date()).toISOString() };
|
|
232
|
-
if (args.continue === true) {
|
|
233
|
-
report(runContinueSync(options));
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
if (args.abort === true) {
|
|
237
|
-
report(runAbortSync(options));
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
report(runStartSync(options));
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
export {
|
|
244
|
-
sync_default as default
|
|
245
|
-
};
|
package/dist/whoami-B6AEMSEV.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
whoami
|
|
4
|
-
} from "./chunk-T5RPGCCM.js";
|
|
5
|
-
import "./chunk-LFIKMVY7.js";
|
|
6
|
-
import "./chunk-RYAFBNES.js";
|
|
7
|
-
|
|
8
|
-
// src/commands/whoami.ts
|
|
9
|
-
import { defineCommand } from "citty";
|
|
10
|
-
var whoami_default = defineCommand({
|
|
11
|
-
meta: { name: "whoami", description: "Show this machine's Fabric uid and mounted stores" },
|
|
12
|
-
run() {
|
|
13
|
-
const info = whoami();
|
|
14
|
-
if (info === null) {
|
|
15
|
-
console.log("no global Fabric config \u2014 run `fabric install --global <url>` first");
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
console.log(`uid: ${info.uid}`);
|
|
19
|
-
if (info.stores.length === 0) {
|
|
20
|
-
console.log("stores: (none mounted)");
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
console.log("stores:");
|
|
24
|
-
for (const store of info.stores) {
|
|
25
|
-
console.log(` ${store.alias} ${store.store_uuid}${store.local_only ? " (local-only)" : ""}`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
export {
|
|
30
|
-
whoami_default as default
|
|
31
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 1,
|
|
3
|
-
"hooks": {
|
|
4
|
-
"stop": [
|
|
5
|
-
{ "command": ".cursor/hooks/fabric-hint.cjs" }
|
|
6
|
-
],
|
|
7
|
-
"sessionStart": [
|
|
8
|
-
{ "command": ".cursor/hooks/knowledge-hint-broad.cjs" },
|
|
9
|
-
{ "command": ".cursor/hooks/cite-policy-evict.cjs" }
|
|
10
|
-
],
|
|
11
|
-
"preToolUse": [
|
|
12
|
-
{
|
|
13
|
-
"matcher": "Edit|Write|MultiEdit",
|
|
14
|
-
"command": ".cursor/hooks/knowledge-hint-narrow.cjs"
|
|
15
|
-
}
|
|
16
|
-
]
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
// v2.0.0-rc.24 TASK-05: L1 Stop hook soft reminder for missing cite contract.
|
|
2
|
-
//
|
|
3
|
-
// Reads `.fabric/agents.meta.json` to build a stable_id → knowledge_type lookup
|
|
4
|
-
// map, then scans summarised assistant turns (cite_ids + cite_tags +
|
|
5
|
-
// cite_commitments parallel arrays produced by lib/cite-line-parser.cjs) for
|
|
6
|
-
// turns that cited a decision-class or pitfall-class id with [applied] tag
|
|
7
|
-
// but no operator commitment and no skip:<reason>. (v2.1.0-rc.1 ADJ-P4-1:
|
|
8
|
-
// legacy [recalled] is remapped to [applied] by the parser upstream.)
|
|
9
|
-
//
|
|
10
|
-
// Emits one reminder line per offending id (deduplicated across the turn
|
|
11
|
-
// summary). Non-blocking — caller writes the lines to stderr; failure to
|
|
12
|
-
// load the meta file or absence of offenders means zero output.
|
|
13
|
-
//
|
|
14
|
-
// Reminder template (rc.24 lock B2 / L1 enforcement layer):
|
|
15
|
-
// ⚠ KB: <id> cited as [applied] but missing contract; add → edit:<glob>
|
|
16
|
-
// or → skip:<reason> next turn
|
|
17
|
-
//
|
|
18
|
-
// Type filter rationale: only `decision` and `pitfall` types are contract-
|
|
19
|
-
// required per rc.24 design lock B6 (idTypeMap routing). `model`,
|
|
20
|
-
// `guideline`, `process` use reference-cite or LLM-judge (deferred to rc.25+)
|
|
21
|
-
// and are intentionally skipped here to avoid false-positive nudges.
|
|
22
|
-
//
|
|
23
|
-
// agents.meta.json schema note: `description.knowledge_type` values are
|
|
24
|
-
// SINGULAR (`decision`, `pitfall`, `model`, `guideline`, `process`) per
|
|
25
|
-
// packages/shared/src/schemas/agents-meta.ts. The reminder filter normalises
|
|
26
|
-
// any plural input defensively but the canonical contract is singular.
|
|
27
|
-
//
|
|
28
|
-
// Reading happens once per hook invocation (caller passes the projectRoot;
|
|
29
|
-
// the lib does the fs read internally). The map is small (<200 entries in
|
|
30
|
-
// typical corpora) so caching beyond the per-invocation scope is unnecessary.
|
|
31
|
-
|
|
32
|
-
const { existsSync, readFileSync } = require("node:fs");
|
|
33
|
-
const { join } = require("node:path");
|
|
34
|
-
|
|
35
|
-
const FABRIC_DIR = ".fabric";
|
|
36
|
-
const AGENTS_META_FILE = "agents.meta.json";
|
|
37
|
-
|
|
38
|
-
// Knowledge types that require contract commitments on [applied] cites.
|
|
39
|
-
// Matches the singular form persisted by `withDerivedAgentsMetaNodeDefaults`
|
|
40
|
-
// in packages/shared/src/schemas/agents-meta.ts. We accept both singular
|
|
41
|
-
// and plural defensively so a future schema change to plurals doesn't
|
|
42
|
-
// silently break the filter.
|
|
43
|
-
const CONTRACT_REQUIRED_TYPES = new Set([
|
|
44
|
-
"decision",
|
|
45
|
-
"decisions",
|
|
46
|
-
"pitfall",
|
|
47
|
-
"pitfalls",
|
|
48
|
-
]);
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Build a Map<stable_id, knowledge_type> from <projectRoot>/.fabric/agents.meta.json.
|
|
52
|
-
*
|
|
53
|
-
* Never throws — missing file, malformed JSON, missing nodes key, etc. all
|
|
54
|
-
* yield an empty Map. The caller's downstream filter then becomes a no-op
|
|
55
|
-
* (no id resolves → no reminders).
|
|
56
|
-
*
|
|
57
|
-
* @param {string} projectRoot - workspace root
|
|
58
|
-
* @returns {Map<string, string>} stable_id → knowledge_type (singular)
|
|
59
|
-
*/
|
|
60
|
-
function readKnowledgeTypeMap(projectRoot) {
|
|
61
|
-
const out = new Map();
|
|
62
|
-
if (typeof projectRoot !== "string" || projectRoot.length === 0) return out;
|
|
63
|
-
|
|
64
|
-
const metaPath = join(projectRoot, FABRIC_DIR, AGENTS_META_FILE);
|
|
65
|
-
if (!existsSync(metaPath)) return out;
|
|
66
|
-
|
|
67
|
-
let raw;
|
|
68
|
-
try {
|
|
69
|
-
raw = readFileSync(metaPath, "utf8");
|
|
70
|
-
} catch {
|
|
71
|
-
return out;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let parsed;
|
|
75
|
-
try {
|
|
76
|
-
parsed = JSON.parse(raw);
|
|
77
|
-
} catch {
|
|
78
|
-
return out;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (parsed === null || typeof parsed !== "object") return out;
|
|
82
|
-
const nodes = parsed.nodes;
|
|
83
|
-
if (nodes === null || typeof nodes !== "object") return out;
|
|
84
|
-
|
|
85
|
-
for (const [id, node] of Object.entries(nodes)) {
|
|
86
|
-
if (node === null || typeof node !== "object") continue;
|
|
87
|
-
const description = node.description;
|
|
88
|
-
if (description === null || typeof description !== "object") continue;
|
|
89
|
-
const kt = description.knowledge_type;
|
|
90
|
-
if (typeof kt !== "string" || kt.length === 0) continue;
|
|
91
|
-
out.set(id, kt);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return out;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Scan parsed assistant turns for cites that should have a contract but
|
|
99
|
-
* don't, returning the reminder lines to emit.
|
|
100
|
-
*
|
|
101
|
-
* Filter (all must hold for a given index i within a turn):
|
|
102
|
-
* 1. cite_tags includes "applied" (turn-level — applies to the cited id)
|
|
103
|
-
* 2. cite_commitments[i].operators is empty AND cite_commitments[i].skip_reason is null
|
|
104
|
-
* 3. idTypeMap.get(cite_ids[i]) is in {decision, pitfall}
|
|
105
|
-
*
|
|
106
|
-
* v2.1.0-rc.1 (ADJ-P4-1, full remap): the gate is the rc.37 NEW-1 `applied`
|
|
107
|
-
* tag. Legacy [recalled] cites are remapped to [applied] by the parser before
|
|
108
|
-
* they reach here, so gating on "applied" covers both old and new authoring.
|
|
109
|
-
*
|
|
110
|
-
* Tag-level filter clarification: rc.20 cite_tags is parallel to ALL parsed
|
|
111
|
-
* lines (including sentinels), but for the contract-missing reminder we use
|
|
112
|
-
* the turn-level semantic — if the assistant tagged the cite as [applied],
|
|
113
|
-
* the operator-or-skip contract applies. Per TASK-04 invariant, cite_ids and
|
|
114
|
-
* cite_commitments are parallel index-aligned arrays (length-N each).
|
|
115
|
-
*
|
|
116
|
-
* Sentinel turns (cite_ids=[], cite_tags=["none"]) contribute no offenders
|
|
117
|
-
* because the cite_ids loop has zero iterations.
|
|
118
|
-
*
|
|
119
|
-
* Offenders are deduplicated by id across the entire turn array; multiple
|
|
120
|
-
* turns citing the same id yield ONE reminder line.
|
|
121
|
-
*
|
|
122
|
-
* @param {Object} args
|
|
123
|
-
* @param {Array<{cite_ids: string[], cite_tags: string[], cite_commitments: Array<{operators: Array<unknown>, skip_reason: string|null}>}>} args.assistant_turns
|
|
124
|
-
* @param {Map<string, string>} args.idTypeMap
|
|
125
|
-
* @returns {string[]} reminder lines (empty when no offenders)
|
|
126
|
-
*/
|
|
127
|
-
function formatContractMissingReminders({ assistant_turns, idTypeMap }) {
|
|
128
|
-
if (!Array.isArray(assistant_turns) || assistant_turns.length === 0) return [];
|
|
129
|
-
if (!(idTypeMap instanceof Map) || idTypeMap.size === 0) return [];
|
|
130
|
-
|
|
131
|
-
const offenders = new Set();
|
|
132
|
-
|
|
133
|
-
for (const turn of assistant_turns) {
|
|
134
|
-
if (turn === null || typeof turn !== "object") continue;
|
|
135
|
-
const citeIds = Array.isArray(turn.cite_ids) ? turn.cite_ids : [];
|
|
136
|
-
const citeTags = Array.isArray(turn.cite_tags) ? turn.cite_tags : [];
|
|
137
|
-
const commitments = Array.isArray(turn.cite_commitments) ? turn.cite_commitments : [];
|
|
138
|
-
|
|
139
|
-
// Turn-level: the [applied] tag must appear in the turn's tag set
|
|
140
|
-
// (v2.1.0-rc.1 ADJ-P4-1: legacy [recalled] is remapped to [applied]).
|
|
141
|
-
if (!citeTags.includes("applied")) continue;
|
|
142
|
-
|
|
143
|
-
// Iterate by cite_ids.length — sentinel entries don't have ids so they
|
|
144
|
-
// contribute zero iterations even if cite_tags carries "none".
|
|
145
|
-
for (let i = 0; i < citeIds.length; i += 1) {
|
|
146
|
-
const id = citeIds[i];
|
|
147
|
-
if (typeof id !== "string" || id.length === 0) continue;
|
|
148
|
-
|
|
149
|
-
const type = idTypeMap.get(id);
|
|
150
|
-
if (!CONTRACT_REQUIRED_TYPES.has(type)) continue;
|
|
151
|
-
|
|
152
|
-
const commitment = commitments[i];
|
|
153
|
-
if (commitment === null || typeof commitment !== "object") continue;
|
|
154
|
-
const operators = Array.isArray(commitment.operators) ? commitment.operators : [];
|
|
155
|
-
const skipReason = commitment.skip_reason;
|
|
156
|
-
const hasContract = operators.length > 0 || (typeof skipReason === "string" && skipReason.length > 0);
|
|
157
|
-
if (hasContract) continue;
|
|
158
|
-
|
|
159
|
-
offenders.add(id);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (offenders.size === 0) return [];
|
|
164
|
-
|
|
165
|
-
// Stable order: insertion order is the order ids first appeared across turns.
|
|
166
|
-
const reminders = [];
|
|
167
|
-
for (const id of offenders) {
|
|
168
|
-
reminders.push(
|
|
169
|
-
`⚠ KB: ${id} cited as [applied] but missing contract; add \`→ edit:<glob>\` or \`→ skip:<reason>\` next turn`,
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
return reminders;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
module.exports = {
|
|
176
|
-
readKnowledgeTypeMap,
|
|
177
|
-
formatContractMissingReminders,
|
|
178
|
-
CONTRACT_REQUIRED_TYPES,
|
|
179
|
-
};
|