@fenglimg/fabric-cli 1.5.0 → 1.6.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 +13 -13
- package/dist/{chunk-N7EZORJZ.js → chunk-2YW5CJ32.js} +17 -8
- package/dist/{chunk-Q4LOVXML.js → chunk-KOAEIH72.js} +40 -5
- package/dist/{doctor-QTSG2RWF.js → doctor-4BPYHV7V.js} +11 -2
- package/dist/index.js +6 -6
- package/dist/{init-YR7EVBYQ.js → init-LBVOI2QI.js} +68 -1
- package/dist/{ledger-append-DULKJ6Q2.js → ledger-append-3MDNR3GU.js} +1 -1
- package/dist/{pre-commit-IK6SJOPT.js → pre-commit-53ENJDRZ.js} +4 -3
- package/dist/{sync-meta-LKVSO6TS.js → sync-meta-IZR2WLIL.js} +1 -1
- package/package.json +3 -3
- package/templates/codex-hooks/fabric-session-start.cjs +1 -1
- package/templates/codex-hooks/fabric-stop-reminder.cjs +1 -1
- package/templates/codex-skills/fabric-init/SKILL.md +15 -15
package/README.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# @fenglimg/fabric-cli
|
|
2
2
|
|
|
3
|
-
`fabric`
|
|
3
|
+
`fabric` 是 Fabric 的主命令,`fab` 是永久别名,两者等价。
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
1.
|
|
8
|
-
2.
|
|
9
|
-
3.
|
|
10
|
-
4.
|
|
11
|
-
|
|
12
|
-
`fabric init`
|
|
5
|
+
## 快速开始
|
|
6
|
+
|
|
7
|
+
1. 在 monorepo 根目录运行 `pnpm install`。
|
|
8
|
+
2. 用 `pnpm --filter @fenglimg/fabric-cli build` 构建 CLI。
|
|
9
|
+
3. 在目标项目运行 `fabric init`,完成一站式初始化。
|
|
10
|
+
4. 启动 `fabric serve`,再去客户端里验证 `fab_get_rules`。
|
|
11
|
+
|
|
12
|
+
`fabric init` 会自动执行 `bootstrap install`、`config install` 和 `hooks install`。只有在需要单独重跑某个阶段时,才需要单独调用它们。
|
|
13
13
|
|
|
14
|
-
`fabric bootstrap install`
|
|
14
|
+
`fabric bootstrap install` 只会刷新 `.fabric/bootstrap/README.md` 里的内部初始化说明,不会再生成根级 `AGENTS.md`、`CLAUDE.md` 或 `GEMINI.md`。
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## 常用命令
|
|
17
17
|
|
|
18
18
|
- `fabric init`
|
|
19
19
|
- `fabric serve`
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
- `fabric approve --interactive`
|
|
22
22
|
- `fabric approve --all`
|
|
23
23
|
|
|
24
|
-
##
|
|
24
|
+
## 进阶命令
|
|
25
25
|
|
|
26
26
|
- `fabric bootstrap install`
|
|
27
27
|
- `fabric config install`
|
|
28
28
|
- `fabric hooks install`
|
|
29
29
|
|
|
30
|
-
`fabric approve`
|
|
30
|
+
`fabric approve` 会在审查完成后更新 `.fabric/human-lock.json` 中已经发生漂移的条目。需要逐项确认时使用 `--interactive`,只有在别处已经完成审查时才使用 `--all`。
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
|
|
6
6
|
// src/commands/ledger-append.ts
|
|
7
7
|
import { execSync } from "child_process";
|
|
8
|
-
import { appendFileSync, existsSync, readFileSync, statSync } from "fs";
|
|
9
|
-
import { basename, isAbsolute,
|
|
8
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync } from "fs";
|
|
9
|
+
import { basename, isAbsolute, resolve } from "path";
|
|
10
|
+
import { getLedgerPath, getLegacyLedgerPath, LEGACY_LEDGER_PATH, LEDGER_PATH } from "@fenglimg/fabric-server";
|
|
10
11
|
import { defineCommand } from "citty";
|
|
11
|
-
var LEDGER_FILE = ".intent-ledger.jsonl";
|
|
12
12
|
var INITIAL_PARENT_SHA = "root";
|
|
13
13
|
var ledgerAppendCommand = defineCommand({
|
|
14
14
|
meta: {
|
|
@@ -35,7 +35,7 @@ var ledgerAppendCommand = defineCommand({
|
|
|
35
35
|
process.exitCode = 1;
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
|
-
const stagedFiles = getStagedFiles(target).filter((file) => file !==
|
|
38
|
+
const stagedFiles = getStagedFiles(target).filter((file) => file !== LEGACY_LEDGER_PATH && file !== LEDGER_PATH);
|
|
39
39
|
if (stagedFiles.length === 0) {
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
@@ -52,9 +52,11 @@ var ledgerAppendCommand = defineCommand({
|
|
|
52
52
|
if (hasMatchingTailEntry(target, entry)) {
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
const ledgerPath = getLedgerPath(target);
|
|
56
|
+
mkdirSync(resolve(target, ".fabric"), { recursive: true });
|
|
57
|
+
appendFileSync(ledgerPath, `${JSON.stringify(entry)}
|
|
56
58
|
`, "utf8");
|
|
57
|
-
execGit(target, `git add ${
|
|
59
|
+
execGit(target, `git add ${LEDGER_PATH}`);
|
|
58
60
|
}
|
|
59
61
|
});
|
|
60
62
|
var ledger_append_default = ledgerAppendCommand;
|
|
@@ -91,7 +93,7 @@ function deriveIntent(stagedFiles) {
|
|
|
91
93
|
return t("cli.ledger-append.intent.auto", { head, suffix });
|
|
92
94
|
}
|
|
93
95
|
function hasMatchingTailEntry(target, entry) {
|
|
94
|
-
const ledgerPath =
|
|
96
|
+
const ledgerPath = resolveTailLedgerPath(target);
|
|
95
97
|
if (!existsSync(ledgerPath)) {
|
|
96
98
|
return false;
|
|
97
99
|
}
|
|
@@ -106,6 +108,13 @@ function hasMatchingTailEntry(target, entry) {
|
|
|
106
108
|
return false;
|
|
107
109
|
}
|
|
108
110
|
}
|
|
111
|
+
function resolveTailLedgerPath(target) {
|
|
112
|
+
const canonicalPath = getLedgerPath(target);
|
|
113
|
+
if (existsSync(canonicalPath)) {
|
|
114
|
+
return canonicalPath;
|
|
115
|
+
}
|
|
116
|
+
return getLegacyLedgerPath(target);
|
|
117
|
+
}
|
|
109
118
|
function isLedgerEntryLine(line) {
|
|
110
119
|
try {
|
|
111
120
|
const parsed = JSON.parse(line);
|
|
@@ -118,7 +127,7 @@ function normalizeDiffStat(diffStat) {
|
|
|
118
127
|
if (typeof diffStat !== "string") {
|
|
119
128
|
return "";
|
|
120
129
|
}
|
|
121
|
-
return diffStat.split(/\r?\n/).map((line) => line.trim()).map((line) => line.replace(/\s+\|\s+/g, " | ")).map((line) => line.replace(/\s+/g, " ")).filter((line) => line.length > 0).filter((line) => !line.includes(
|
|
130
|
+
return diffStat.split(/\r?\n/).map((line) => line.trim()).map((line) => line.replace(/\s+\|\s+/g, " | ")).map((line) => line.replace(/\s+/g, " ")).filter((line) => line.length > 0).filter((line) => !line.includes(LEGACY_LEDGER_PATH)).filter((line) => !line.includes(LEDGER_PATH)).filter((line) => !/\d+ files? changed(?:, \d+ insertions?\(\+\))?(?:, \d+ deletions?\(-\))?$/.test(line.trim())).join("\n");
|
|
122
131
|
}
|
|
123
132
|
function execGit(target, command) {
|
|
124
133
|
return execSync(command, {
|
|
@@ -10,6 +10,7 @@ import { isAbsolute, join, relative, resolve, sep } from "path";
|
|
|
10
10
|
import {
|
|
11
11
|
agentsMetaSchema,
|
|
12
12
|
deriveAgentsMetaLayer,
|
|
13
|
+
deriveAgentsMetaStableId,
|
|
13
14
|
deriveAgentsMetaTopologyType
|
|
14
15
|
} from "@fenglimg/fabric-shared";
|
|
15
16
|
import { defineCommand } from "citty";
|
|
@@ -68,14 +69,18 @@ function computeAgentsMeta(target) {
|
|
|
68
69
|
}
|
|
69
70
|
for (const file of agentsFiles) {
|
|
70
71
|
const existing = existingByFile.get(file);
|
|
72
|
+
const source = readFileSync(join(target, file), "utf8");
|
|
71
73
|
const id = deriveNodeId(file);
|
|
72
|
-
const hash = sha256(
|
|
74
|
+
const hash = sha256(source);
|
|
73
75
|
const defaults = createDefaultNodeMeta(file);
|
|
76
|
+
const identity = deriveRuleIdentity(file, source, existing?.node);
|
|
74
77
|
nodes[id] = {
|
|
75
78
|
...defaults,
|
|
76
79
|
...existing?.node,
|
|
77
80
|
file,
|
|
78
|
-
hash
|
|
81
|
+
hash,
|
|
82
|
+
stable_id: identity.stableId,
|
|
83
|
+
identity_source: identity.identitySource
|
|
79
84
|
};
|
|
80
85
|
}
|
|
81
86
|
return {
|
|
@@ -168,11 +173,17 @@ function createBootstrapNode(target, existing) {
|
|
|
168
173
|
return void 0;
|
|
169
174
|
}
|
|
170
175
|
const hash = sha256(readFileSync(sourcePath, "utf8"));
|
|
176
|
+
const identity = {
|
|
177
|
+
stableId: existing?.stable_id ?? deriveAgentsMetaStableId(".fabric/bootstrap/README.md"),
|
|
178
|
+
identitySource: existing?.identity_source ?? "derived"
|
|
179
|
+
};
|
|
171
180
|
return {
|
|
172
181
|
...createDefaultNodeMeta(".fabric/bootstrap/README.md"),
|
|
173
182
|
...existing,
|
|
174
183
|
file: ".fabric/bootstrap/README.md",
|
|
175
|
-
hash
|
|
184
|
+
hash,
|
|
185
|
+
stable_id: identity.stableId,
|
|
186
|
+
identity_source: identity.identitySource
|
|
176
187
|
};
|
|
177
188
|
}
|
|
178
189
|
function deriveScopeGlob(file) {
|
|
@@ -200,8 +211,8 @@ function sortNodes(nodes) {
|
|
|
200
211
|
return Object.fromEntries(Object.entries(nodes).sort(([left], [right]) => left.localeCompare(right)));
|
|
201
212
|
}
|
|
202
213
|
function computeRevision(nodes) {
|
|
203
|
-
const
|
|
204
|
-
return sha256(
|
|
214
|
+
const revisionSource = Object.entries(sortNodes(nodes)).map(([id, node]) => [id, node.hash, node.stable_id ?? "", node.identity_source ?? ""].join("|")).join("\n");
|
|
215
|
+
return sha256(revisionSource);
|
|
205
216
|
}
|
|
206
217
|
function writeStderr(message) {
|
|
207
218
|
process.stderr.write(`${message}
|
|
@@ -225,6 +236,30 @@ function toPosixPath(path) {
|
|
|
225
236
|
function sha256(content) {
|
|
226
237
|
return `sha256:${createHash("sha256").update(content).digest("hex")}`;
|
|
227
238
|
}
|
|
239
|
+
function deriveRuleIdentity(file, source, existing) {
|
|
240
|
+
const declaredStableId = extractDeclaredStableId(source);
|
|
241
|
+
const derivedStableId = deriveAgentsMetaStableId(file);
|
|
242
|
+
if (declaredStableId !== void 0) {
|
|
243
|
+
return {
|
|
244
|
+
stableId: declaredStableId,
|
|
245
|
+
identitySource: "declared"
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (existing?.identity_source === "declared" && existing.stable_id !== void 0 && existing.stable_id !== derivedStableId) {
|
|
249
|
+
return {
|
|
250
|
+
stableId: existing.stable_id,
|
|
251
|
+
identitySource: "declared"
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
stableId: derivedStableId,
|
|
256
|
+
identitySource: "derived"
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function extractDeclaredStableId(source) {
|
|
260
|
+
const match = /^(?:\uFEFF)?<!--\s*fab:rule-id\s+([A-Za-z0-9][A-Za-z0-9/_-]*)\s*-->\s*(?:\r?\n|$)/u.exec(source);
|
|
261
|
+
return match?.[1];
|
|
262
|
+
}
|
|
228
263
|
|
|
229
264
|
export {
|
|
230
265
|
syncMetaCommand,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
|
|
14
14
|
// src/commands/doctor.ts
|
|
15
15
|
import { defineCommand } from "citty";
|
|
16
|
-
import { runDoctorAuditReport, runDoctorReport } from "@fenglimg/fabric-server";
|
|
16
|
+
import { runDoctorAuditReport, runDoctorFix, runDoctorReport } from "@fenglimg/fabric-server";
|
|
17
17
|
var DEFAULT_AUDIT_WINDOW_MINUTES = 5;
|
|
18
18
|
var doctorCommand = defineCommand({
|
|
19
19
|
meta: {
|
|
@@ -30,6 +30,11 @@ var doctorCommand = defineCommand({
|
|
|
30
30
|
description: t("cli.doctor.args.audit.description"),
|
|
31
31
|
default: false
|
|
32
32
|
},
|
|
33
|
+
fix: {
|
|
34
|
+
type: "boolean",
|
|
35
|
+
description: t("cli.doctor.args.fix.description"),
|
|
36
|
+
default: false
|
|
37
|
+
},
|
|
33
38
|
"window-minutes": {
|
|
34
39
|
type: "string",
|
|
35
40
|
description: t("cli.doctor.args.window-minutes.description"),
|
|
@@ -39,7 +44,11 @@ var doctorCommand = defineCommand({
|
|
|
39
44
|
async run({ args }) {
|
|
40
45
|
const workspaceRoot = process.cwd();
|
|
41
46
|
const resolution = resolveDevMode(args.target, workspaceRoot);
|
|
42
|
-
const
|
|
47
|
+
const fixReport = args.fix ? await runDoctorFix(resolution.target) : null;
|
|
48
|
+
const report = fixReport?.report ?? await runDoctorReport(resolution.target);
|
|
49
|
+
if (fixReport !== null) {
|
|
50
|
+
writeStdout(fixReport.message);
|
|
51
|
+
}
|
|
43
52
|
writeStdout(`${renderStatus(report.status)} ${paint.ai("fab doctor")} ${paint.human(resolution.target)}`);
|
|
44
53
|
for (const check of report.checks) {
|
|
45
54
|
writeStdout(`${renderStatus(check.status)} ${check.name}: ${check.message}`);
|
package/dist/index.js
CHANGED
|
@@ -9,15 +9,15 @@ import { defineCommand, runMain } from "citty";
|
|
|
9
9
|
// src/commands/index.ts
|
|
10
10
|
var allCommands = {
|
|
11
11
|
approve: () => import("./approve-YT4DEABS.js").then((module) => module.default),
|
|
12
|
-
init: () => import("./init-
|
|
12
|
+
init: () => import("./init-LBVOI2QI.js").then((module) => module.default),
|
|
13
13
|
update: () => import("./update-M5M5PYKE.js").then((module) => module.default),
|
|
14
14
|
scan: () => import("./scan-QH76LC7Z.js").then((module) => module.default),
|
|
15
15
|
serve: () => import("./serve-4J2CQY25.js").then((module) => module.default),
|
|
16
|
-
doctor: () => import("./doctor-
|
|
17
|
-
"sync-meta": () => import("./sync-meta-
|
|
16
|
+
doctor: () => import("./doctor-4BPYHV7V.js").then((module) => module.default),
|
|
17
|
+
"sync-meta": () => import("./sync-meta-IZR2WLIL.js").then((module) => module.default),
|
|
18
18
|
"human-lint": () => import("./human-lint-YSFOZHZ7.js").then((module) => module.default),
|
|
19
|
-
"ledger-append": () => import("./ledger-append-
|
|
20
|
-
"pre-commit": () => import("./pre-commit-
|
|
19
|
+
"ledger-append": () => import("./ledger-append-3MDNR3GU.js").then((module) => module.default),
|
|
20
|
+
"pre-commit": () => import("./pre-commit-53ENJDRZ.js").then((module) => module.default),
|
|
21
21
|
bootstrap: () => import("./bootstrap-VGL3AR26.js").then((module) => module.default),
|
|
22
22
|
config: () => import("./config-EC5L2QNI.js").then((module) => module.configCmd),
|
|
23
23
|
hooks: () => import("./hooks-ZSWVH2JD.js").then((module) => ({
|
|
@@ -33,7 +33,7 @@ var allCommands = {
|
|
|
33
33
|
var main = defineCommand({
|
|
34
34
|
meta: {
|
|
35
35
|
name: "fabric",
|
|
36
|
-
version: "1.
|
|
36
|
+
version: "1.6.0",
|
|
37
37
|
description: 'Initialize and manage Fabric projects. Use "fabric init" for one-shot setup.'
|
|
38
38
|
},
|
|
39
39
|
subCommands: allCommands
|
|
@@ -1147,7 +1147,7 @@ function readProjectName(target) {
|
|
|
1147
1147
|
return basename(target);
|
|
1148
1148
|
}
|
|
1149
1149
|
function getCliVersion() {
|
|
1150
|
-
return true ? "1.
|
|
1150
|
+
return true ? "1.6.0" : "unknown";
|
|
1151
1151
|
}
|
|
1152
1152
|
function sortRecord(record) {
|
|
1153
1153
|
return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
|
|
@@ -1375,6 +1375,7 @@ async function buildInitFabricPlan(target, options) {
|
|
|
1375
1375
|
const fabricDir = join2(target, ".fabric");
|
|
1376
1376
|
const bootstrapPath = join2(fabricDir, "bootstrap", "README.md");
|
|
1377
1377
|
const forensicPath = join2(fabricDir, "forensic.json");
|
|
1378
|
+
const taxonomyPath = join2(fabricDir, "INITIAL_TAXONOMY.md");
|
|
1378
1379
|
const claudeSkillPath = join2(target, ".claude", "skills", "agents-md-init", "SKILL.md");
|
|
1379
1380
|
const codexSkillPath = join2(target, ".agents", "skills", "fabric-init", "SKILL.md");
|
|
1380
1381
|
const codexSessionStartHookPath = join2(target, ".codex", "hooks", "fabric-session-start.cjs");
|
|
@@ -1387,11 +1388,13 @@ async function buildInitFabricPlan(target, options) {
|
|
|
1387
1388
|
const replaceFabricDir = shouldReplaceWritableDirectory(fabricDir, options);
|
|
1388
1389
|
const bootstrapAction = planFreshPath(bootstrapPath, options);
|
|
1389
1390
|
const metaAction = planFreshPath(metaPath, options);
|
|
1391
|
+
const taxonomyAction = planFreshPath(taxonomyPath, options);
|
|
1390
1392
|
const humanLockAction = planFreshPath(humanLockPath, options);
|
|
1391
1393
|
const forensicAction = planFreshPath(forensicPath, options);
|
|
1392
1394
|
const forensicReport = await buildForensicReport(target);
|
|
1393
1395
|
const humanLockTemplate = readFileSync2(findTemplatePath("templates/fabric/human-lock.json"), "utf8");
|
|
1394
1396
|
const bootstrapContent = await buildFabricBootstrapGuide(target);
|
|
1397
|
+
const taxonomyContent = buildInitialTaxonomyMarkdown(forensicReport);
|
|
1395
1398
|
const bootstrapHash = sha256(bootstrapContent);
|
|
1396
1399
|
const meta = createInitialMeta(bootstrapHash);
|
|
1397
1400
|
return {
|
|
@@ -1405,6 +1408,9 @@ async function buildInitFabricPlan(target, options) {
|
|
|
1405
1408
|
metaPath,
|
|
1406
1409
|
metaAction,
|
|
1407
1410
|
meta,
|
|
1411
|
+
taxonomyPath,
|
|
1412
|
+
taxonomyAction,
|
|
1413
|
+
taxonomyContent,
|
|
1408
1414
|
humanLockPath,
|
|
1409
1415
|
humanLockAction,
|
|
1410
1416
|
humanLockContent: humanLockTemplate.endsWith("\n") ? humanLockTemplate : `${humanLockTemplate}
|
|
@@ -1447,6 +1453,8 @@ function executeInitFabricPlan(plan) {
|
|
|
1447
1453
|
preparePlannedPath(plan.metaPath, plan.metaAction);
|
|
1448
1454
|
writeFileSync(plan.metaPath, `${JSON.stringify(plan.meta, null, 2)}
|
|
1449
1455
|
`, "utf8");
|
|
1456
|
+
preparePlannedPath(plan.taxonomyPath, plan.taxonomyAction);
|
|
1457
|
+
writeFileSync(plan.taxonomyPath, ensureTrailingNewline(plan.taxonomyContent), "utf8");
|
|
1450
1458
|
preparePlannedPath(plan.humanLockPath, plan.humanLockAction);
|
|
1451
1459
|
writeFileSync(plan.humanLockPath, plan.humanLockContent, "utf8");
|
|
1452
1460
|
preparePlannedPath(plan.forensicPath, plan.forensicAction);
|
|
@@ -1464,6 +1472,8 @@ function executeInitFabricPlan(plan) {
|
|
|
1464
1472
|
bootstrapAction: plan.bootstrapAction,
|
|
1465
1473
|
metaPath: plan.metaPath,
|
|
1466
1474
|
metaAction: plan.metaAction,
|
|
1475
|
+
taxonomyPath: plan.taxonomyPath,
|
|
1476
|
+
taxonomyAction: plan.taxonomyAction,
|
|
1467
1477
|
humanLockPath: plan.humanLockPath,
|
|
1468
1478
|
humanLockAction: plan.humanLockAction,
|
|
1469
1479
|
forensicPath: plan.forensicPath,
|
|
@@ -1526,6 +1536,7 @@ function exhaustiveInitStagePlan(value) {
|
|
|
1526
1536
|
function printInitScaffoldResult(created) {
|
|
1527
1537
|
console.log(formatInitPathAction(created.bootstrapPath, created.bootstrapAction));
|
|
1528
1538
|
console.log(formatInitPathAction(created.metaPath, created.metaAction));
|
|
1539
|
+
console.log(formatInitPathAction(created.taxonomyPath, created.taxonomyAction));
|
|
1529
1540
|
console.log(formatInitPathAction(created.humanLockPath, created.humanLockAction));
|
|
1530
1541
|
console.log(formatInitPathAction(created.forensicPath, created.forensicAction));
|
|
1531
1542
|
writeStderr(formatOptionalInitPathAction(created.claudeSkillPath, created.claudeSkillAction));
|
|
@@ -1576,6 +1587,8 @@ function buildPlanOnlyScaffoldResult(plan) {
|
|
|
1576
1587
|
bootstrapAction: plan.bootstrapAction,
|
|
1577
1588
|
metaPath: plan.metaPath,
|
|
1578
1589
|
metaAction: plan.metaAction,
|
|
1590
|
+
taxonomyPath: plan.taxonomyPath,
|
|
1591
|
+
taxonomyAction: plan.taxonomyAction,
|
|
1579
1592
|
humanLockPath: plan.humanLockPath,
|
|
1580
1593
|
humanLockAction: plan.humanLockAction,
|
|
1581
1594
|
forensicPath: plan.forensicPath,
|
|
@@ -2003,6 +2016,60 @@ function createInitialMeta(agentsHash) {
|
|
|
2003
2016
|
}
|
|
2004
2017
|
};
|
|
2005
2018
|
}
|
|
2019
|
+
function buildInitialTaxonomyMarkdown(forensicReport) {
|
|
2020
|
+
const frameworkInfo = forensicReport.framework;
|
|
2021
|
+
const framework = [frameworkInfo?.kind ?? "unknown", frameworkInfo?.subkind ?? ""].filter((value) => value.trim() !== "").join(" / ") || "unknown";
|
|
2022
|
+
const keyDirs = forensicReport.topology?.key_dirs?.slice(0, 8) ?? [];
|
|
2023
|
+
const candidateFiles = forensicReport.candidate_files?.slice(0, 8) ?? [];
|
|
2024
|
+
const generatedAt = forensicReport.generated_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2025
|
+
return `# Fabric Initial Taxonomy
|
|
2026
|
+
|
|
2027
|
+
**Date**: ${generatedAt}
|
|
2028
|
+
**Base Architecture**: L0/L1/L2 Tiered System
|
|
2029
|
+
**Detected Framework**: ${framework}
|
|
2030
|
+
|
|
2031
|
+
## Origin Logic
|
|
2032
|
+
|
|
2033
|
+
- **L0 \u5224\u5B9A**: \u5168\u5C40\u534F\u4F5C\u7A33\u5B9A\u6027\u89C4\u5219\u3002\u5178\u578B\u6765\u6E90\u5305\u62EC\u4ED3\u5E93\u6839\u914D\u7F6E\u3001package metadata\u3001Fabric \u5185\u90E8\u534F\u8BAE\u548C\u4E0D\u53EF\u968F\u5C40\u90E8\u4E1A\u52A1\u6F02\u79FB\u7684\u7EA6\u675F\u3002
|
|
2034
|
+
- **L1 \u5224\u5B9A**: \u9886\u57DF/\u6A21\u5757\u7EA7\u89C4\u5219\u3002\u4F9D\u636E\u6280\u672F\u6808\u3001\u76EE\u5F55\u804C\u8D23\u3001\u6846\u67B6\u7279\u5F81\u548C\u529F\u80FD\u6A21\u5757\u5212\u5206\uFF0C\u800C\u4E0D\u662F\u8DEF\u5F84\u6DF1\u5EA6\u3002
|
|
2035
|
+
- **L2 \u5224\u5B9A**: \u5177\u4F53\u811A\u672C\u3001\u8D44\u6E90\u6216\u5C40\u90E8\u4E1A\u52A1\u72B6\u6001\u89C4\u5219\u3002\u7528\u4E8E\u627F\u8F7D\u7279\u5B9A\u6587\u4EF6\u3001\u8D44\u6E90\u3001\u5386\u53F2\u8865\u4E01\u548C\u5C40\u90E8\u5904\u7406\u7EC6\u5219\u3002
|
|
2036
|
+
|
|
2037
|
+
## Initial L1 Buckets
|
|
2038
|
+
|
|
2039
|
+
${formatInitialL1Buckets(keyDirs)}
|
|
2040
|
+
|
|
2041
|
+
## L2 Candidate Signals
|
|
2042
|
+
|
|
2043
|
+
${formatInitialL2Signals(candidateFiles)}
|
|
2044
|
+
|
|
2045
|
+
## Evolution Guide
|
|
2046
|
+
|
|
2047
|
+
- \u6D89\u53CA\u5168\u4ED3\u534F\u4F5C\u7A33\u5B9A\u6027\u7684\u89C4\u5219\u8FDB\u5165 L0\u3002
|
|
2048
|
+
- \u6D89\u53CA\u6280\u672F\u9886\u57DF\u3001\u6846\u67B6\u6A21\u5757\u6216\u529F\u80FD\u6A21\u5757\u7684\u89C4\u5219\u8FDB\u5165 L1\u3002
|
|
2049
|
+
- \u6D89\u53CA\u5177\u4F53\u6587\u4EF6\u3001\u5177\u4F53\u8D44\u6E90\u6216\u5C40\u90E8\u4E1A\u52A1\u72B6\u6001\u7684\u89C4\u5219\u8FDB\u5165 L2\u3002
|
|
2050
|
+
- \u51B2\u7A81\u65F6\u6267\u884C\u89E3\u91CA\u56FA\u5B9A\u4E3A L2 > L1 > L0\uFF1B\u540C\u5C42\u5185\u624D\u4F7F\u7528 priority \u6392\u5E8F\u3002
|
|
2051
|
+
`;
|
|
2052
|
+
}
|
|
2053
|
+
function formatInitialL1Buckets(keyDirs) {
|
|
2054
|
+
if (keyDirs.length === 0) {
|
|
2055
|
+
return "- **L1-General**: \u521D\u59CB\u5316\u65F6\u672A\u68C0\u6D4B\u5230\u7A33\u5B9A\u76EE\u5F55\u8F74\u7EBF\uFF0C\u540E\u7EED\u4F9D\u636E\u6280\u672F\u6808\u548C\u6A21\u5757\u804C\u8D23\u6F14\u8FDB\u3002";
|
|
2056
|
+
}
|
|
2057
|
+
return keyDirs.map((dir) => `- **L1-${sanitizeTaxonomyLabel(dir)}**: \u6302\u8F7D\u4F9D\u636E\u2014\u2014forensic topology detected \`${dir}\`.`).join("\n");
|
|
2058
|
+
}
|
|
2059
|
+
function formatInitialL2Signals(candidateFiles) {
|
|
2060
|
+
if (candidateFiles.length === 0) {
|
|
2061
|
+
return "- \u6682\u672A\u8BC6\u522B\u660E\u786E L2 \u5019\u9009\u6587\u4EF6\u3002";
|
|
2062
|
+
}
|
|
2063
|
+
return candidateFiles.map((entry) => `- \`${entry.path}\`: ${entry.family} \u2014 ${entry.rationale}`).join("\n");
|
|
2064
|
+
}
|
|
2065
|
+
function sanitizeTaxonomyLabel(value) {
|
|
2066
|
+
const sanitized = value.replaceAll("\\", "/").split("/").filter(Boolean).join("-").replace(/[^A-Za-z0-9_-]+/gu, "-").replace(/^-+|-+$/gu, "");
|
|
2067
|
+
return sanitized === "" ? "General" : sanitized;
|
|
2068
|
+
}
|
|
2069
|
+
function ensureTrailingNewline(value) {
|
|
2070
|
+
return value.endsWith("\n") ? value : `${value}
|
|
2071
|
+
`;
|
|
2072
|
+
}
|
|
2006
2073
|
function findTemplatePath(relativePath) {
|
|
2007
2074
|
const currentModuleDir = dirname(fileURLToPath(import.meta.url));
|
|
2008
2075
|
const candidates = [
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
sync_meta_default
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KOAEIH72.js";
|
|
5
5
|
import {
|
|
6
6
|
human_lint_default
|
|
7
7
|
} from "./chunk-L43IGJ6X.js";
|
|
8
8
|
import {
|
|
9
9
|
ledger_append_default
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-2YW5CJ32.js";
|
|
11
11
|
import {
|
|
12
12
|
resolveDevModeTarget
|
|
13
13
|
} from "./chunk-AEOYCVBG.js";
|
|
@@ -24,6 +24,7 @@ import process from "process";
|
|
|
24
24
|
import { agentsMetaSchema } from "@fenglimg/fabric-shared";
|
|
25
25
|
import { defineCommand } from "citty";
|
|
26
26
|
import { minimatch } from "minimatch";
|
|
27
|
+
import { LEGACY_LEDGER_PATH, LEDGER_PATH } from "@fenglimg/fabric-server";
|
|
27
28
|
async function runOrFail(name, cmd, args) {
|
|
28
29
|
try {
|
|
29
30
|
await cmd.run?.({ args });
|
|
@@ -59,7 +60,7 @@ function tryReadAgentsMeta(target) {
|
|
|
59
60
|
function matchesFabricScope(stagedFiles, meta) {
|
|
60
61
|
const scopeGlobs = Object.values(meta.nodes).filter((node) => node.file !== ".fabric/bootstrap/README.md" && node.file !== "AGENTS.md").map((node) => node.scope_glob);
|
|
61
62
|
return stagedFiles.some(
|
|
62
|
-
(file) => file === ".fabric/bootstrap/README.md" || file === "AGENTS.md" || file === ".fabric/agents.meta.json" || file === ".fabric/human-lock.json" || file ===
|
|
63
|
+
(file) => file === ".fabric/bootstrap/README.md" || file === "AGENTS.md" || file === ".fabric/agents.meta.json" || file === ".fabric/human-lock.json" || file === LEDGER_PATH || file === LEGACY_LEDGER_PATH || scopeGlobs.some((pattern) => minimatch(file, pattern, { dot: true }))
|
|
63
64
|
);
|
|
64
65
|
}
|
|
65
66
|
var pre_commit_default = defineCommand({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fenglimg/fabric-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fab": "dist/index.js",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"tree-sitter-javascript": "^0.25.0",
|
|
23
23
|
"tree-sitter-typescript": "^0.23.2",
|
|
24
24
|
"web-tree-sitter": "^0.26.8",
|
|
25
|
-
"@fenglimg/fabric-server": "1.
|
|
26
|
-
"@fenglimg/fabric-shared": "1.
|
|
25
|
+
"@fenglimg/fabric-server": "1.6.0",
|
|
26
|
+
"@fenglimg/fabric-shared": "1.6.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/iarna__toml": "^2.0.5",
|
|
@@ -13,7 +13,7 @@ process.stdout.write(
|
|
|
13
13
|
JSON.stringify({
|
|
14
14
|
hookSpecificOutput: {
|
|
15
15
|
additionalContext:
|
|
16
|
-
"Fabric
|
|
16
|
+
"这个仓库的 Fabric 初始化还没完成。继续操作前,请先查看 .fabric/forensic.json 和 .fabric/bootstrap/README.md,并使用仓库内的 .agents/skills/fabric-init/SKILL.md。若 Codex hooks 没有触发,请确认配置里已启用 features.codex_hooks = true。",
|
|
17
17
|
},
|
|
18
18
|
}),
|
|
19
19
|
);
|
|
@@ -13,6 +13,6 @@ process.stdout.write(
|
|
|
13
13
|
JSON.stringify({
|
|
14
14
|
decision: "block",
|
|
15
15
|
reason:
|
|
16
|
-
"fab init
|
|
16
|
+
"fab init 已经收集完当前仓库的初始化依据,但后续初始化还没完成。请先确认 Codex 已启用 features.codex_hooks = true,然后查看 .fabric/forensic.json 和 .fabric/bootstrap/README.md,并使用仓库内的 .agents/skills/fabric-init/SKILL.md 继续初始化。",
|
|
17
17
|
}),
|
|
18
18
|
);
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fabric-init
|
|
3
|
-
description: Use this skill when `.fabric/forensic.json` exists and
|
|
3
|
+
description: Use this skill when `.fabric/forensic.json` exists and this repository still needs the remaining Fabric initialization steps.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
## Hard Rules (
|
|
6
|
+
## Hard Rules (不要翻译受保护 token)
|
|
7
7
|
|
|
8
|
-
MUST:
|
|
9
|
-
MUST:
|
|
10
|
-
MUST:
|
|
11
|
-
MUST:
|
|
8
|
+
MUST: 先读取 `.fabric/forensic.json`,再做其他动作。
|
|
9
|
+
MUST: 把 `.fabric/bootstrap/README.md` 视为当前仓库的初始化说明。
|
|
10
|
+
MUST: 如果 `.fabric/init-context.json` 已存在,立即停止并报告当前仓库看起来已经完成后续初始化。
|
|
11
|
+
MUST: 使用 `.fabric/forensic.json` 和仓库结构中的依据,判断接下来该做什么。
|
|
12
12
|
MUST: Preserve protected tokens exactly: `AGENTS.md`, `FABRIC.md`, `.fabric/agents.meta.json`, `.fabric/human-lock.json`, `.fabric/init-context.json`, `.fabric/forensic.json`, `MUST`, `NEVER`.
|
|
13
|
-
NEVER:
|
|
14
|
-
NEVER:
|
|
15
|
-
NEVER:
|
|
13
|
+
NEVER: 在没有检查 `.fabric/init-context.json` 的情况下声称初始化已经完成。
|
|
14
|
+
NEVER: 改写或翻译受保护 token。
|
|
15
|
+
NEVER: 在判断下一步初始化动作时忽略 `.fabric/bootstrap/README.md`。
|
|
16
16
|
|
|
17
17
|
## Purpose
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
当你在 Codex 中处理这个仓库,并且 `fab init` 已经生成 `.fabric/forensic.json` 时,使用这个 skill 继续完成仓库专属的 Fabric 初始化。目标是基于当前仓库的初始化依据和内部说明,明确下一步该做什么,而不是重新解释一遍通用流程。
|
|
20
20
|
|
|
21
21
|
## Workflow
|
|
22
22
|
|
|
23
|
-
1.
|
|
24
|
-
2.
|
|
25
|
-
3.
|
|
26
|
-
4.
|
|
27
|
-
5.
|
|
23
|
+
1. 读取 `.fabric/forensic.json`。
|
|
24
|
+
2. 读取 `.fabric/bootstrap/README.md`。
|
|
25
|
+
3. 检查 `.fabric/init-context.json` 是否已经存在。
|
|
26
|
+
4. 如果初始化仍未完成,明确总结当前仓库接下来要做的初始化动作。
|
|
27
|
+
5. 只讨论这个仓库的后续初始化,不扩展到无关建议。
|