@fenglimg/fabric-cli 1.1.0 → 1.3.0
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 +24 -0
- package/dist/bootstrap-IUL4SAAK.js +14 -0
- package/dist/{bootstrap-PMIA4W6G.js → chunk-RUQCZA2Q.js} +110 -33
- package/dist/{config-PXEEXWLM.js → chunk-TO5RUB4R.js} +53 -17
- package/dist/chunk-YDZJRLHL.js +155 -0
- package/dist/config-MKWKDE32.js +16 -0
- package/dist/hooks-ZSWVH2JD.js +12 -0
- package/dist/index.js +18 -14
- package/dist/{init-R73E5YTG.js → init-BZ73IUHH.js} +277 -58
- package/dist/pre-commit-AK55G73F.js +97 -0
- package/dist/{scan-JBGFRB7P.js → scan-WKDSKEBB.js} +2 -2
- package/dist/update-JZPUJ36D.js +116 -0
- package/package.json +6 -4
- package/dist/hooks-5S5IRVQE.js +0 -124
- package/dist/pre-commit-BLSUMT3P.js +0 -59
- package/dist/{chunk-JWUO6TIS.js → chunk-N4DCTOXW.js} +5 -5
- package/dist/{doctor-QTSG2RWF.js → doctor-5KJGOV2P.js} +3 -3
- package/dist/{serve-4J2CQY25.js → serve-MMN4GYLM.js} +4 -4
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# @fenglimg/fabric-cli
|
|
2
|
+
|
|
3
|
+
`fabric` is the primary CLI binary for Fabric. `fab` is a permanent alias, so you can use either binary.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
1. Install dependencies from the monorepo root with `pnpm install`.
|
|
8
|
+
2. Build the CLI with `pnpm --filter @fenglimg/fabric-cli build`.
|
|
9
|
+
3. Run `fabric init` in the target project for the one-shot setup flow.
|
|
10
|
+
4. Start `fabric serve` and verify `fab_get_rules` in your client.
|
|
11
|
+
|
|
12
|
+
`fabric init` auto-runs `bootstrap install`, `config install`, and `hooks install`. Use them standalone only for targeted re-runs.
|
|
13
|
+
|
|
14
|
+
## Common Commands
|
|
15
|
+
|
|
16
|
+
- `fabric init`
|
|
17
|
+
- `fabric serve`
|
|
18
|
+
- `fabric doctor --audit`
|
|
19
|
+
|
|
20
|
+
## Advanced Commands
|
|
21
|
+
|
|
22
|
+
- `fabric bootstrap install`
|
|
23
|
+
- `fabric config install`
|
|
24
|
+
- `fabric hooks install`
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
bootstrapCommand,
|
|
4
|
+
bootstrap_default,
|
|
5
|
+
installBootstrap
|
|
6
|
+
} from "./chunk-RUQCZA2Q.js";
|
|
7
|
+
import "./chunk-VMYPJPKV.js";
|
|
8
|
+
import "./chunk-AEOYCVBG.js";
|
|
9
|
+
import "./chunk-6ICJICVU.js";
|
|
10
|
+
export {
|
|
11
|
+
bootstrapCommand,
|
|
12
|
+
bootstrap_default as default,
|
|
13
|
+
installBootstrap
|
|
14
|
+
};
|
|
@@ -70,24 +70,53 @@ var bootstrapCommand = defineCommand({
|
|
|
70
70
|
async run({ args }) {
|
|
71
71
|
const workspaceRoot = process.cwd();
|
|
72
72
|
const selectedClients = parseClientFilter(args.clients);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (
|
|
73
|
+
const result = await installBootstrap(workspaceRoot, {
|
|
74
|
+
clients: selectedClients === null ? void 0 : Array.from(selectedClients, mapBootstrapClientToClientKind)
|
|
75
|
+
});
|
|
76
|
+
if (result.details.length === 0) {
|
|
77
77
|
process.stderr.write(
|
|
78
78
|
`${t("cli.bootstrap.install.no-targets")}
|
|
79
79
|
`
|
|
80
80
|
);
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
|
-
for (const
|
|
84
|
-
|
|
83
|
+
for (const detail of result.details) {
|
|
84
|
+
if (detail.action === "skipped") {
|
|
85
|
+
process.stderr.write(`${t("cli.bootstrap.install.skipped-header", { path: detail.path })}
|
|
86
|
+
`);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (detail.action === "prepended") {
|
|
90
|
+
process.stderr.write(`${t("cli.bootstrap.install.prepended", { path: detail.path })}
|
|
91
|
+
`);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
process.stderr.write(`${t("cli.bootstrap.install.installed", { path: detail.path })}
|
|
95
|
+
`);
|
|
85
96
|
}
|
|
86
97
|
}
|
|
87
98
|
})
|
|
88
99
|
}
|
|
89
100
|
});
|
|
90
101
|
var bootstrap_default = bootstrapCommand;
|
|
102
|
+
async function installBootstrap(target, options = {}) {
|
|
103
|
+
const workspaceRoot = resolve(target);
|
|
104
|
+
const fabricConfig = readFabricConfig(workspaceRoot);
|
|
105
|
+
const targets = resolveBootstrapTargets(workspaceRoot, fabricConfig, options.clients);
|
|
106
|
+
const installed = [];
|
|
107
|
+
const skipped = [];
|
|
108
|
+
const details = [];
|
|
109
|
+
for (const bootstrapTarget of targets) {
|
|
110
|
+
const detail = installBootstrapTarget(bootstrapTarget, workspaceRoot, options);
|
|
111
|
+
details.push(detail);
|
|
112
|
+
if (detail.action === "skipped") {
|
|
113
|
+
skipped.push(bootstrapTarget.client);
|
|
114
|
+
} else {
|
|
115
|
+
installed.push(bootstrapTarget.client);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { installed, skipped, details };
|
|
119
|
+
}
|
|
91
120
|
function parseClientFilter(value) {
|
|
92
121
|
if (value === void 0 || value.trim().length === 0) {
|
|
93
122
|
return null;
|
|
@@ -103,15 +132,19 @@ function parseClientFilter(value) {
|
|
|
103
132
|
}
|
|
104
133
|
return clients;
|
|
105
134
|
}
|
|
106
|
-
function
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
135
|
+
function resolveBootstrapTargets(workspaceRoot, fabricConfig, selectedClients) {
|
|
136
|
+
const targets = [];
|
|
137
|
+
const seenClients = /* @__PURE__ */ new Set();
|
|
138
|
+
const clientKinds = selectedClients ?? resolveClients(workspaceRoot, fabricConfig).map((writer) => writer.clientKind);
|
|
139
|
+
for (const clientKind of clientKinds) {
|
|
140
|
+
const bootstrapClient = mapClientKind(clientKind);
|
|
141
|
+
if (bootstrapClient === null || seenClients.has(bootstrapClient)) {
|
|
142
|
+
continue;
|
|
112
143
|
}
|
|
144
|
+
seenClients.add(bootstrapClient);
|
|
145
|
+
targets.push({ client: clientKind, bootstrapClient });
|
|
113
146
|
}
|
|
114
|
-
return
|
|
147
|
+
return targets;
|
|
115
148
|
}
|
|
116
149
|
function mapClientKind(clientKind) {
|
|
117
150
|
switch (clientKind) {
|
|
@@ -132,37 +165,79 @@ function mapClientKind(clientKind) {
|
|
|
132
165
|
return null;
|
|
133
166
|
}
|
|
134
167
|
}
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
|
|
168
|
+
function mapBootstrapClientToClientKind(client) {
|
|
169
|
+
switch (client) {
|
|
170
|
+
case "claude":
|
|
171
|
+
return "ClaudeCodeCLI";
|
|
172
|
+
case "cursor":
|
|
173
|
+
return "Cursor";
|
|
174
|
+
case "windsurf":
|
|
175
|
+
return "Windsurf";
|
|
176
|
+
case "roo":
|
|
177
|
+
return "RooCode";
|
|
178
|
+
case "gemini":
|
|
179
|
+
return "GeminiCLI";
|
|
180
|
+
case "codex":
|
|
181
|
+
return "CodexCLI";
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function installBootstrapTarget(target, workspaceRoot, options) {
|
|
185
|
+
const targetPath = resolve(workspaceRoot, CLIENT_TARGET_MAP[target.bootstrapClient]);
|
|
186
|
+
const templatePath = findTemplatePath(CLIENT_TEMPLATE_MAP[target.bootstrapClient]);
|
|
138
187
|
const template = readFileSync(templatePath, "utf8");
|
|
139
188
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
|
|
189
|
+
if (target.bootstrapClient === "codex") {
|
|
190
|
+
return {
|
|
191
|
+
client: target.client,
|
|
192
|
+
path: targetPath,
|
|
193
|
+
action: writeCodexBootstrap(targetPath, template, options.force)
|
|
194
|
+
};
|
|
143
195
|
}
|
|
196
|
+
const existed = existsSync(targetPath);
|
|
144
197
|
writeFileSync(targetPath, ensureTrailingNewline(template), "utf8");
|
|
145
|
-
|
|
146
|
-
|
|
198
|
+
return {
|
|
199
|
+
client: target.client,
|
|
200
|
+
path: targetPath,
|
|
201
|
+
action: existed ? "overwritten" : "installed"
|
|
202
|
+
};
|
|
147
203
|
}
|
|
148
|
-
function writeCodexBootstrap(targetPath, template) {
|
|
204
|
+
function writeCodexBootstrap(targetPath, template, force) {
|
|
149
205
|
const nextContent = ensureTrailingNewline(template);
|
|
150
206
|
if (!existsSync(targetPath)) {
|
|
151
207
|
writeFileSync(targetPath, nextContent, "utf8");
|
|
152
|
-
|
|
153
|
-
`);
|
|
154
|
-
return;
|
|
208
|
+
return "installed";
|
|
155
209
|
}
|
|
156
210
|
const existing = readFileSync(targetPath, "utf8");
|
|
157
211
|
if (existing.includes("# Fabric Bootstrap")) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
212
|
+
if (!force) {
|
|
213
|
+
return "skipped";
|
|
214
|
+
}
|
|
215
|
+
const remainder = stripExistingCodexBootstrap(existing, nextContent);
|
|
216
|
+
writeFileSync(targetPath, joinBootstrapSections(nextContent, remainder), "utf8");
|
|
217
|
+
return "overwritten";
|
|
161
218
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
219
|
+
writeFileSync(targetPath, joinBootstrapSections(nextContent, existing), "utf8");
|
|
220
|
+
return force ? "overwritten" : "prepended";
|
|
221
|
+
}
|
|
222
|
+
function stripExistingCodexBootstrap(existing, template) {
|
|
223
|
+
if (existing.startsWith(template)) {
|
|
224
|
+
return existing.slice(template.length).replace(/^\n+/, "");
|
|
225
|
+
}
|
|
226
|
+
if (!existing.startsWith("# Fabric Bootstrap")) {
|
|
227
|
+
return existing;
|
|
228
|
+
}
|
|
229
|
+
const nextTopLevelHeadingIndex = existing.indexOf("\n# ", "# Fabric Bootstrap".length);
|
|
230
|
+
if (nextTopLevelHeadingIndex === -1) {
|
|
231
|
+
return "";
|
|
232
|
+
}
|
|
233
|
+
return existing.slice(nextTopLevelHeadingIndex + 1).replace(/^\n+/, "");
|
|
234
|
+
}
|
|
235
|
+
function joinBootstrapSections(header, body) {
|
|
236
|
+
if (body.trim().length === 0) {
|
|
237
|
+
return header;
|
|
238
|
+
}
|
|
239
|
+
const separator = body.startsWith("\n") ? "" : "\n";
|
|
240
|
+
return `${header}${separator}${body}`;
|
|
166
241
|
}
|
|
167
242
|
function ensureTrailingNewline(content) {
|
|
168
243
|
return content.endsWith("\n") ? content : `${content}
|
|
@@ -194,7 +269,9 @@ function templateCandidatesFrom(start, relativePath) {
|
|
|
194
269
|
}
|
|
195
270
|
return candidates.reverse();
|
|
196
271
|
}
|
|
272
|
+
|
|
197
273
|
export {
|
|
198
274
|
bootstrapCommand,
|
|
199
|
-
bootstrap_default
|
|
275
|
+
bootstrap_default,
|
|
276
|
+
installBootstrap
|
|
200
277
|
};
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
resolveClients
|
|
4
4
|
} from "./chunk-VMYPJPKV.js";
|
|
5
|
+
import {
|
|
6
|
+
hooksCommand
|
|
7
|
+
} from "./chunk-YDZJRLHL.js";
|
|
5
8
|
import {
|
|
6
9
|
t
|
|
7
10
|
} from "./chunk-6ICJICVU.js";
|
|
@@ -13,6 +16,7 @@ import { resolve } from "path";
|
|
|
13
16
|
import { fileURLToPath } from "url";
|
|
14
17
|
import { defineCommand } from "citty";
|
|
15
18
|
var CLIENT_ALIASES = {
|
|
19
|
+
claude: "ClaudeCodeCLI",
|
|
16
20
|
claudecodecli: "ClaudeCodeCLI",
|
|
17
21
|
"claude-code-cli": "ClaudeCodeCLI",
|
|
18
22
|
claudecli: "ClaudeCodeCLI",
|
|
@@ -57,7 +61,8 @@ async function loadFabricConfig(workspaceRoot) {
|
|
|
57
61
|
}
|
|
58
62
|
return parsed;
|
|
59
63
|
}
|
|
60
|
-
function resolveServerPath() {
|
|
64
|
+
function resolveServerPath(override) {
|
|
65
|
+
if (override) return override;
|
|
61
66
|
if (process.env.FAB_SERVER_PATH) return resolve(process.env.FAB_SERVER_PATH);
|
|
62
67
|
return fileURLToPath(import.meta.resolve("@fenglimg/fabric-server"));
|
|
63
68
|
}
|
|
@@ -71,6 +76,7 @@ var configCmd = defineCommand({
|
|
|
71
76
|
description: t("cli.config.description")
|
|
72
77
|
},
|
|
73
78
|
subCommands: {
|
|
79
|
+
hooks: hooksCommand,
|
|
74
80
|
install: defineCommand({
|
|
75
81
|
meta: {
|
|
76
82
|
name: "install",
|
|
@@ -88,36 +94,66 @@ var configCmd = defineCommand({
|
|
|
88
94
|
}
|
|
89
95
|
},
|
|
90
96
|
async run({ args }) {
|
|
91
|
-
const workspaceRoot = process.cwd();
|
|
92
|
-
const fabricConfig = await loadFabricConfig(workspaceRoot);
|
|
93
97
|
const selectedClients = parseClientFilter(args.clients);
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
);
|
|
98
|
-
if (
|
|
98
|
+
const result = await installMcpClients(process.cwd(), {
|
|
99
|
+
clients: selectedClients === null ? void 0 : Array.from(selectedClients),
|
|
100
|
+
dryRun: args["dry-run"]
|
|
101
|
+
});
|
|
102
|
+
if (result.details.length === 0) {
|
|
99
103
|
writeStderr(t("cli.config.install.no-configs"));
|
|
100
104
|
return;
|
|
101
105
|
}
|
|
102
|
-
for (const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
writeStderr(t("cli.config.install.no-config-path", { client: writer.clientKind }));
|
|
106
|
+
for (const detail of result.details) {
|
|
107
|
+
if (detail.action === "skipped") {
|
|
108
|
+
writeStderr(t("cli.config.install.no-config-path", { client: detail.client }));
|
|
106
109
|
continue;
|
|
107
110
|
}
|
|
108
|
-
if (
|
|
109
|
-
writeStderr(t("cli.config.install.dry-run", { client:
|
|
111
|
+
if (detail.action === "dry-run" && detail.path !== null) {
|
|
112
|
+
writeStderr(t("cli.config.install.dry-run", { client: detail.client, path: detail.path }));
|
|
110
113
|
continue;
|
|
111
114
|
}
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
if (detail.path !== null) {
|
|
116
|
+
writeStderr(t("cli.config.install.wrote", { client: detail.client, path: detail.path }));
|
|
117
|
+
}
|
|
114
118
|
}
|
|
115
119
|
}
|
|
116
120
|
})
|
|
117
121
|
}
|
|
118
122
|
});
|
|
119
123
|
var config_default = configCmd;
|
|
124
|
+
async function installMcpClients(target, options = {}) {
|
|
125
|
+
const workspaceRoot = resolve(target);
|
|
126
|
+
const fabricConfig = await loadFabricConfig(workspaceRoot);
|
|
127
|
+
const selectedClients = options.clients === void 0 ? null : new Set(options.clients);
|
|
128
|
+
const serverPath = resolveServerPath(options.localServerPath);
|
|
129
|
+
const writers = resolveClients(workspaceRoot, fabricConfig).filter(
|
|
130
|
+
(writer) => selectedClients === null ? true : selectedClients.has(writer.clientKind)
|
|
131
|
+
);
|
|
132
|
+
const installed = [];
|
|
133
|
+
const skipped = [];
|
|
134
|
+
const details = [];
|
|
135
|
+
for (const writer of writers) {
|
|
136
|
+
const configPath = await writer.detect(workspaceRoot);
|
|
137
|
+
if (configPath === null) {
|
|
138
|
+
skipped.push(writer.clientKind);
|
|
139
|
+
details.push({ client: writer.clientKind, path: null, action: "skipped" });
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (options.dryRun) {
|
|
143
|
+
skipped.push(writer.clientKind);
|
|
144
|
+
details.push({ client: writer.clientKind, path: configPath, action: "dry-run" });
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
await writer.write(serverPath, workspaceRoot);
|
|
148
|
+
installed.push(writer.clientKind);
|
|
149
|
+
details.push({ client: writer.clientKind, path: configPath, action: "wrote" });
|
|
150
|
+
}
|
|
151
|
+
return { installed, skipped, details };
|
|
152
|
+
}
|
|
153
|
+
|
|
120
154
|
export {
|
|
155
|
+
parseClientFilter,
|
|
121
156
|
configCmd,
|
|
122
|
-
config_default
|
|
157
|
+
config_default,
|
|
158
|
+
installMcpClients
|
|
123
159
|
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
t
|
|
4
|
+
} from "./chunk-6ICJICVU.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/hooks.ts
|
|
7
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
8
|
+
import { dirname, isAbsolute, join, parse, resolve } from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
import { defineCommand } from "citty";
|
|
11
|
+
var hooksCommand = defineCommand({
|
|
12
|
+
meta: {
|
|
13
|
+
name: "hooks",
|
|
14
|
+
description: t("cli.hooks.description")
|
|
15
|
+
},
|
|
16
|
+
subCommands: {
|
|
17
|
+
install: defineCommand({
|
|
18
|
+
meta: {
|
|
19
|
+
name: "install",
|
|
20
|
+
description: t("cli.hooks.install.description")
|
|
21
|
+
},
|
|
22
|
+
args: {
|
|
23
|
+
target: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: t("cli.hooks.install.args.target.description"),
|
|
26
|
+
default: process.cwd()
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
async run({ args }) {
|
|
30
|
+
const result = await installHooks(args.target);
|
|
31
|
+
if (result.hookAction === "skipped") {
|
|
32
|
+
writeStderr(t("cli.hooks.install.hook-skipped", { path: result.hookPath }));
|
|
33
|
+
} else if (result.hookAction === "appended") {
|
|
34
|
+
writeStderr(t("cli.hooks.install.hook-appended", { path: result.hookPath }));
|
|
35
|
+
} else {
|
|
36
|
+
writeStderr(t("cli.hooks.install.hook-created", { path: result.hookPath }));
|
|
37
|
+
}
|
|
38
|
+
if (result.prepareAction === "left") {
|
|
39
|
+
writeStderr(t("cli.hooks.install.prepare-left", { path: result.packageJsonPath }));
|
|
40
|
+
} else {
|
|
41
|
+
writeStderr(t("cli.hooks.install.prepare-added", { path: result.packageJsonPath }));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
var hooks_default = hooksCommand;
|
|
48
|
+
async function installHooks(target, options = {}) {
|
|
49
|
+
const normalizedTarget = normalizeTarget(target);
|
|
50
|
+
assertExistingDirectory(normalizedTarget);
|
|
51
|
+
const huskyDir = join(normalizedTarget, ".husky");
|
|
52
|
+
const hookPath = join(huskyDir, "pre-commit");
|
|
53
|
+
const packageJsonPath = join(normalizedTarget, "package.json");
|
|
54
|
+
if (!existsSync(packageJsonPath)) {
|
|
55
|
+
throw new Error(t("cli.hooks.errors.package-json-required", { path: packageJsonPath }));
|
|
56
|
+
}
|
|
57
|
+
mkdirSync(huskyDir, { recursive: true });
|
|
58
|
+
const templateContent = readFileSync(findTemplatePath("templates/husky/pre-commit"), "utf8");
|
|
59
|
+
const hookAction = installHookFile(hookPath, templateContent, options.force);
|
|
60
|
+
chmodSync(hookPath, 493);
|
|
61
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
62
|
+
const scripts = packageJson.scripts && typeof packageJson.scripts === "object" && !Array.isArray(packageJson.scripts) ? packageJson.scripts : {};
|
|
63
|
+
const hadPrepare = typeof scripts.prepare === "string" && scripts.prepare.trim().length > 0;
|
|
64
|
+
let prepareAction = "left";
|
|
65
|
+
if (!hadPrepare) {
|
|
66
|
+
scripts.prepare = "husky install";
|
|
67
|
+
packageJson.scripts = scripts;
|
|
68
|
+
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}
|
|
69
|
+
`, "utf8");
|
|
70
|
+
prepareAction = "added";
|
|
71
|
+
}
|
|
72
|
+
const installed = [];
|
|
73
|
+
const skipped = [];
|
|
74
|
+
if (hookAction === "skipped") {
|
|
75
|
+
skipped.push(hookPath);
|
|
76
|
+
} else {
|
|
77
|
+
installed.push(hookPath);
|
|
78
|
+
}
|
|
79
|
+
if (prepareAction === "left") {
|
|
80
|
+
skipped.push(packageJsonPath);
|
|
81
|
+
} else {
|
|
82
|
+
installed.push(packageJsonPath);
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
installed,
|
|
86
|
+
skipped,
|
|
87
|
+
hookPath,
|
|
88
|
+
packageJsonPath,
|
|
89
|
+
hookAction,
|
|
90
|
+
prepareAction
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function normalizeTarget(targetInput) {
|
|
94
|
+
return isAbsolute(targetInput) ? targetInput : resolve(process.cwd(), targetInput);
|
|
95
|
+
}
|
|
96
|
+
function assertExistingDirectory(target) {
|
|
97
|
+
if (!existsSync(target) || !statSync(target).isDirectory()) {
|
|
98
|
+
throw new Error(t("cli.shared.target-invalid", { target }));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function installHookFile(hookPath, templateContent, force) {
|
|
102
|
+
if (existsSync(hookPath)) {
|
|
103
|
+
if (force) {
|
|
104
|
+
writeFileSync(hookPath, templateContent, "utf8");
|
|
105
|
+
return "overwritten";
|
|
106
|
+
}
|
|
107
|
+
const existing = readFileSync(hookPath, "utf8");
|
|
108
|
+
if (existing.includes("FAB_BIN=")) {
|
|
109
|
+
return "skipped";
|
|
110
|
+
}
|
|
111
|
+
const fabricBlock = templateContent.replace(/^#!\/bin\/sh\n/, "");
|
|
112
|
+
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
113
|
+
writeFileSync(hookPath, `${existing}${separator}# --- Fabric ---
|
|
114
|
+
${fabricBlock}`, "utf8");
|
|
115
|
+
return "appended";
|
|
116
|
+
}
|
|
117
|
+
writeFileSync(hookPath, templateContent, "utf8");
|
|
118
|
+
return "created";
|
|
119
|
+
}
|
|
120
|
+
function findTemplatePath(relativePath) {
|
|
121
|
+
const currentModuleDir = dirname(fileURLToPath(import.meta.url));
|
|
122
|
+
const candidates = [
|
|
123
|
+
...templateCandidatesFrom(process.cwd(), relativePath),
|
|
124
|
+
...templateCandidatesFrom(currentModuleDir, relativePath)
|
|
125
|
+
];
|
|
126
|
+
for (const candidate of candidates) {
|
|
127
|
+
if (existsSync(candidate)) {
|
|
128
|
+
return candidate;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
throw new Error(t("cli.shared.template-not-found", { path: relativePath }));
|
|
132
|
+
}
|
|
133
|
+
function templateCandidatesFrom(start, relativePath) {
|
|
134
|
+
const candidates = [];
|
|
135
|
+
let current = resolve(start);
|
|
136
|
+
while (true) {
|
|
137
|
+
candidates.push(join(current, ...relativePath.split("/")));
|
|
138
|
+
const parent = dirname(current);
|
|
139
|
+
if (parent === current || parse(current).root === current) {
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
current = parent;
|
|
143
|
+
}
|
|
144
|
+
return candidates.reverse();
|
|
145
|
+
}
|
|
146
|
+
function writeStderr(message) {
|
|
147
|
+
process.stderr.write(`${message}
|
|
148
|
+
`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export {
|
|
152
|
+
hooksCommand,
|
|
153
|
+
hooks_default,
|
|
154
|
+
installHooks
|
|
155
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
configCmd,
|
|
4
|
+
config_default,
|
|
5
|
+
installMcpClients,
|
|
6
|
+
parseClientFilter
|
|
7
|
+
} from "./chunk-TO5RUB4R.js";
|
|
8
|
+
import "./chunk-VMYPJPKV.js";
|
|
9
|
+
import "./chunk-YDZJRLHL.js";
|
|
10
|
+
import "./chunk-6ICJICVU.js";
|
|
11
|
+
export {
|
|
12
|
+
configCmd,
|
|
13
|
+
config_default as default,
|
|
14
|
+
installMcpClients,
|
|
15
|
+
parseClientFilter
|
|
16
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
t
|
|
4
|
-
} from "./chunk-6ICJICVU.js";
|
|
5
2
|
|
|
6
3
|
// src/index.ts
|
|
7
4
|
import { realpathSync } from "fs";
|
|
@@ -11,25 +8,32 @@ import { defineCommand, runMain } from "citty";
|
|
|
11
8
|
|
|
12
9
|
// src/commands/index.ts
|
|
13
10
|
var allCommands = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
init: () => import("./init-BZ73IUHH.js").then((module) => module.default),
|
|
12
|
+
update: () => import("./update-JZPUJ36D.js").then((module) => module.default),
|
|
13
|
+
scan: () => import("./scan-WKDSKEBB.js").then((module) => module.default),
|
|
14
|
+
serve: () => import("./serve-MMN4GYLM.js").then((module) => module.default),
|
|
15
|
+
doctor: () => import("./doctor-5KJGOV2P.js").then((module) => module.default),
|
|
19
16
|
"sync-meta": () => import("./sync-meta-THZSEM7Y.js").then((module) => module.default),
|
|
20
17
|
"human-lint": () => import("./human-lint-YSFOZHZ7.js").then((module) => module.default),
|
|
21
18
|
"ledger-append": () => import("./ledger-append-XZ5SX4O5.js").then((module) => module.default),
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
"pre-commit": () => import("./pre-commit-AK55G73F.js").then((module) => module.default),
|
|
20
|
+
bootstrap: () => import("./bootstrap-IUL4SAAK.js").then((module) => module.default),
|
|
21
|
+
config: () => import("./config-MKWKDE32.js").then((module) => module.configCmd),
|
|
22
|
+
hooks: () => import("./hooks-ZSWVH2JD.js").then((module) => ({
|
|
23
|
+
...module.default,
|
|
24
|
+
meta: {
|
|
25
|
+
...module.default.meta,
|
|
26
|
+
hidden: true
|
|
27
|
+
}
|
|
28
|
+
}))
|
|
25
29
|
};
|
|
26
30
|
|
|
27
31
|
// src/index.ts
|
|
28
32
|
var main = defineCommand({
|
|
29
33
|
meta: {
|
|
30
|
-
name: "
|
|
31
|
-
version: "1.
|
|
32
|
-
description:
|
|
34
|
+
name: "fabric",
|
|
35
|
+
version: "1.3.0",
|
|
36
|
+
description: 'Initialize and manage Fabric projects. Use "fabric init" for one-shot setup.'
|
|
33
37
|
},
|
|
34
38
|
subCommands: allCommands
|
|
35
39
|
});
|