@fenglimg/fabric-cli 0.1.4 → 1.0.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.
Files changed (30) hide show
  1. package/dist/{bootstrap-HUDJ2E3Q.js → bootstrap-PMIA4W6G.js} +16 -12
  2. package/dist/{chunk-T3WQUWW4.js → chunk-5BSTO745.js} +9 -6
  3. package/dist/chunk-6ICJICVU.js +10 -0
  4. package/dist/chunk-AEOYCVBG.js +0 -0
  5. package/dist/chunk-DKQ3HOTK.js +206 -0
  6. package/dist/{chunk-U376IPKT.js → chunk-F2BXHPM5.js} +11 -7
  7. package/dist/{chunk-CZ7U6ULM.js → chunk-L43IGJ6X.js} +17 -7
  8. package/dist/chunk-P4KVFB2T.js +0 -0
  9. package/dist/{chunk-N7TTCGJA.js → chunk-VMYPJPKV.js} +1 -0
  10. package/dist/chunk-WWNXR34K.js +49 -0
  11. package/dist/{config-YKDWIRCT.js → config-PXEEXWLM.js} +14 -11
  12. package/dist/{hooks-VXXO4VZP.js → hooks-5S5IRVQE.js} +15 -12
  13. package/dist/human-lint-YSFOZHZ7.js +13 -0
  14. package/dist/index.js +15 -11
  15. package/dist/init-G6Q3OOMC.js +601 -0
  16. package/dist/{ledger-append-EGIKSMU5.js → ledger-append-XZ5SX4O5.js} +2 -1
  17. package/dist/{pre-commit-CXPH7BZH.js → pre-commit-IEIXHKOD.js} +13 -7
  18. package/dist/{scan-UASZQLQP.js → scan-6CURGC3D.js} +3 -1
  19. package/dist/serve-4J2CQY25.js +112 -0
  20. package/dist/{sync-meta-YTG5V3Y6.js → sync-meta-L6M4AEUT.js} +2 -1
  21. package/package.json +12 -8
  22. package/templates/agents-md/AGENTS.md.template +17 -11
  23. package/templates/agents-md/variants/cocos.md +37 -0
  24. package/templates/agents-md/variants/next.md +37 -0
  25. package/templates/agents-md/variants/vite.md +37 -0
  26. package/templates/claude-hooks/agents-md-init-reminder.cjs +18 -0
  27. package/templates/claude-skills/agents-md-init/SKILL.md +86 -0
  28. package/dist/chunk-BWZHNZG6.js +0 -236
  29. package/dist/human-lint-II6TBGP4.js +0 -9
  30. package/dist/init-IBS7KO7A.js +0 -149
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolveClients
4
- } from "./chunk-N7TTCGJA.js";
4
+ } from "./chunk-VMYPJPKV.js";
5
5
  import {
6
6
  readFabricConfig
7
7
  } from "./chunk-AEOYCVBG.js";
8
+ import {
9
+ t
10
+ } from "./chunk-6ICJICVU.js";
8
11
 
9
12
  // src/commands/bootstrap.ts
10
13
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -50,18 +53,18 @@ var CLIENT_TARGET_MAP = {
50
53
  var bootstrapCommand = defineCommand({
51
54
  meta: {
52
55
  name: "bootstrap",
53
- description: "\u4E3A\u652F\u6301\u7684 AI \u5BA2\u6237\u7AEF\u5B89\u88C5 Fabric \u5F15\u5BFC\u63D0\u793A\u6A21\u677F\u3002"
56
+ description: t("cli.bootstrap.description")
54
57
  },
55
58
  subCommands: {
56
59
  install: defineCommand({
57
60
  meta: {
58
61
  name: "install",
59
- description: "\u5C06 Fabric \u5F15\u5BFC\u6A21\u677F\u590D\u5236\u5230\u5404\u5BA2\u6237\u7AEF\u7684\u539F\u751F\u4F4D\u7F6E\u3002"
62
+ description: t("cli.bootstrap.install.description")
60
63
  },
61
64
  args: {
62
65
  clients: {
63
66
  type: "string",
64
- description: "\u53EF\u9009\u7684\u9017\u53F7\u5206\u9694\u5BA2\u6237\u7AEF\u8FC7\u6EE4\u5668\uFF0C\u4F8B\u5982 claude,cursor,codex\u3002"
67
+ description: t("cli.bootstrap.install.args.clients.description")
65
68
  }
66
69
  },
67
70
  async run({ args }) {
@@ -72,7 +75,8 @@ var bootstrapCommand = defineCommand({
72
75
  const clients = selectedClients ?? detectedClients;
73
76
  if (clients.size === 0) {
74
77
  process.stderr.write(
75
- "No bootstrap targets detected. Pass --clients claude,cursor,windsurf,roo,gemini,codex to install explicitly.\n"
78
+ `${t("cli.bootstrap.install.no-targets")}
79
+ `
76
80
  );
77
81
  return;
78
82
  }
@@ -93,7 +97,7 @@ function parseClientFilter(value) {
93
97
  const alias = rawClient.trim().toLowerCase();
94
98
  const client = CLIENT_ALIASES[alias];
95
99
  if (client === void 0) {
96
- throw new Error(`Unknown client "${rawClient}". Use a comma-separated list such as claude,cursor,codex.`);
100
+ throw new Error(t("cli.bootstrap.errors.unknown-client", { client: rawClient }));
97
101
  }
98
102
  clients.add(client);
99
103
  }
@@ -138,26 +142,26 @@ function installBootstrap(client, workspaceRoot) {
138
142
  return;
139
143
  }
140
144
  writeFileSync(targetPath, ensureTrailingNewline(template), "utf8");
141
- process.stderr.write(`Installed ${targetPath}
145
+ process.stderr.write(`${t("cli.bootstrap.install.installed", { path: targetPath })}
142
146
  `);
143
147
  }
144
148
  function writeCodexBootstrap(targetPath, template) {
145
149
  const nextContent = ensureTrailingNewline(template);
146
150
  if (!existsSync(targetPath)) {
147
151
  writeFileSync(targetPath, nextContent, "utf8");
148
- process.stderr.write(`Installed ${targetPath}
152
+ process.stderr.write(`${t("cli.bootstrap.install.installed", { path: targetPath })}
149
153
  `);
150
154
  return;
151
155
  }
152
156
  const existing = readFileSync(targetPath, "utf8");
153
157
  if (existing.includes("# Fabric Bootstrap")) {
154
- process.stderr.write(`Skipped ${targetPath}: Fabric Bootstrap header already present.
158
+ process.stderr.write(`${t("cli.bootstrap.install.skipped-header", { path: targetPath })}
155
159
  `);
156
160
  return;
157
161
  }
158
162
  const separator = existing.startsWith("\n") || existing.length === 0 ? "" : "\n";
159
163
  writeFileSync(targetPath, `${nextContent}${separator}${existing}`, "utf8");
160
- process.stderr.write(`Prepended ${targetPath}
164
+ process.stderr.write(`${t("cli.bootstrap.install.prepended", { path: targetPath })}
161
165
  `);
162
166
  }
163
167
  function ensureTrailingNewline(content) {
@@ -175,7 +179,7 @@ function findTemplatePath(relativePath) {
175
179
  return candidate;
176
180
  }
177
181
  }
178
- throw new Error(`Template not found: ${relativePath}`);
182
+ throw new Error(t("cli.shared.template-not-found", { path: relativePath }));
179
183
  }
180
184
  function templateCandidatesFrom(start, relativePath) {
181
185
  const candidates = [];
@@ -188,7 +192,7 @@ function templateCandidatesFrom(start, relativePath) {
188
192
  }
189
193
  current = parent;
190
194
  }
191
- return candidates;
195
+ return candidates.reverse();
192
196
  }
193
197
  export {
194
198
  bootstrapCommand,
@@ -2,6 +2,9 @@
2
2
  import {
3
3
  resolveIgnores
4
4
  } from "./chunk-P4KVFB2T.js";
5
+ import {
6
+ t
7
+ } from "./chunk-6ICJICVU.js";
5
8
 
6
9
  // src/commands/sync-meta.ts
7
10
  import { createHash } from "crypto";
@@ -11,17 +14,17 @@ import { defineCommand } from "citty";
11
14
  var syncMetaCommand = defineCommand({
12
15
  meta: {
13
16
  name: "sync-meta",
14
- description: "\u4ECE AGENTS.md \u6587\u4EF6\u540C\u6B65 Fabric \u5143\u6570\u636E\u3002"
17
+ description: t("cli.sync-meta.description")
15
18
  },
16
19
  args: {
17
20
  target: {
18
21
  type: "string",
19
- description: "\u76EE\u6807\u9879\u76EE\u8DEF\u5F84\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u3002",
22
+ description: t("cli.sync-meta.args.target.description"),
20
23
  default: process.cwd()
21
24
  },
22
25
  "check-only": {
23
26
  type: "boolean",
24
- description: "\u5982\u679C .fabric/agents.meta.json \u5DF2\u8FC7\u671F\u5219\u4EE5\u4EE3\u7801 1 \u9000\u51FA\u3002",
27
+ description: t("cli.sync-meta.args.check-only.description"),
25
28
  default: false
26
29
  }
27
30
  },
@@ -32,7 +35,7 @@ var syncMetaCommand = defineCommand({
32
35
  const existingMeta = readExistingMeta(metaPath);
33
36
  if (args["check-only"]) {
34
37
  if (!existingMeta || stableStringify(existingMeta) !== stableStringify(computedMeta)) {
35
- writeStderr("Fabric metadata drift detected. Run fab sync-meta to update.");
38
+ writeStderr(t("cli.sync-meta.drift-detected"));
36
39
  process.exitCode = 1;
37
40
  }
38
41
  return;
@@ -43,7 +46,7 @@ var syncMetaCommand = defineCommand({
43
46
  mkdirSync(join(target, ".fabric"), { recursive: true });
44
47
  writeFileSync(metaPath, `${JSON.stringify(computedMeta, null, 2)}
45
48
  `, "utf8");
46
- writeStderr(`Updated ${metaPath}`);
49
+ writeStderr(t("cli.sync-meta.updated", { label: t("cli.shared.updated"), path: metaPath }));
47
50
  }
48
51
  });
49
52
  var sync_meta_default = syncMetaCommand;
@@ -77,7 +80,7 @@ function normalizeTarget(targetInput) {
77
80
  }
78
81
  function assertExistingDirectory(target) {
79
82
  if (!existsSync(target) || !statSync(target).isDirectory()) {
80
- throw new Error(`Target must be an existing directory: ${target}`);
83
+ throw new Error(t("cli.shared.target-invalid", { target }));
81
84
  }
82
85
  }
83
86
  function readExistingMeta(metaPath) {
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/i18n.ts
4
+ import { createTranslator, detectNodeLocale } from "@fenglimg/fabric-shared";
5
+ var locale = detectNodeLocale();
6
+ var t = createTranslator(locale);
7
+
8
+ export {
9
+ t
10
+ };
File without changes
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ createDebugLogger,
4
+ readFabricConfig,
5
+ resolveDevMode
6
+ } from "./chunk-AEOYCVBG.js";
7
+ import {
8
+ resolveIgnores
9
+ } from "./chunk-P4KVFB2T.js";
10
+ import {
11
+ displayWidth,
12
+ padEnd,
13
+ paint,
14
+ symbol
15
+ } from "./chunk-WWNXR34K.js";
16
+ import {
17
+ t
18
+ } from "./chunk-6ICJICVU.js";
19
+
20
+ // src/commands/scan.ts
21
+ import { existsSync, readdirSync, readFileSync, statSync } from "fs";
22
+ import { isAbsolute, join, relative, resolve, sep } from "path";
23
+ import { defineCommand } from "citty";
24
+
25
+ // src/scanner/detector.ts
26
+ import { detectFramework } from "@fenglimg/fabric-shared";
27
+
28
+ // src/commands/scan.ts
29
+ function createScanReport(targetInput = process.cwd(), fabricConfig) {
30
+ const target = normalizeTarget(targetInput);
31
+ const framework = detectFramework(target);
32
+ const readmeQuality = getReadmeQuality(target);
33
+ const hasContributing = existsSync(join(target, "CONTRIBUTING.md"));
34
+ const hasExistingFabric = existsSync(join(target, "AGENTS.md")) || existsSync(join(target, ".fabric"));
35
+ const walkResult = walkFiles(target, resolveIgnores(fabricConfig));
36
+ return {
37
+ target,
38
+ framework,
39
+ readmeQuality,
40
+ hasContributing,
41
+ fileCount: walkResult.fileCount,
42
+ ignoredCount: walkResult.ignoredCount,
43
+ hasExistingFabric,
44
+ recommendations: buildRecommendations({
45
+ framework,
46
+ readmeQuality,
47
+ hasContributing,
48
+ hasExistingFabric
49
+ })
50
+ };
51
+ }
52
+ var scanCommand = defineCommand({
53
+ meta: {
54
+ name: "scan",
55
+ description: t("cli.scan.description")
56
+ },
57
+ args: {
58
+ target: {
59
+ type: "string",
60
+ description: t("cli.scan.args.target.description")
61
+ },
62
+ debug: {
63
+ type: "boolean",
64
+ description: t("cli.scan.args.debug.description"),
65
+ default: false
66
+ },
67
+ json: {
68
+ type: "boolean",
69
+ description: t("cli.scan.args.json.description"),
70
+ default: false
71
+ }
72
+ },
73
+ async run({ args }) {
74
+ const workspaceRoot = process.cwd();
75
+ const logger = createDebugLogger(args.debug);
76
+ const resolution = resolveDevMode(args.target, workspaceRoot);
77
+ const fabricConfig = readFabricConfig(workspaceRoot);
78
+ logger(`scan target source: ${resolution.source}`);
79
+ for (const step of resolution.chain) {
80
+ logger(step);
81
+ }
82
+ const report = createScanReport(resolution.target, fabricConfig);
83
+ if (args.json) {
84
+ console.log(JSON.stringify(report, null, 2));
85
+ return;
86
+ }
87
+ printPrettyReport(report, Boolean(args.debug));
88
+ }
89
+ });
90
+ var scan_default = scanCommand;
91
+ function normalizeTarget(targetInput) {
92
+ return isAbsolute(targetInput) ? targetInput : resolve(process.cwd(), targetInput);
93
+ }
94
+ function getReadmeQuality(target) {
95
+ const readmePath = join(target, "README.md");
96
+ if (!existsSync(readmePath)) {
97
+ return "stub";
98
+ }
99
+ const wordCount = readFileSync(readmePath, "utf8").trim().split(/\s+/).filter(Boolean).length;
100
+ return wordCount >= 200 ? "ok" : "stub";
101
+ }
102
+ function walkFiles(root, ignorePatterns) {
103
+ if (!existsSync(root) || !statSync(root).isDirectory()) {
104
+ throw new Error(t("cli.shared.target-invalid", { target: root }));
105
+ }
106
+ let fileCount = 0;
107
+ let ignoredCount = 0;
108
+ const stack = [root];
109
+ while (stack.length > 0) {
110
+ const current = stack.pop();
111
+ if (current === void 0) {
112
+ continue;
113
+ }
114
+ for (const entry of readdirSync(current, { withFileTypes: true })) {
115
+ const absolutePath = join(current, entry.name);
116
+ const relativePath = toPosixPath(relative(root, absolutePath));
117
+ if (shouldIgnore(relativePath, entry.isDirectory(), ignorePatterns)) {
118
+ ignoredCount += 1;
119
+ continue;
120
+ }
121
+ if (entry.isDirectory()) {
122
+ stack.push(absolutePath);
123
+ } else if (entry.isFile()) {
124
+ fileCount += 1;
125
+ }
126
+ }
127
+ }
128
+ return { fileCount, ignoredCount };
129
+ }
130
+ function shouldIgnore(relativePath, isDirectory, ignorePatterns) {
131
+ return ignorePatterns.some((pattern) => matchesIgnorePattern(relativePath, isDirectory, pattern));
132
+ }
133
+ function matchesIgnorePattern(relativePath, isDirectory, pattern) {
134
+ const normalizedPattern = toPosixPath(pattern);
135
+ if (normalizedPattern === "**/*.meta") {
136
+ return relativePath.endsWith(".meta");
137
+ }
138
+ if (normalizedPattern.endsWith("/**")) {
139
+ const directoryPrefix = normalizedPattern.slice(0, -3);
140
+ return relativePath === directoryPrefix || relativePath.startsWith(`${directoryPrefix}/`) || isDirectory && `${relativePath}/` === directoryPrefix;
141
+ }
142
+ return relativePath === normalizedPattern;
143
+ }
144
+ function toPosixPath(path) {
145
+ return path.split(sep).join("/");
146
+ }
147
+ function buildRecommendations(input) {
148
+ const recommendations = [];
149
+ if (!input.hasExistingFabric) {
150
+ recommendations.push(t("cli.scan.recommendation.init"));
151
+ }
152
+ if (input.readmeQuality === "stub") {
153
+ recommendations.push(t("cli.scan.recommendation.readme"));
154
+ }
155
+ if (!input.hasContributing) {
156
+ recommendations.push(t("cli.scan.recommendation.contributing"));
157
+ }
158
+ if (input.framework.kind === "unknown") {
159
+ recommendations.push(t("cli.scan.recommendation.unknown-framework"));
160
+ } else {
161
+ recommendations.push(t("cli.scan.recommendation.framework-dirs", { framework: input.framework.kind }));
162
+ }
163
+ return recommendations;
164
+ }
165
+ function printPrettyReport(report, debug) {
166
+ console.log(paint.ai(t("cli.scan.report.title")));
167
+ const rows = [
168
+ [t("cli.scan.report.target"), paint.human(report.target)],
169
+ [t("cli.scan.report.framework"), paint.ai(report.framework.kind)],
170
+ [
171
+ t("cli.scan.report.readme-quality"),
172
+ report.readmeQuality === "ok" ? paint.success(t("cli.scan.readme-quality.ok")) : paint.warn(t("cli.scan.readme-quality.stub"))
173
+ ],
174
+ [
175
+ t("cli.scan.report.contributing"),
176
+ report.hasContributing ? paint.success(t("cli.shared.present")) : paint.warn(t("cli.shared.absent"))
177
+ ],
178
+ [t("cli.scan.report.files-counted"), String(report.fileCount)],
179
+ [t("cli.scan.report.ignored-entries"), report.ignoredCount > 0 ? paint.muted(String(report.ignoredCount)) : "0"],
180
+ [
181
+ t("cli.scan.report.existing-fabric"),
182
+ report.hasExistingFabric ? paint.warn(t("cli.shared.yes")) : paint.success(t("cli.shared.no"))
183
+ ]
184
+ ];
185
+ if (debug) {
186
+ rows.splice(2, 0, [
187
+ t("cli.scan.report.evidence"),
188
+ report.framework.evidence.length > 0 ? paint.muted(report.framework.evidence.join(", ")) : paint.muted(t("cli.shared.none"))
189
+ ]);
190
+ }
191
+ const labelWidth = Math.max(...rows.map(([key]) => displayWidth(key)));
192
+ for (const [key, value] of rows) {
193
+ console.log(`${paint.muted(padEnd(key, labelWidth))} ${value}`);
194
+ }
195
+ console.log(paint.muted(t("cli.scan.report.recommendations")));
196
+ for (const recommendation of report.recommendations) {
197
+ console.log(`${symbol.warn} ${paint.drift(recommendation)}`);
198
+ }
199
+ }
200
+
201
+ export {
202
+ detectFramework,
203
+ createScanReport,
204
+ scanCommand,
205
+ scan_default
206
+ };
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ t
4
+ } from "./chunk-6ICJICVU.js";
2
5
 
3
6
  // src/commands/ledger-append.ts
4
7
  import { execSync } from "child_process";
@@ -10,17 +13,17 @@ var INITIAL_PARENT_SHA = "root";
10
13
  var ledgerAppendCommand = defineCommand({
11
14
  meta: {
12
15
  name: "ledger-append",
13
- description: "\u5411 Fabric \u610F\u56FE\u65E5\u5FD7\u6DFB\u52A0\u4E00\u6761\u8BB0\u5F55\u3002"
16
+ description: t("cli.ledger-append.description")
14
17
  },
15
18
  args: {
16
19
  target: {
17
20
  type: "string",
18
- description: "\u76EE\u6807\u9879\u76EE\u8DEF\u5F84\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u3002",
21
+ description: t("cli.ledger-append.args.target.description"),
19
22
  default: process.cwd()
20
23
  },
21
24
  staged: {
22
25
  type: "boolean",
23
- description: "\u4ECE\u6682\u5B58\u53D8\u66F4\u4E2D\u63A8\u5BFC\u8BB0\u5F55\uFF08\u7528\u4E8E pre-commit \u9636\u6BB5\uFF09\u3002",
26
+ description: t("cli.ledger-append.args.staged.description"),
24
27
  default: false
25
28
  }
26
29
  },
@@ -28,7 +31,7 @@ var ledgerAppendCommand = defineCommand({
28
31
  const target = normalizeTarget(args.target);
29
32
  assertExistingDirectory(target);
30
33
  if (!args.staged) {
31
- writeStderr("requires --staged in pre-commit context");
34
+ writeStderr(t("cli.ledger-append.requires-staged"));
32
35
  process.exitCode = 1;
33
36
  return;
34
37
  }
@@ -40,6 +43,7 @@ var ledgerAppendCommand = defineCommand({
40
43
  const diffStat = readDiffStat(target).trim();
41
44
  const entry = {
42
45
  ts: Date.now(),
46
+ source: "human",
43
47
  parent_sha: readParentSha(target),
44
48
  intent,
45
49
  affected_paths: stagedFiles,
@@ -59,7 +63,7 @@ function normalizeTarget(targetInput) {
59
63
  }
60
64
  function assertExistingDirectory(target) {
61
65
  if (!existsSync(target) || !statSync(target).isDirectory()) {
62
- throw new Error(`Target must be an existing directory: ${target}`);
66
+ throw new Error(t("cli.shared.target-invalid", { target }));
63
67
  }
64
68
  }
65
69
  function getStagedFiles(target) {
@@ -83,8 +87,8 @@ function deriveIntent(stagedFiles) {
83
87
  }
84
88
  const uniqueNames = Array.from(new Set(stagedFiles.map((file) => basename(file))));
85
89
  const head = uniqueNames.slice(0, 2).join(", ");
86
- const suffix = uniqueNames.length > 2 ? ` +${uniqueNames.length - 2} more` : "";
87
- return `auto: ${head}${suffix}`;
90
+ const suffix = uniqueNames.length > 2 ? t("cli.ledger-append.intent.auto-more", { count: String(uniqueNames.length - 2) }) : "";
91
+ return t("cli.ledger-append.intent.auto", { head, suffix });
88
92
  }
89
93
  function hasMatchingTailEntry(target, entry) {
90
94
  const ledgerPath = join(target, LEDGER_FILE);
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ padEnd
4
+ } from "./chunk-WWNXR34K.js";
5
+ import {
6
+ t
7
+ } from "./chunk-6ICJICVU.js";
2
8
 
3
9
  // src/commands/human-lint.ts
4
10
  import { createHash } from "crypto";
@@ -6,15 +12,16 @@ import { existsSync } from "fs";
6
12
  import { readFile } from "fs/promises";
7
13
  import { isAbsolute, join, resolve } from "path";
8
14
  import { defineCommand } from "citty";
15
+ import { humanLockEntrySchema } from "@fenglimg/fabric-shared";
9
16
  var humanLintCommand = defineCommand({
10
17
  meta: {
11
18
  name: "human-lint",
12
- description: "\u9A8C\u8BC1\u9501\u5B9A\u7684\u4EBA\u5DE5\u7F16\u8F91\u533A\u5757\u3002"
19
+ description: t("cli.human-lint.description")
13
20
  },
14
21
  args: {
15
22
  target: {
16
23
  type: "string",
17
- description: "\u76EE\u6807\u9879\u76EE\u8DEF\u5F84\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u3002",
24
+ description: t("cli.human-lint.args.target.description"),
18
25
  default: process.cwd()
19
26
  }
20
27
  },
@@ -60,11 +67,13 @@ var humanLintCommand = defineCommand({
60
67
  if (violations.length === 0) {
61
68
  return;
62
69
  }
63
- writeStderr("Human-locked content drift detected. Revert the edit or update approved hashes before committing.");
64
- writeStderr("Location Expected Got");
70
+ writeStderr(t("cli.human-lint.drift-detected"));
71
+ writeStderr(
72
+ `${padEnd(t("cli.human-lint.table.location"), 32)} ${padEnd(t("cli.human-lint.table.expected"), 18)} ${t("cli.human-lint.table.got")}`
73
+ );
65
74
  for (const violation of violations) {
66
75
  writeStderr(
67
- `${violation.location.padEnd(32)} ${violation.expected.padEnd(18)} ${violation.actual}`
76
+ `${padEnd(violation.location, 32)} ${padEnd(violation.expected, 18)} ${violation.actual}`
68
77
  );
69
78
  }
70
79
  process.exitCode = 1;
@@ -81,7 +90,7 @@ function hashLockedContent(content, entry) {
81
90
  }
82
91
  function shortenHash(value) {
83
92
  if (value === "missing") {
84
- return value;
93
+ return t("cli.shared.missing");
85
94
  }
86
95
  return value.slice(0, 15);
87
96
  }
@@ -92,5 +101,6 @@ function writeStderr(message) {
92
101
 
93
102
  export {
94
103
  humanLintCommand,
95
- human_lint_default
104
+ human_lint_default,
105
+ humanLockEntrySchema
96
106
  };
File without changes
@@ -240,6 +240,7 @@ var CodexTOMLConfigWriter = class {
240
240
  };
241
241
 
242
242
  // src/config/resolver.ts
243
+ import { clientPathsSchema, fabricConfigSchema } from "@fenglimg/fabric-shared";
243
244
  function hasExplicitPath(clientPaths, key) {
244
245
  return typeof clientPaths?.[key] === "string" && clientPaths[key].trim().length > 0;
245
246
  }
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/colors.ts
4
+ import pc from "picocolors";
5
+ import stringWidth from "string-width";
6
+ function isColorEnabled() {
7
+ return !process.env.NO_COLOR && Boolean(process.stdout.isTTY) && Boolean(process.stderr.isTTY);
8
+ }
9
+ function colorize(painter) {
10
+ return (value) => isColorEnabled() ? painter(value) : value;
11
+ }
12
+ var paint = {
13
+ success: colorize(pc.green),
14
+ warn: colorize(pc.yellow),
15
+ error: colorize(pc.red),
16
+ drift: colorize(pc.magenta),
17
+ ai: colorize(pc.blue),
18
+ human: colorize(pc.cyan),
19
+ muted: colorize(pc.dim)
20
+ };
21
+ var symbol = {
22
+ get ok() {
23
+ return isColorEnabled() ? paint.success("\u2713") : "[ok]";
24
+ },
25
+ get warn() {
26
+ return isColorEnabled() ? paint.warn("!") : "[warn]";
27
+ },
28
+ get error() {
29
+ return isColorEnabled() ? paint.error("x") : "[error]";
30
+ }
31
+ };
32
+ function displayWidth(value) {
33
+ return stringWidth(value);
34
+ }
35
+ function padEnd(value, width, char = " ") {
36
+ const fill = char.length > 0 ? char : " ";
37
+ let result = value;
38
+ while (displayWidth(result) < width) {
39
+ result += fill;
40
+ }
41
+ return result;
42
+ }
43
+
44
+ export {
45
+ paint,
46
+ symbol,
47
+ displayWidth,
48
+ padEnd
49
+ };
@@ -1,7 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolveClients
4
- } from "./chunk-N7TTCGJA.js";
4
+ } from "./chunk-VMYPJPKV.js";
5
+ import {
6
+ t
7
+ } from "./chunk-6ICJICVU.js";
5
8
 
6
9
  // src/commands/config.ts
7
10
  import { existsSync } from "fs";
@@ -37,7 +40,7 @@ function parseClientFilter(value) {
37
40
  const alias = rawClient.trim().toLowerCase();
38
41
  const clientKind = CLIENT_ALIASES[alias];
39
42
  if (clientKind === void 0) {
40
- throw new Error(`Unknown client "${rawClient}". Use a comma-separated list such as cursor,codex,gemini.`);
43
+ throw new Error(t("cli.config.errors.unknown-client", { client: rawClient }));
41
44
  }
42
45
  clients.add(clientKind);
43
46
  }
@@ -50,7 +53,7 @@ async function loadFabricConfig(workspaceRoot) {
50
53
  }
51
54
  const parsed = JSON.parse(await readFile(configPath, "utf8"));
52
55
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
53
- throw new Error(`Expected object in ${configPath}`);
56
+ throw new Error(t("cli.config.errors.expected-object", { path: configPath }));
54
57
  }
55
58
  return parsed;
56
59
  }
@@ -65,22 +68,22 @@ function writeStderr(message) {
65
68
  var configCmd = defineCommand({
66
69
  meta: {
67
70
  name: "config",
68
- description: "\u7BA1\u7406 Fabric MCP \u5BA2\u6237\u7AEF\u914D\u7F6E\u3002"
71
+ description: t("cli.config.description")
69
72
  },
70
73
  subCommands: {
71
74
  install: defineCommand({
72
75
  meta: {
73
76
  name: "install",
74
- description: "\u5C06 Fabric MCP \u670D\u52A1\u5668\u6761\u76EE\u5B89\u88C5\u5230\u68C0\u6D4B\u5230\u7684\u5BA2\u6237\u7AEF\u914D\u7F6E\u4E2D\u3002"
77
+ description: t("cli.config.install.description")
75
78
  },
76
79
  args: {
77
80
  clients: {
78
81
  type: "string",
79
- description: "\u53EF\u9009\u7684\u9017\u53F7\u5206\u9694\u5BA2\u6237\u7AEF\u8FC7\u6EE4\u5668\uFF0C\u4F8B\u5982 cursor,codex,gemini\u3002"
82
+ description: t("cli.config.install.args.clients.description")
80
83
  },
81
84
  "dry-run": {
82
85
  type: "boolean",
83
- description: "\u9884\u89C8\u68C0\u6D4B\u5230\u7684\u5199\u5165\u64CD\u4F5C\uFF0C\u4E0D\u4FEE\u6539\u6587\u4EF6\u3002",
86
+ description: t("cli.config.install.args.dry-run.description"),
84
87
  default: false
85
88
  }
86
89
  },
@@ -93,21 +96,21 @@ var configCmd = defineCommand({
93
96
  (writer) => selectedClients === null ? true : selectedClients.has(writer.clientKind)
94
97
  );
95
98
  if (writers.length === 0) {
96
- writeStderr("\u672A\u68C0\u6D4B\u5230 Fabric MCP \u5BA2\u6237\u7AEF\u914D\u7F6E\u3002\u8BF7\u521B\u5EFA\u5BA2\u6237\u7AEF\u76EE\u5F55\u6216\u5728 fabric.config.json \u4E2D\u8BBE\u7F6E clientPaths\u3002");
99
+ writeStderr(t("cli.config.install.no-configs"));
97
100
  return;
98
101
  }
99
102
  for (const writer of writers) {
100
103
  const configPath = await writer.detect(workspaceRoot);
101
104
  if (configPath === null) {
102
- writeStderr(`Skipping ${writer.clientKind}: no config path detected.`);
105
+ writeStderr(t("cli.config.install.no-config-path", { client: writer.clientKind }));
103
106
  continue;
104
107
  }
105
108
  if (args["dry-run"]) {
106
- writeStderr(`[dry-run] ${writer.clientKind}: would write ${configPath}`);
109
+ writeStderr(t("cli.config.install.dry-run", { client: writer.clientKind, path: configPath }));
107
110
  continue;
108
111
  }
109
112
  await writer.write(serverPath, workspaceRoot);
110
- writeStderr(`${writer.clientKind}: wrote ${configPath}`);
113
+ writeStderr(t("cli.config.install.wrote", { client: writer.clientKind, path: configPath }));
111
114
  }
112
115
  }
113
116
  })