@fenglimg/fabric-cli 1.6.0 → 1.8.0-rc.1
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 -14
- package/dist/{chunk-QSAEGVKE.js → chunk-NMMUETVK.js} +4 -8
- package/dist/{chunk-AEOYCVBG.js → chunk-QPCRBQ5Y.js} +52 -5
- package/dist/doctor-F52XWWZC.js +98 -0
- package/dist/index.js +5 -20
- package/dist/{init-LBVOI2QI.js → init-AEO5JU7R.js} +1084 -167
- package/dist/{scan-QH76LC7Z.js → scan-NNBNGIZG.js} +2 -4
- package/dist/{serve-4J2CQY25.js → serve-466QXQ5Q.js} +17 -9
- package/package.json +5 -7
- package/templates/agents-md/AGENTS.md.template +7 -7
- package/templates/agents-md/variants/cocos.md +7 -7
- package/templates/agents-md/variants/next.md +7 -7
- package/templates/agents-md/variants/vite.md +7 -7
- package/templates/bootstrap/CLAUDE.md +3 -1
- package/templates/bootstrap/GEMINI.md +3 -1
- package/templates/bootstrap/codex-AGENTS-header.md +3 -1
- package/templates/bootstrap/cursor-fabric-bootstrap.mdc +5 -6
- package/templates/bootstrap/roo-fabric.md +5 -6
- package/templates/bootstrap/windsurf-fabric.md +5 -6
- package/templates/claude-skills/fabric-init/SKILL.md +163 -0
- package/templates/codex-skills/fabric-init/SKILL.md +153 -18
- package/templates/husky/pre-commit +9 -24
- package/templates/skill-source/fabric-init/SOURCE.md +157 -0
- package/templates/skill-source/fabric-init/clients.json +17 -0
- package/dist/approve-YT4DEABS.js +0 -138
- package/dist/bootstrap-VGL3AR26.js +0 -16
- package/dist/chunk-2YW5CJ32.js +0 -147
- package/dist/chunk-6ICJICVU.js +0 -10
- package/dist/chunk-BEKSXO5N.js +0 -442
- package/dist/chunk-BVTMVW5M.js +0 -159
- package/dist/chunk-KOAEIH72.js +0 -270
- package/dist/chunk-L43IGJ6X.js +0 -106
- package/dist/chunk-T2WJF5I3.js +0 -254
- package/dist/chunk-WWNXR34K.js +0 -49
- package/dist/chunk-YDZJRLHL.js +0 -155
- package/dist/config-EC5L2QNI.js +0 -16
- package/dist/doctor-4BPYHV7V.js +0 -134
- package/dist/hooks-ZSWVH2JD.js +0 -12
- package/dist/human-lint-YSFOZHZ7.js +0 -13
- package/dist/ledger-append-3MDNR3GU.js +0 -10
- package/dist/pre-commit-53ENJDRZ.js +0 -98
- package/dist/sync-meta-IZR2WLIL.js +0 -16
- package/dist/update-M5M5PYKE.js +0 -116
- package/templates/claude-skills/agents-md-init/SKILL.md +0 -86
- package/templates/fabric/human-lock.json +0 -12
package/dist/chunk-YDZJRLHL.js
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
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
|
-
};
|
package/dist/config-EC5L2QNI.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
configCmd,
|
|
4
|
-
config_default,
|
|
5
|
-
installMcpClients,
|
|
6
|
-
parseClientFilter
|
|
7
|
-
} from "./chunk-BVTMVW5M.js";
|
|
8
|
-
import "./chunk-BEKSXO5N.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/doctor-4BPYHV7V.js
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
resolveDevMode
|
|
4
|
-
} from "./chunk-AEOYCVBG.js";
|
|
5
|
-
import {
|
|
6
|
-
padEnd,
|
|
7
|
-
paint,
|
|
8
|
-
symbol
|
|
9
|
-
} from "./chunk-WWNXR34K.js";
|
|
10
|
-
import {
|
|
11
|
-
t
|
|
12
|
-
} from "./chunk-6ICJICVU.js";
|
|
13
|
-
|
|
14
|
-
// src/commands/doctor.ts
|
|
15
|
-
import { defineCommand } from "citty";
|
|
16
|
-
import { runDoctorAuditReport, runDoctorFix, runDoctorReport } from "@fenglimg/fabric-server";
|
|
17
|
-
var DEFAULT_AUDIT_WINDOW_MINUTES = 5;
|
|
18
|
-
var doctorCommand = defineCommand({
|
|
19
|
-
meta: {
|
|
20
|
-
name: "doctor",
|
|
21
|
-
description: t("cli.doctor.description")
|
|
22
|
-
},
|
|
23
|
-
args: {
|
|
24
|
-
target: {
|
|
25
|
-
type: "string",
|
|
26
|
-
description: t("cli.doctor.args.target.description")
|
|
27
|
-
},
|
|
28
|
-
audit: {
|
|
29
|
-
type: "boolean",
|
|
30
|
-
description: t("cli.doctor.args.audit.description"),
|
|
31
|
-
default: false
|
|
32
|
-
},
|
|
33
|
-
fix: {
|
|
34
|
-
type: "boolean",
|
|
35
|
-
description: t("cli.doctor.args.fix.description"),
|
|
36
|
-
default: false
|
|
37
|
-
},
|
|
38
|
-
"window-minutes": {
|
|
39
|
-
type: "string",
|
|
40
|
-
description: t("cli.doctor.args.window-minutes.description"),
|
|
41
|
-
default: String(DEFAULT_AUDIT_WINDOW_MINUTES)
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
async run({ args }) {
|
|
45
|
-
const workspaceRoot = process.cwd();
|
|
46
|
-
const resolution = resolveDevMode(args.target, workspaceRoot);
|
|
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
|
-
}
|
|
52
|
-
writeStdout(`${renderStatus(report.status)} ${paint.ai("fab doctor")} ${paint.human(resolution.target)}`);
|
|
53
|
-
for (const check of report.checks) {
|
|
54
|
-
writeStdout(`${renderStatus(check.status)} ${check.name}: ${check.message}`);
|
|
55
|
-
}
|
|
56
|
-
if (!args.audit) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const auditReport = await runDoctorAuditReport(resolution.target, {
|
|
60
|
-
force: true,
|
|
61
|
-
windowMs: parseWindowMinutes(args["window-minutes"])
|
|
62
|
-
});
|
|
63
|
-
if (auditReport.mode === "off") {
|
|
64
|
-
writeStderr(t("cli.doctor.audit.preview-only"));
|
|
65
|
-
}
|
|
66
|
-
if (auditReport.checkedPathCount === 0) {
|
|
67
|
-
writeStderr(t("cli.doctor.audit.none"));
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
if (auditReport.violationCount === 0) {
|
|
71
|
-
writeStderr(
|
|
72
|
-
`${symbol.ok} ${t("cli.doctor.audit.clean", {
|
|
73
|
-
count: String(auditReport.checkedPathCount),
|
|
74
|
-
window: formatDuration(auditReport.windowMs)
|
|
75
|
-
})}`
|
|
76
|
-
);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const writer = auditReport.mode === "strict" ? console.error : console.warn;
|
|
80
|
-
writer(
|
|
81
|
-
t("cli.doctor.audit.violations", {
|
|
82
|
-
count: String(auditReport.violationCount),
|
|
83
|
-
window: formatDuration(auditReport.windowMs)
|
|
84
|
-
})
|
|
85
|
-
);
|
|
86
|
-
writeStderr(
|
|
87
|
-
`${padEnd(t("cli.doctor.audit.table.path"), 32)} ${padEnd(t("cli.doctor.audit.table.edit"), 22)} ${padEnd(t("cli.doctor.audit.table.rules"), 22)} ${t("cli.doctor.audit.table.intent")}`
|
|
88
|
-
);
|
|
89
|
-
for (const violation of auditReport.violations) {
|
|
90
|
-
writeStderr(
|
|
91
|
-
`${padEnd(violation.path, 32)} ${padEnd(new Date(violation.editTs).toISOString(), 22)} ${padEnd(formatRulesTs(violation.lastGetRulesTs), 22)} ${violation.intent}`
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
if (auditReport.mode === "strict") {
|
|
95
|
-
process.exitCode = 1;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
var doctor_default = doctorCommand;
|
|
100
|
-
function renderStatus(status) {
|
|
101
|
-
if (status === "ok") {
|
|
102
|
-
return symbol.ok;
|
|
103
|
-
}
|
|
104
|
-
if (status === "warn") {
|
|
105
|
-
return symbol.warn;
|
|
106
|
-
}
|
|
107
|
-
return symbol.error;
|
|
108
|
-
}
|
|
109
|
-
function parseWindowMinutes(value) {
|
|
110
|
-
const minutes = Number.parseInt(value ?? String(DEFAULT_AUDIT_WINDOW_MINUTES), 10);
|
|
111
|
-
if (!Number.isInteger(minutes) || minutes < 1) {
|
|
112
|
-
throw new Error(t("cli.doctor.errors.invalid-window", { value: value ?? "<unset>" }));
|
|
113
|
-
}
|
|
114
|
-
return minutes * 60 * 1e3;
|
|
115
|
-
}
|
|
116
|
-
function formatDuration(durationMs) {
|
|
117
|
-
const minutes = Math.max(Math.floor(durationMs / (60 * 1e3)), 1);
|
|
118
|
-
return `${minutes}m`;
|
|
119
|
-
}
|
|
120
|
-
function formatRulesTs(value) {
|
|
121
|
-
return value === null ? t("cli.shared.none") : new Date(value).toISOString();
|
|
122
|
-
}
|
|
123
|
-
function writeStdout(message) {
|
|
124
|
-
process.stdout.write(`${message}
|
|
125
|
-
`);
|
|
126
|
-
}
|
|
127
|
-
function writeStderr(message) {
|
|
128
|
-
process.stderr.write(`${message}
|
|
129
|
-
`);
|
|
130
|
-
}
|
|
131
|
-
export {
|
|
132
|
-
doctor_default as default,
|
|
133
|
-
doctorCommand
|
|
134
|
-
};
|
package/dist/hooks-ZSWVH2JD.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
humanLintCommand,
|
|
4
|
-
humanLockEntrySchema,
|
|
5
|
-
human_lint_default
|
|
6
|
-
} from "./chunk-L43IGJ6X.js";
|
|
7
|
-
import "./chunk-WWNXR34K.js";
|
|
8
|
-
import "./chunk-6ICJICVU.js";
|
|
9
|
-
export {
|
|
10
|
-
human_lint_default as default,
|
|
11
|
-
humanLintCommand,
|
|
12
|
-
humanLockEntrySchema
|
|
13
|
-
};
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
sync_meta_default
|
|
4
|
-
} from "./chunk-KOAEIH72.js";
|
|
5
|
-
import {
|
|
6
|
-
human_lint_default
|
|
7
|
-
} from "./chunk-L43IGJ6X.js";
|
|
8
|
-
import {
|
|
9
|
-
ledger_append_default
|
|
10
|
-
} from "./chunk-2YW5CJ32.js";
|
|
11
|
-
import {
|
|
12
|
-
resolveDevModeTarget
|
|
13
|
-
} from "./chunk-AEOYCVBG.js";
|
|
14
|
-
import "./chunk-WWNXR34K.js";
|
|
15
|
-
import {
|
|
16
|
-
t
|
|
17
|
-
} from "./chunk-6ICJICVU.js";
|
|
18
|
-
|
|
19
|
-
// src/commands/pre-commit.ts
|
|
20
|
-
import { execSync } from "child_process";
|
|
21
|
-
import { readFileSync } from "fs";
|
|
22
|
-
import { join } from "path";
|
|
23
|
-
import process from "process";
|
|
24
|
-
import { agentsMetaSchema } from "@fenglimg/fabric-shared";
|
|
25
|
-
import { defineCommand } from "citty";
|
|
26
|
-
import { minimatch } from "minimatch";
|
|
27
|
-
import { LEGACY_LEDGER_PATH, LEDGER_PATH } from "@fenglimg/fabric-server";
|
|
28
|
-
async function runOrFail(name, cmd, args) {
|
|
29
|
-
try {
|
|
30
|
-
await cmd.run?.({ args });
|
|
31
|
-
} catch (err) {
|
|
32
|
-
process.stderr.write(
|
|
33
|
-
`${t("cli.pre-commit.run-failed", { name, message: err.message })}
|
|
34
|
-
`
|
|
35
|
-
);
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function getStagedFiles(target) {
|
|
40
|
-
try {
|
|
41
|
-
const output = execSync("git diff --cached --name-only --no-renames", {
|
|
42
|
-
cwd: target,
|
|
43
|
-
encoding: "utf8",
|
|
44
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
45
|
-
});
|
|
46
|
-
return output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
47
|
-
} catch {
|
|
48
|
-
return [];
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function tryReadAgentsMeta(target) {
|
|
52
|
-
const metaPath = join(target, ".fabric", "agents.meta.json");
|
|
53
|
-
try {
|
|
54
|
-
const raw = readFileSync(metaPath, "utf8");
|
|
55
|
-
return agentsMetaSchema.parse(JSON.parse(raw));
|
|
56
|
-
} catch {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
function matchesFabricScope(stagedFiles, meta) {
|
|
61
|
-
const scopeGlobs = Object.values(meta.nodes).filter((node) => node.file !== ".fabric/bootstrap/README.md" && node.file !== "AGENTS.md").map((node) => node.scope_glob);
|
|
62
|
-
return stagedFiles.some(
|
|
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 }))
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
var pre_commit_default = defineCommand({
|
|
67
|
-
meta: {
|
|
68
|
-
name: "pre-commit",
|
|
69
|
-
description: t("cli.pre-commit.description")
|
|
70
|
-
},
|
|
71
|
-
args: {
|
|
72
|
-
target: {
|
|
73
|
-
type: "string",
|
|
74
|
-
description: t("cli.pre-commit.args.target.description")
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
async run({ args }) {
|
|
78
|
-
const target = resolveDevModeTarget(args.target);
|
|
79
|
-
const stagedFiles = getStagedFiles(target);
|
|
80
|
-
const meta = tryReadAgentsMeta(target);
|
|
81
|
-
if (stagedFiles.length > 0 && meta !== null && !matchesFabricScope(stagedFiles, meta)) {
|
|
82
|
-
process.stderr.write("No fabric-managed files staged, skipping checks\n");
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
await runOrFail("sync-meta --check-only", sync_meta_default, {
|
|
86
|
-
target,
|
|
87
|
-
"check-only": true
|
|
88
|
-
});
|
|
89
|
-
await runOrFail("human-lint", human_lint_default, { target });
|
|
90
|
-
await runOrFail("ledger-append --staged", ledger_append_default, {
|
|
91
|
-
target,
|
|
92
|
-
staged: true
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
export {
|
|
97
|
-
pre_commit_default as default
|
|
98
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
computeAgentsMeta,
|
|
4
|
-
deriveLayer,
|
|
5
|
-
deriveTopologyType,
|
|
6
|
-
syncMetaCommand,
|
|
7
|
-
sync_meta_default
|
|
8
|
-
} from "./chunk-KOAEIH72.js";
|
|
9
|
-
import "./chunk-6ICJICVU.js";
|
|
10
|
-
export {
|
|
11
|
-
computeAgentsMeta,
|
|
12
|
-
sync_meta_default as default,
|
|
13
|
-
deriveLayer,
|
|
14
|
-
deriveTopologyType,
|
|
15
|
-
syncMetaCommand
|
|
16
|
-
};
|
package/dist/update-M5M5PYKE.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
resolveDevModeTarget
|
|
4
|
-
} from "./chunk-AEOYCVBG.js";
|
|
5
|
-
import {
|
|
6
|
-
paint
|
|
7
|
-
} from "./chunk-WWNXR34K.js";
|
|
8
|
-
import {
|
|
9
|
-
installMcpClients
|
|
10
|
-
} from "./chunk-BVTMVW5M.js";
|
|
11
|
-
import "./chunk-BEKSXO5N.js";
|
|
12
|
-
import {
|
|
13
|
-
installHooks
|
|
14
|
-
} from "./chunk-YDZJRLHL.js";
|
|
15
|
-
import {
|
|
16
|
-
t
|
|
17
|
-
} from "./chunk-6ICJICVU.js";
|
|
18
|
-
|
|
19
|
-
// src/commands/update.ts
|
|
20
|
-
import { defineCommand } from "citty";
|
|
21
|
-
function writeStderr(message) {
|
|
22
|
-
process.stderr.write(`${message}
|
|
23
|
-
`);
|
|
24
|
-
}
|
|
25
|
-
function completedLabel() {
|
|
26
|
-
return paint.success(t("cli.init.stages.completed"));
|
|
27
|
-
}
|
|
28
|
-
function skippedLabel() {
|
|
29
|
-
return paint.muted(t("cli.init.stages.skipped"));
|
|
30
|
-
}
|
|
31
|
-
function failedLabel() {
|
|
32
|
-
return paint.error(t("cli.init.stages.failed"));
|
|
33
|
-
}
|
|
34
|
-
function formatStageResult(stage, status, installedCount, skippedCount, note) {
|
|
35
|
-
const label = status === "completed" ? completedLabel() : skippedLabel();
|
|
36
|
-
const counts = `installed=${installedCount} skipped=${skippedCount}`;
|
|
37
|
-
const suffix = note ? ` ${paint.muted(`(${note})`)}` : "";
|
|
38
|
-
return `${label} ${stage}: ${counts}${suffix}`;
|
|
39
|
-
}
|
|
40
|
-
function formatStageFailure(stage, error) {
|
|
41
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
42
|
-
return `${failedLabel()} ${stage}: ${message}`;
|
|
43
|
-
}
|
|
44
|
-
function printStageSummary(stageResults) {
|
|
45
|
-
const ran = stageResults.filter((s) => s.disposition === "ran").map((s) => s.name);
|
|
46
|
-
const skipped = stageResults.filter((s) => s.disposition === "skipped").map((s) => s.name);
|
|
47
|
-
const failed = stageResults.filter((s) => s.disposition === "failed").map((s) => s.name);
|
|
48
|
-
console.log(`${paint.success(t("cli.init.stages.summary.ran"))}: ${ran.length > 0 ? ran.join(", ") : t("cli.shared.none")}`);
|
|
49
|
-
console.log(`${paint.muted(t("cli.init.stages.summary.skipped"))}: ${skipped.length > 0 ? skipped.join(", ") : t("cli.shared.none")}`);
|
|
50
|
-
console.log(`${paint.error(t("cli.init.stages.summary.failed"))}: ${failed.length > 0 ? failed.join(", ") : t("cli.shared.none")}`);
|
|
51
|
-
}
|
|
52
|
-
var updateCommand = defineCommand({
|
|
53
|
-
meta: {
|
|
54
|
-
name: "update",
|
|
55
|
-
description: t("cli.update.description")
|
|
56
|
-
},
|
|
57
|
-
args: {
|
|
58
|
-
target: {
|
|
59
|
-
type: "string",
|
|
60
|
-
description: t("cli.update.args.target.description")
|
|
61
|
-
},
|
|
62
|
-
mcp: {
|
|
63
|
-
type: "boolean",
|
|
64
|
-
default: true,
|
|
65
|
-
negativeDescription: t("cli.update.args.no-mcp.description")
|
|
66
|
-
},
|
|
67
|
-
hooks: {
|
|
68
|
-
type: "boolean",
|
|
69
|
-
default: true,
|
|
70
|
-
negativeDescription: t("cli.update.args.no-hooks.description")
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
async run({ args }) {
|
|
74
|
-
const target = resolveDevModeTarget(args.target);
|
|
75
|
-
const skipMcp = args.mcp === false;
|
|
76
|
-
const skipHooks = args.hooks === false;
|
|
77
|
-
const stageResults = [];
|
|
78
|
-
if (skipMcp) {
|
|
79
|
-
stageResults.push({ name: "mcp", disposition: "skipped" });
|
|
80
|
-
} else {
|
|
81
|
-
console.log(`${paint.ai(t("cli.shared.next"))} ${paint.muted(t("cli.init.stages.mcp"))}`);
|
|
82
|
-
try {
|
|
83
|
-
const result = await installMcpClients(target);
|
|
84
|
-
if (result.details.length === 0) {
|
|
85
|
-
console.log(formatStageResult("mcp", "skipped", 0, 0, t("cli.config.install.no-configs")));
|
|
86
|
-
stageResults.push({ name: "mcp", disposition: "skipped" });
|
|
87
|
-
} else {
|
|
88
|
-
console.log(formatStageResult("mcp", "completed", result.installed.length, result.skipped.length));
|
|
89
|
-
stageResults.push({ name: "mcp", disposition: "ran" });
|
|
90
|
-
}
|
|
91
|
-
} catch (error) {
|
|
92
|
-
writeStderr(formatStageFailure("mcp", error));
|
|
93
|
-
stageResults.push({ name: "mcp", disposition: "failed" });
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
if (skipHooks) {
|
|
97
|
-
stageResults.push({ name: "hooks", disposition: "skipped" });
|
|
98
|
-
} else {
|
|
99
|
-
console.log(`${paint.ai(t("cli.shared.next"))} ${paint.muted(t("cli.init.stages.hooks"))}`);
|
|
100
|
-
try {
|
|
101
|
-
const result = await installHooks(target);
|
|
102
|
-
console.log(formatStageResult("hooks", "completed", result.installed.length, result.skipped.length));
|
|
103
|
-
stageResults.push({ name: "hooks", disposition: "ran" });
|
|
104
|
-
} catch (error) {
|
|
105
|
-
writeStderr(formatStageFailure("hooks", error));
|
|
106
|
-
stageResults.push({ name: "hooks", disposition: "failed" });
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
printStageSummary(stageResults);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
var update_default = updateCommand;
|
|
113
|
-
export {
|
|
114
|
-
update_default as default,
|
|
115
|
-
updateCommand
|
|
116
|
-
};
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: agents-md-init
|
|
3
|
-
description: Use this skill when fab init just completed, when forensic.json generated, or when the user is asking to initialize AGENTS.md. This skill runs a 3-phase initialization interview, writes .fabric/init-context.json, generates layered AGENTS.md, and updates .fabric/agents.meta.json.
|
|
4
|
-
allowed-tools: Read, Write, Glob, Grep, Bash
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Precondition
|
|
8
|
-
|
|
9
|
-
必须先 Read `.fabric/forensic.json`。若该文件不存在,终止 skill 并告知用户:`请先运行 fab init 生成证据包`。
|
|
10
|
-
|
|
11
|
-
把以下状态视为 initialization pending:
|
|
12
|
-
|
|
13
|
-
- `.fabric/forensic.json` 存在
|
|
14
|
-
- `.fabric/init-context.json` 不存在
|
|
15
|
-
|
|
16
|
-
## 执行流程 (3 Phase / 3 Round)
|
|
17
|
-
|
|
18
|
-
### Phase 1 — 框架确认(1 轮,高效)
|
|
19
|
-
|
|
20
|
-
展示 `.fabric/forensic.json` 的 `framework`、`topology.by_ext`、`entry_points` 摘要,向用户提 1-2 个框架架构澄清问题。
|
|
21
|
-
|
|
22
|
-
示例(Cocos Creator 3.x):
|
|
23
|
-
|
|
24
|
-
> 我检测到 Cocos Creator 3.8 项目,主要脚本在 `assets/scripts`,采用 `@ccclass + extends Component` 模式。请确认:(1) 这是 TypeScript 项目(非 JavaScript)对吗?(2) 节点引用主要通过 `@property(Node)` 注入,还是 `find/getChildByName`?
|
|
25
|
-
|
|
26
|
-
将用户确认结果暂存为已验证 framework assumptions。
|
|
27
|
-
|
|
28
|
-
### Phase 2 — 不变式提取(1 轮,关键)
|
|
29
|
-
|
|
30
|
-
基于 `.fabric/forensic.json` 的 `recommendations_for_skill` 列表,向用户提 3-5 个 invariants 问题,覆盖三类:
|
|
31
|
-
|
|
32
|
-
- `ban`:禁止 any、禁止 update() 中 async、禁止 find-by-name 等
|
|
33
|
-
- `require`:必须 strict TypeScript、必须 `@ccclass` decorator、必须 import from `cc` only 等
|
|
34
|
-
- `protect`:哪些目录或文件 AI 不能修改,一般是 `assets/prefabs/**`、`assets/scenes/**`、`**/*.meta`
|
|
35
|
-
|
|
36
|
-
原则:
|
|
37
|
-
|
|
38
|
-
- 只问 invariants,不问 preferences
|
|
39
|
-
- 每个问题只接受 yes/no/具体规则,不接受模糊回答
|
|
40
|
-
- 不要自动推测用户未确认的硬约束
|
|
41
|
-
|
|
42
|
-
### Phase 3 — 构造与落地(1 轮,自动)
|
|
43
|
-
|
|
44
|
-
1. 写入 `.fabric/init-context.json`,包含:
|
|
45
|
-
|
|
46
|
-
- `framework`
|
|
47
|
-
- `architecture_patterns`
|
|
48
|
-
- `invariants`
|
|
49
|
-
- `domain_groups`
|
|
50
|
-
- `interview_trail`
|
|
51
|
-
- `forensic_ref`
|
|
52
|
-
|
|
53
|
-
写入规则:
|
|
54
|
-
|
|
55
|
-
- `invariants[].type` 必须是 `ban`、`require`、`protect`
|
|
56
|
-
- `domain_groups` 由 `entry_points` 和访谈结果推断
|
|
57
|
-
- `interview_trail[]` 必须记录 Phase 1 和 Phase 2 的原始问答
|
|
58
|
-
- `forensic_ref` 必须为 `.fabric/forensic.json`
|
|
59
|
-
|
|
60
|
-
2. 生成分层 `AGENTS.md`:
|
|
61
|
-
|
|
62
|
-
- 根 `AGENTS.md` 必须在 300 行以内,结构包含:
|
|
63
|
-
- `# {projectName} — L0 AGENTS.md`
|
|
64
|
-
- `<!-- fab:index -->`:填充 `domain_groups` 索引
|
|
65
|
-
- `## L0 AI Constraints`:从 invariants 派生,按 `ban`、`require`、`protect` 分段
|
|
66
|
-
- `## @HUMAN`:protect 路径和用户声明的人类保护规则
|
|
67
|
-
- `## L1 Candidate Notes`:domain_groups 对应的候选子模块说明
|
|
68
|
-
|
|
69
|
-
如果 `domain_groups.length >= 2`,为每个 group 生成 `{group_path}/AGENTS.md`。最多到 L3,总嵌套不超过 4 层。
|
|
70
|
-
|
|
71
|
-
3. 更新 `.fabric/agents.meta.json` 的 nodes 树,保持 revision hash 链一致:
|
|
72
|
-
|
|
73
|
-
- nodes 结构与生成后的 AGENTS 层级一致
|
|
74
|
-
- 更新所有变更 AGENTS 文件的 hash
|
|
75
|
-
- 保持 revision hash 链内部一致
|
|
76
|
-
|
|
77
|
-
4. 最终输出:向用户列出生成文件清单,并建议后续维护时运行 `fab sync-meta`。
|
|
78
|
-
|
|
79
|
-
## Hard Rules
|
|
80
|
-
|
|
81
|
-
- Zero TODO: 不生成任何 `TODO`、`TBD`、placeholder、stub
|
|
82
|
-
- No YAML frontmatter in outputs: 除本 skill 自身外,生成的 `AGENTS.md` 不得包含 YAML frontmatter
|
|
83
|
-
- Root AGENTS.md <= 300 lines
|
|
84
|
-
- AGENTS 嵌套总层级 <= 4
|
|
85
|
-
- 不得自动推测用户未确认的 invariants
|
|
86
|
-
- 有不确定内容时删除,不要留占位符
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://fabric.local/schemas/human-lock.json",
|
|
3
|
-
"locked": [],
|
|
4
|
-
"examples": [
|
|
5
|
-
{
|
|
6
|
-
"file": "AGENTS.md",
|
|
7
|
-
"start_line": 18,
|
|
8
|
-
"end_line": 22,
|
|
9
|
-
"hash": "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
|
10
|
-
}
|
|
11
|
-
]
|
|
12
|
-
}
|