@forge-ts/cli 0.14.0 → 0.16.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/dist/chunk-CNINN7IQ.js +28 -0
- package/dist/chunk-CNINN7IQ.js.map +1 -0
- package/dist/chunk-DV33Y4E2.js +333 -0
- package/dist/chunk-DV33Y4E2.js.map +1 -0
- package/dist/chunk-ZFFY4AQX.js +73 -0
- package/dist/chunk-ZFFY4AQX.js.map +1 -0
- package/dist/forge-logger-RTOBEKWH.js +10 -0
- package/dist/forge-logger-RTOBEKWH.js.map +1 -0
- package/dist/index.d.ts +292 -28
- package/dist/index.js +644 -184
- package/dist/index.js.map +1 -1
- package/dist/init-project-5AWZ2LAI.js +14 -0
- package/dist/init-project-5AWZ2LAI.js.map +1 -0
- package/dist/output-OSCHMPOX.js +10 -0
- package/dist/output-OSCHMPOX.js.map +1 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
initProjectCommand,
|
|
4
|
+
runInitProject
|
|
5
|
+
} from "./chunk-DV33Y4E2.js";
|
|
6
|
+
import {
|
|
7
|
+
configureLogger,
|
|
8
|
+
forgeLogger
|
|
9
|
+
} from "./chunk-CNINN7IQ.js";
|
|
10
|
+
import {
|
|
11
|
+
emitResult,
|
|
12
|
+
resolveExitCode
|
|
13
|
+
} from "./chunk-ZFFY4AQX.js";
|
|
2
14
|
|
|
3
15
|
// src/index.ts
|
|
4
16
|
import { createRequire } from "module";
|
|
5
|
-
import { defineCommand as
|
|
17
|
+
import { defineCommand as defineCommand13, runMain } from "citty";
|
|
6
18
|
|
|
7
19
|
// src/commands/audit.ts
|
|
8
20
|
import {
|
|
@@ -10,74 +22,6 @@ import {
|
|
|
10
22
|
readAuditLog
|
|
11
23
|
} from "@forge-ts/core";
|
|
12
24
|
import { defineCommand } from "citty";
|
|
13
|
-
|
|
14
|
-
// src/output.ts
|
|
15
|
-
import { randomUUID } from "crypto";
|
|
16
|
-
import {
|
|
17
|
-
createEnvelope,
|
|
18
|
-
resolveFlags
|
|
19
|
-
} from "@cleocode/lafs-protocol";
|
|
20
|
-
function emitResult(output, flags, humanFormatter) {
|
|
21
|
-
const flagInput = {
|
|
22
|
-
json: flags.json,
|
|
23
|
-
human: flags.human,
|
|
24
|
-
quiet: flags.quiet,
|
|
25
|
-
mvi: flags.mvi,
|
|
26
|
-
// LAFS 1.8.0: TTY detection drives the default format.
|
|
27
|
-
// Terminals get human output, pipes/agents get JSON.
|
|
28
|
-
tty: process.stdout.isTTY ?? false
|
|
29
|
-
};
|
|
30
|
-
const resolved = resolveFlags(flagInput);
|
|
31
|
-
const format = resolved.format.format;
|
|
32
|
-
const quiet = resolved.format.quiet;
|
|
33
|
-
if (quiet) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
const meta = {
|
|
37
|
-
operation: `forge-ts.${output.operation}`,
|
|
38
|
-
requestId: randomUUID(),
|
|
39
|
-
transport: "cli",
|
|
40
|
-
mvi: flags.mvi ?? "full"
|
|
41
|
-
};
|
|
42
|
-
const resultData = output.data;
|
|
43
|
-
if (output.warnings && output.warnings.length > 0) {
|
|
44
|
-
resultData._warnings = output.warnings.map((w) => ({
|
|
45
|
-
code: w.code,
|
|
46
|
-
message: w.message
|
|
47
|
-
}));
|
|
48
|
-
}
|
|
49
|
-
const envelope = output.success ? createEnvelope({
|
|
50
|
-
success: true,
|
|
51
|
-
result: resultData,
|
|
52
|
-
meta
|
|
53
|
-
}) : createEnvelope({
|
|
54
|
-
success: false,
|
|
55
|
-
result: resultData,
|
|
56
|
-
error: {
|
|
57
|
-
code: output.errors?.[0]?.code ?? "FORGE_CHECK_FAILED",
|
|
58
|
-
message: output.errors?.[0]?.message ?? "Check failed \u2014 see result for actionable fixes",
|
|
59
|
-
category: "VALIDATION",
|
|
60
|
-
retryable: true,
|
|
61
|
-
retryAfterMs: null
|
|
62
|
-
},
|
|
63
|
-
meta
|
|
64
|
-
});
|
|
65
|
-
if (format === "json") {
|
|
66
|
-
process.stdout.write(`${JSON.stringify(envelope, null, 2)}
|
|
67
|
-
`);
|
|
68
|
-
} else {
|
|
69
|
-
const formatted = humanFormatter(output.data, output);
|
|
70
|
-
if (formatted) {
|
|
71
|
-
console.log(formatted);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function resolveExitCode(output) {
|
|
76
|
-
if (output.success) return 0;
|
|
77
|
-
return 1;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// src/commands/audit.ts
|
|
81
25
|
function runAudit(args) {
|
|
82
26
|
const rootDir = args.cwd ?? process.cwd();
|
|
83
27
|
const limit = args.limit ?? 20;
|
|
@@ -153,8 +97,8 @@ var auditCommand = defineCommand({
|
|
|
153
97
|
run({ args }) {
|
|
154
98
|
const eventType = args.type;
|
|
155
99
|
if (eventType && !VALID_EVENT_TYPES.includes(eventType)) {
|
|
156
|
-
|
|
157
|
-
`
|
|
100
|
+
forgeLogger.error(
|
|
101
|
+
`Invalid event type "${eventType}". Valid types: ${VALID_EVENT_TYPES.join(", ")}`
|
|
158
102
|
);
|
|
159
103
|
process.exit(1);
|
|
160
104
|
}
|
|
@@ -178,46 +122,6 @@ import { generateApi } from "@forge-ts/api";
|
|
|
178
122
|
import { loadConfig } from "@forge-ts/core";
|
|
179
123
|
import { generate } from "@forge-ts/gen";
|
|
180
124
|
import { defineCommand as defineCommand2 } from "citty";
|
|
181
|
-
|
|
182
|
-
// src/logger.ts
|
|
183
|
-
var GREEN = "\x1B[32m";
|
|
184
|
-
var YELLOW = "\x1B[33m";
|
|
185
|
-
var RED = "\x1B[31m";
|
|
186
|
-
var BOLD = "\x1B[1m";
|
|
187
|
-
var RESET = "\x1B[0m";
|
|
188
|
-
function createLogger(options) {
|
|
189
|
-
const useColors = options?.colors ?? process.stdout.isTTY ?? false;
|
|
190
|
-
function colorize(text, code) {
|
|
191
|
-
return useColors ? `${code}${text}${RESET}` : text;
|
|
192
|
-
}
|
|
193
|
-
function bold(text) {
|
|
194
|
-
return useColors ? `${BOLD}${text}${RESET}` : text;
|
|
195
|
-
}
|
|
196
|
-
return {
|
|
197
|
-
info(msg) {
|
|
198
|
-
console.log(msg);
|
|
199
|
-
},
|
|
200
|
-
success(msg) {
|
|
201
|
-
const prefix = colorize("\u2713", GREEN);
|
|
202
|
-
console.log(`${prefix} ${msg}`);
|
|
203
|
-
},
|
|
204
|
-
warn(msg) {
|
|
205
|
-
const prefix = colorize("warn", YELLOW);
|
|
206
|
-
console.warn(`${bold(prefix)} ${msg}`);
|
|
207
|
-
},
|
|
208
|
-
error(msg) {
|
|
209
|
-
const prefix = colorize("error", RED);
|
|
210
|
-
console.error(`${bold(prefix)} ${msg}`);
|
|
211
|
-
},
|
|
212
|
-
step(label, detail, duration) {
|
|
213
|
-
const check = colorize("\u2713", GREEN);
|
|
214
|
-
const durationStr = duration !== void 0 ? ` (${duration}ms)` : "";
|
|
215
|
-
console.log(` ${check} ${bold(label)}: ${detail}${durationStr}`);
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// src/commands/build.ts
|
|
221
125
|
async function runBuild(args) {
|
|
222
126
|
const config = await loadConfig(args.cwd);
|
|
223
127
|
const buildStart = Date.now();
|
|
@@ -380,15 +284,15 @@ var buildCommand = defineCommand2({
|
|
|
380
284
|
mvi: args.mvi
|
|
381
285
|
};
|
|
382
286
|
emitResult(output, flags, (data) => {
|
|
383
|
-
const logger = createLogger();
|
|
384
287
|
for (const step of data.steps) {
|
|
385
288
|
if (step.status === "failed") {
|
|
386
289
|
for (const err of step.errors ?? []) {
|
|
387
|
-
|
|
290
|
+
forgeLogger.error(`[${step.name}] ${err.message}`);
|
|
388
291
|
}
|
|
389
292
|
} else if (step.status === "success") {
|
|
390
293
|
const detail = step.name === "api" && step.outputPath != null ? `Generated OpenAPI spec \u2192 ${step.outputPath}` : `Step complete`;
|
|
391
|
-
|
|
294
|
+
const durationStr = step.duration !== void 0 ? ` (${step.duration}ms)` : "";
|
|
295
|
+
forgeLogger.success(`${step.name.toUpperCase()}: ${detail}${durationStr}`);
|
|
392
296
|
}
|
|
393
297
|
}
|
|
394
298
|
if (output.success) {
|
|
@@ -555,9 +459,7 @@ var bypassCommand = defineCommand3({
|
|
|
555
459
|
return;
|
|
556
460
|
}
|
|
557
461
|
if (!args.reason) {
|
|
558
|
-
|
|
559
|
-
"[forge-ts] error: --reason is required. Provide a justification for the bypass."
|
|
560
|
-
);
|
|
462
|
+
forgeLogger.error("--reason is required. Provide a justification for the bypass.");
|
|
561
463
|
process.exit(1);
|
|
562
464
|
}
|
|
563
465
|
const output = await runBypassCreate({
|
|
@@ -924,17 +826,15 @@ import { loadConfig as loadConfig4 } from "@forge-ts/core";
|
|
|
924
826
|
import { DEFAULT_TARGET, getAdapter } from "@forge-ts/gen";
|
|
925
827
|
import { defineCommand as defineCommand5 } from "citty";
|
|
926
828
|
async function runDocsDev(args) {
|
|
927
|
-
const logger = createLogger();
|
|
928
829
|
const config = await loadConfig4(args.cwd);
|
|
929
830
|
const target = args.target ?? config.gen.ssgTarget ?? DEFAULT_TARGET;
|
|
930
831
|
const adapter = getAdapter(target);
|
|
931
832
|
const outDir = resolve(config.outDir);
|
|
932
833
|
const devCmd = adapter.getDevCommand(outDir);
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
logger.info("");
|
|
834
|
+
forgeLogger.info(`Starting ${devCmd.label}...`);
|
|
835
|
+
forgeLogger.info(` Target: ${target}`);
|
|
836
|
+
forgeLogger.info(` Directory: ${outDir}`);
|
|
837
|
+
forgeLogger.info(` URL: ${devCmd.url}`);
|
|
938
838
|
const spawnArgs = [...devCmd.args];
|
|
939
839
|
if (args.port) {
|
|
940
840
|
spawnArgs.push("--port", args.port);
|
|
@@ -979,17 +879,474 @@ var docsDevCommand = defineCommand5({
|
|
|
979
879
|
}
|
|
980
880
|
});
|
|
981
881
|
|
|
982
|
-
// src/commands/
|
|
983
|
-
import { existsSync } from "fs";
|
|
882
|
+
// src/commands/doctor.ts
|
|
883
|
+
import { existsSync, readFileSync } from "fs";
|
|
984
884
|
import { mkdir, writeFile } from "fs/promises";
|
|
985
|
-
import { join
|
|
885
|
+
import { join } from "path";
|
|
886
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
887
|
+
function readJsonSafe(filePath) {
|
|
888
|
+
if (!existsSync(filePath)) {
|
|
889
|
+
return null;
|
|
890
|
+
}
|
|
891
|
+
try {
|
|
892
|
+
const raw = readFileSync(filePath, "utf8");
|
|
893
|
+
return JSON.parse(raw);
|
|
894
|
+
} catch {
|
|
895
|
+
return null;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
var DEFAULT_CONFIG_CONTENT = `import { defineConfig } from "@forge-ts/core";
|
|
899
|
+
|
|
900
|
+
export default defineConfig({
|
|
901
|
+
rootDir: ".",
|
|
902
|
+
tsconfig: "tsconfig.json",
|
|
903
|
+
outDir: "docs/generated",
|
|
904
|
+
enforce: {
|
|
905
|
+
enabled: true,
|
|
906
|
+
minVisibility: "public",
|
|
907
|
+
strict: false,
|
|
908
|
+
},
|
|
909
|
+
gen: {
|
|
910
|
+
enabled: true,
|
|
911
|
+
formats: ["mdx"],
|
|
912
|
+
llmsTxt: true,
|
|
913
|
+
readmeSync: false,
|
|
914
|
+
ssgTarget: "mintlify",
|
|
915
|
+
},
|
|
916
|
+
});
|
|
917
|
+
`;
|
|
918
|
+
var DEFAULT_TSDOC_CONTENT = JSON.stringify(
|
|
919
|
+
{
|
|
920
|
+
$schema: "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
|
|
921
|
+
extends: ["@forge-ts/core/tsdoc-preset/tsdoc.json"]
|
|
922
|
+
},
|
|
923
|
+
null,
|
|
924
|
+
" "
|
|
925
|
+
);
|
|
926
|
+
async function runDoctor(args) {
|
|
927
|
+
const start = Date.now();
|
|
928
|
+
const rootDir = args.cwd ?? process.cwd();
|
|
929
|
+
const fix = args.fix ?? false;
|
|
930
|
+
const checks = [];
|
|
931
|
+
const fixed = [];
|
|
932
|
+
const configPath = join(rootDir, "forge-ts.config.ts");
|
|
933
|
+
const configJsPath = join(rootDir, "forge-ts.config.js");
|
|
934
|
+
if (existsSync(configPath) || existsSync(configJsPath)) {
|
|
935
|
+
const which = existsSync(configPath) ? "forge-ts.config.ts" : "forge-ts.config.js";
|
|
936
|
+
checks.push({
|
|
937
|
+
name: "forge-ts.config",
|
|
938
|
+
status: "pass",
|
|
939
|
+
message: `${which} \u2014 found`,
|
|
940
|
+
fixable: false
|
|
941
|
+
});
|
|
942
|
+
} else if (fix) {
|
|
943
|
+
await mkdir(rootDir, { recursive: true });
|
|
944
|
+
await writeFile(configPath, DEFAULT_CONFIG_CONTENT, "utf8");
|
|
945
|
+
fixed.push("forge-ts.config.ts");
|
|
946
|
+
checks.push({
|
|
947
|
+
name: "forge-ts.config",
|
|
948
|
+
status: "pass",
|
|
949
|
+
message: "forge-ts.config.ts \u2014 created by --fix",
|
|
950
|
+
fixable: true
|
|
951
|
+
});
|
|
952
|
+
} else {
|
|
953
|
+
checks.push({
|
|
954
|
+
name: "forge-ts.config",
|
|
955
|
+
status: "error",
|
|
956
|
+
message: "forge-ts.config.ts \u2014 MISSING (run forge-ts init or forge-ts doctor --fix)",
|
|
957
|
+
fixable: true
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
const tsdocPath = join(rootDir, "tsdoc.json");
|
|
961
|
+
if (existsSync(tsdocPath)) {
|
|
962
|
+
const tsdoc = readJsonSafe(tsdocPath);
|
|
963
|
+
if (tsdoc?.extends?.includes("@forge-ts/core/tsdoc-preset/tsdoc.json")) {
|
|
964
|
+
checks.push({
|
|
965
|
+
name: "tsdoc.json",
|
|
966
|
+
status: "pass",
|
|
967
|
+
message: "tsdoc.json \u2014 extends @forge-ts/core/tsdoc-preset",
|
|
968
|
+
fixable: false
|
|
969
|
+
});
|
|
970
|
+
} else {
|
|
971
|
+
checks.push({
|
|
972
|
+
name: "tsdoc.json",
|
|
973
|
+
status: "warn",
|
|
974
|
+
message: "tsdoc.json \u2014 does not extend @forge-ts/core/tsdoc-preset",
|
|
975
|
+
fixable: false
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
} else if (fix) {
|
|
979
|
+
await writeFile(tsdocPath, `${DEFAULT_TSDOC_CONTENT}
|
|
980
|
+
`, "utf8");
|
|
981
|
+
fixed.push("tsdoc.json");
|
|
982
|
+
checks.push({
|
|
983
|
+
name: "tsdoc.json",
|
|
984
|
+
status: "pass",
|
|
985
|
+
message: "tsdoc.json \u2014 created by --fix",
|
|
986
|
+
fixable: true
|
|
987
|
+
});
|
|
988
|
+
} else {
|
|
989
|
+
checks.push({
|
|
990
|
+
name: "tsdoc.json",
|
|
991
|
+
status: "error",
|
|
992
|
+
message: "tsdoc.json \u2014 MISSING (run forge-ts init or forge-ts doctor --fix)",
|
|
993
|
+
fixable: true
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
const coreModulePath = join(rootDir, "node_modules", "@forge-ts", "core", "package.json");
|
|
997
|
+
if (existsSync(coreModulePath)) {
|
|
998
|
+
const corePkg = readJsonSafe(coreModulePath);
|
|
999
|
+
const version = corePkg?.version ?? "unknown";
|
|
1000
|
+
checks.push({
|
|
1001
|
+
name: "@forge-ts/core",
|
|
1002
|
+
status: "pass",
|
|
1003
|
+
message: `@forge-ts/core \u2014 installed (${version})`,
|
|
1004
|
+
fixable: false
|
|
1005
|
+
});
|
|
1006
|
+
} else {
|
|
1007
|
+
checks.push({
|
|
1008
|
+
name: "@forge-ts/core",
|
|
1009
|
+
status: "warn",
|
|
1010
|
+
message: "@forge-ts/core \u2014 MISSING (run npm install @forge-ts/core)",
|
|
1011
|
+
fixable: false
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
const tsPkgPath = join(rootDir, "node_modules", "typescript", "package.json");
|
|
1015
|
+
if (existsSync(tsPkgPath)) {
|
|
1016
|
+
const tsPkg = readJsonSafe(tsPkgPath);
|
|
1017
|
+
const version = tsPkg?.version ?? "unknown";
|
|
1018
|
+
checks.push({
|
|
1019
|
+
name: "TypeScript",
|
|
1020
|
+
status: "pass",
|
|
1021
|
+
message: `TypeScript \u2014 ${version}`,
|
|
1022
|
+
fixable: false
|
|
1023
|
+
});
|
|
1024
|
+
} else {
|
|
1025
|
+
const pkgPath = join(rootDir, "package.json");
|
|
1026
|
+
const pkg2 = readJsonSafe(pkgPath);
|
|
1027
|
+
const allDeps = {
|
|
1028
|
+
...pkg2?.dependencies,
|
|
1029
|
+
...pkg2?.devDependencies
|
|
1030
|
+
};
|
|
1031
|
+
if ("typescript" in allDeps) {
|
|
1032
|
+
checks.push({
|
|
1033
|
+
name: "TypeScript",
|
|
1034
|
+
status: "warn",
|
|
1035
|
+
message: `TypeScript \u2014 in package.json (${allDeps.typescript}) but not in node_modules`,
|
|
1036
|
+
fixable: false
|
|
1037
|
+
});
|
|
1038
|
+
} else {
|
|
1039
|
+
checks.push({
|
|
1040
|
+
name: "TypeScript",
|
|
1041
|
+
status: "error",
|
|
1042
|
+
message: "TypeScript \u2014 MISSING (run npm install -D typescript)",
|
|
1043
|
+
fixable: false
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
const tsconfigPath = join(rootDir, "tsconfig.json");
|
|
1048
|
+
if (existsSync(tsconfigPath)) {
|
|
1049
|
+
const tsconfig = readJsonSafe(tsconfigPath);
|
|
1050
|
+
if (tsconfig?.compilerOptions?.strict === true) {
|
|
1051
|
+
checks.push({
|
|
1052
|
+
name: "tsconfig.json",
|
|
1053
|
+
status: "pass",
|
|
1054
|
+
message: "tsconfig.json \u2014 strict mode enabled",
|
|
1055
|
+
fixable: false
|
|
1056
|
+
});
|
|
1057
|
+
} else {
|
|
1058
|
+
const missingFlags = ["strict"];
|
|
1059
|
+
if (!tsconfig?.compilerOptions?.strictNullChecks) {
|
|
1060
|
+
missingFlags.push("strictNullChecks");
|
|
1061
|
+
}
|
|
1062
|
+
if (!tsconfig?.compilerOptions?.noImplicitAny) {
|
|
1063
|
+
missingFlags.push("noImplicitAny");
|
|
1064
|
+
}
|
|
1065
|
+
checks.push({
|
|
1066
|
+
name: "tsconfig.json",
|
|
1067
|
+
status: "warn",
|
|
1068
|
+
message: `tsconfig.json \u2014 strict mode not fully enabled (missing ${missingFlags.join(", ")})`,
|
|
1069
|
+
fixable: false
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
} else {
|
|
1073
|
+
checks.push({
|
|
1074
|
+
name: "tsconfig.json",
|
|
1075
|
+
status: "warn",
|
|
1076
|
+
message: "tsconfig.json \u2014 not found",
|
|
1077
|
+
fixable: false
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
const biomePath = join(rootDir, "biome.json");
|
|
1081
|
+
const biomecPath = join(rootDir, "biome.jsonc");
|
|
1082
|
+
if (existsSync(biomePath) || existsSync(biomecPath)) {
|
|
1083
|
+
checks.push({
|
|
1084
|
+
name: "biome.json",
|
|
1085
|
+
status: "pass",
|
|
1086
|
+
message: "biome.json \u2014 found",
|
|
1087
|
+
fixable: false
|
|
1088
|
+
});
|
|
1089
|
+
} else {
|
|
1090
|
+
checks.push({
|
|
1091
|
+
name: "biome.json",
|
|
1092
|
+
status: "info",
|
|
1093
|
+
message: "biome.json \u2014 not found (optional)",
|
|
1094
|
+
fixable: false
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
const lockPath = join(rootDir, ".forge-lock.json");
|
|
1098
|
+
if (existsSync(lockPath)) {
|
|
1099
|
+
const lock = readJsonSafe(lockPath);
|
|
1100
|
+
if (lock?.lockedAt) {
|
|
1101
|
+
checks.push({
|
|
1102
|
+
name: ".forge-lock.json",
|
|
1103
|
+
status: "pass",
|
|
1104
|
+
message: `.forge-lock.json \u2014 locked at ${lock.lockedAt}`,
|
|
1105
|
+
fixable: false
|
|
1106
|
+
});
|
|
1107
|
+
} else {
|
|
1108
|
+
checks.push({
|
|
1109
|
+
name: ".forge-lock.json",
|
|
1110
|
+
status: "warn",
|
|
1111
|
+
message: ".forge-lock.json \u2014 invalid format (run forge-ts lock to regenerate)",
|
|
1112
|
+
fixable: false
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
} else {
|
|
1116
|
+
checks.push({
|
|
1117
|
+
name: ".forge-lock.json",
|
|
1118
|
+
status: "warn",
|
|
1119
|
+
message: ".forge-lock.json \u2014 not locked (run forge-ts lock)",
|
|
1120
|
+
fixable: false
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
const auditPath = join(rootDir, ".forge-audit.jsonl");
|
|
1124
|
+
if (existsSync(auditPath)) {
|
|
1125
|
+
try {
|
|
1126
|
+
const raw = readFileSync(auditPath, "utf8");
|
|
1127
|
+
const lines = raw.split("\n").filter((line) => line.trim().length > 0);
|
|
1128
|
+
checks.push({
|
|
1129
|
+
name: ".forge-audit.jsonl",
|
|
1130
|
+
status: "info",
|
|
1131
|
+
message: `.forge-audit.jsonl \u2014 ${lines.length} event${lines.length !== 1 ? "s" : ""}`,
|
|
1132
|
+
fixable: false
|
|
1133
|
+
});
|
|
1134
|
+
} catch {
|
|
1135
|
+
checks.push({
|
|
1136
|
+
name: ".forge-audit.jsonl",
|
|
1137
|
+
status: "warn",
|
|
1138
|
+
message: ".forge-audit.jsonl \u2014 exists but unreadable",
|
|
1139
|
+
fixable: false
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
} else {
|
|
1143
|
+
checks.push({
|
|
1144
|
+
name: ".forge-audit.jsonl",
|
|
1145
|
+
status: "info",
|
|
1146
|
+
message: ".forge-audit.jsonl \u2014 no audit trail",
|
|
1147
|
+
fixable: false
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
const bypassPath = join(rootDir, ".forge-bypass.json");
|
|
1151
|
+
if (existsSync(bypassPath)) {
|
|
1152
|
+
const records = readJsonSafe(bypassPath);
|
|
1153
|
+
if (records && Array.isArray(records)) {
|
|
1154
|
+
const now = /* @__PURE__ */ new Date();
|
|
1155
|
+
const active = records.filter((r) => r.expiresAt && new Date(r.expiresAt) > now);
|
|
1156
|
+
const expired = records.length - active.length;
|
|
1157
|
+
if (active.length > 0) {
|
|
1158
|
+
checks.push({
|
|
1159
|
+
name: ".forge-bypass.json",
|
|
1160
|
+
status: "info",
|
|
1161
|
+
message: `.forge-bypass.json \u2014 ${active.length} active bypass${active.length !== 1 ? "es" : ""}`,
|
|
1162
|
+
fixable: false
|
|
1163
|
+
});
|
|
1164
|
+
} else if (expired > 0) {
|
|
1165
|
+
checks.push({
|
|
1166
|
+
name: ".forge-bypass.json",
|
|
1167
|
+
status: "info",
|
|
1168
|
+
message: `.forge-bypass.json \u2014 ${expired} expired bypass${expired !== 1 ? "es" : ""} (run cleanup)`,
|
|
1169
|
+
fixable: false
|
|
1170
|
+
});
|
|
1171
|
+
} else {
|
|
1172
|
+
checks.push({
|
|
1173
|
+
name: ".forge-bypass.json",
|
|
1174
|
+
status: "pass",
|
|
1175
|
+
message: ".forge-bypass.json \u2014 no active bypasses",
|
|
1176
|
+
fixable: false
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
} else {
|
|
1180
|
+
checks.push({
|
|
1181
|
+
name: ".forge-bypass.json",
|
|
1182
|
+
status: "warn",
|
|
1183
|
+
message: ".forge-bypass.json \u2014 invalid format",
|
|
1184
|
+
fixable: false
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
} else {
|
|
1188
|
+
checks.push({
|
|
1189
|
+
name: ".forge-bypass.json",
|
|
1190
|
+
status: "pass",
|
|
1191
|
+
message: ".forge-bypass.json \u2014 no active bypasses",
|
|
1192
|
+
fixable: false
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
const huskyPreCommit = join(rootDir, ".husky", "pre-commit");
|
|
1196
|
+
const lefthookYml = join(rootDir, "lefthook.yml");
|
|
1197
|
+
let hookConfigured = false;
|
|
1198
|
+
let hookLocation = "";
|
|
1199
|
+
if (existsSync(huskyPreCommit)) {
|
|
1200
|
+
try {
|
|
1201
|
+
const content = readFileSync(huskyPreCommit, "utf8");
|
|
1202
|
+
if (content.includes("forge-ts check")) {
|
|
1203
|
+
hookConfigured = true;
|
|
1204
|
+
hookLocation = "husky pre-commit";
|
|
1205
|
+
}
|
|
1206
|
+
} catch {
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
if (!hookConfigured && existsSync(lefthookYml)) {
|
|
1210
|
+
try {
|
|
1211
|
+
const content = readFileSync(lefthookYml, "utf8");
|
|
1212
|
+
if (content.includes("forge-ts check")) {
|
|
1213
|
+
hookConfigured = true;
|
|
1214
|
+
hookLocation = "lefthook";
|
|
1215
|
+
}
|
|
1216
|
+
} catch {
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
if (hookConfigured) {
|
|
1220
|
+
checks.push({
|
|
1221
|
+
name: "Git hooks",
|
|
1222
|
+
status: "pass",
|
|
1223
|
+
message: `Git hooks \u2014 forge-ts check in ${hookLocation}`,
|
|
1224
|
+
fixable: false
|
|
1225
|
+
});
|
|
1226
|
+
} else {
|
|
1227
|
+
checks.push({
|
|
1228
|
+
name: "Git hooks",
|
|
1229
|
+
status: "warn",
|
|
1230
|
+
message: "Git hooks \u2014 forge-ts check not in pre-commit (run forge-ts init hooks)",
|
|
1231
|
+
fixable: false
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
const summary = {
|
|
1235
|
+
passed: checks.filter((c) => c.status === "pass").length,
|
|
1236
|
+
warnings: checks.filter((c) => c.status === "warn").length,
|
|
1237
|
+
errors: checks.filter((c) => c.status === "error").length,
|
|
1238
|
+
info: checks.filter((c) => c.status === "info").length
|
|
1239
|
+
};
|
|
1240
|
+
const data = {
|
|
1241
|
+
success: summary.errors === 0,
|
|
1242
|
+
checks,
|
|
1243
|
+
summary,
|
|
1244
|
+
fixed
|
|
1245
|
+
};
|
|
1246
|
+
return {
|
|
1247
|
+
operation: "doctor",
|
|
1248
|
+
success: summary.errors === 0,
|
|
1249
|
+
data,
|
|
1250
|
+
duration: Date.now() - start
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
var STATUS_LABELS = {
|
|
1254
|
+
pass: "[PASS]",
|
|
1255
|
+
warn: "[WARN]",
|
|
1256
|
+
error: "[FAIL]",
|
|
1257
|
+
info: "[INFO]"
|
|
1258
|
+
};
|
|
1259
|
+
function formatDoctorHuman(result) {
|
|
1260
|
+
const lines = [];
|
|
1261
|
+
lines.push("\nforge-ts doctor: project health check\n");
|
|
1262
|
+
for (const check of result.checks) {
|
|
1263
|
+
const label = STATUS_LABELS[check.status];
|
|
1264
|
+
lines.push(` ${label} ${check.message}`);
|
|
1265
|
+
}
|
|
1266
|
+
if (result.fixed.length > 0) {
|
|
1267
|
+
lines.push("");
|
|
1268
|
+
lines.push(" Fixed:");
|
|
1269
|
+
for (const file of result.fixed) {
|
|
1270
|
+
lines.push(` ${file}`);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
lines.push("");
|
|
1274
|
+
lines.push(
|
|
1275
|
+
` Summary: ${result.summary.passed} passed, ${result.summary.warnings} warning${result.summary.warnings !== 1 ? "s" : ""}, ${result.summary.errors} error${result.summary.errors !== 1 ? "s" : ""}`
|
|
1276
|
+
);
|
|
1277
|
+
if (result.summary.errors > 0 || result.summary.warnings > 0) {
|
|
1278
|
+
lines.push(" Run: forge-ts doctor --fix to auto-fix resolvable issues");
|
|
1279
|
+
}
|
|
1280
|
+
return lines.join("\n");
|
|
1281
|
+
}
|
|
1282
|
+
var doctorCommand = defineCommand6({
|
|
1283
|
+
meta: {
|
|
1284
|
+
name: "doctor",
|
|
1285
|
+
description: "Project integrity check and repair"
|
|
1286
|
+
},
|
|
1287
|
+
args: {
|
|
1288
|
+
cwd: {
|
|
1289
|
+
type: "string",
|
|
1290
|
+
description: "Project root directory"
|
|
1291
|
+
},
|
|
1292
|
+
fix: {
|
|
1293
|
+
type: "boolean",
|
|
1294
|
+
description: "Auto-fix resolvable issues",
|
|
1295
|
+
default: false
|
|
1296
|
+
},
|
|
1297
|
+
json: {
|
|
1298
|
+
type: "boolean",
|
|
1299
|
+
description: "Output as LAFS JSON envelope",
|
|
1300
|
+
default: false
|
|
1301
|
+
},
|
|
1302
|
+
human: {
|
|
1303
|
+
type: "boolean",
|
|
1304
|
+
description: "Output as formatted text",
|
|
1305
|
+
default: false
|
|
1306
|
+
},
|
|
1307
|
+
quiet: {
|
|
1308
|
+
type: "boolean",
|
|
1309
|
+
description: "Suppress non-essential output",
|
|
1310
|
+
default: false
|
|
1311
|
+
},
|
|
1312
|
+
mvi: {
|
|
1313
|
+
type: "string",
|
|
1314
|
+
description: "MVI verbosity level: minimal, standard, full"
|
|
1315
|
+
}
|
|
1316
|
+
},
|
|
1317
|
+
async run({ args }) {
|
|
1318
|
+
const output = await runDoctor({
|
|
1319
|
+
cwd: args.cwd,
|
|
1320
|
+
fix: args.fix,
|
|
1321
|
+
mvi: args.mvi
|
|
1322
|
+
});
|
|
1323
|
+
const flags = {
|
|
1324
|
+
json: args.json,
|
|
1325
|
+
human: args.human,
|
|
1326
|
+
quiet: args.quiet,
|
|
1327
|
+
mvi: args.mvi
|
|
1328
|
+
};
|
|
1329
|
+
emitResult(output, flags, (data, cmd) => {
|
|
1330
|
+
if (!cmd.success) {
|
|
1331
|
+
return formatDoctorHuman(data);
|
|
1332
|
+
}
|
|
1333
|
+
return formatDoctorHuman(data);
|
|
1334
|
+
});
|
|
1335
|
+
process.exit(resolveExitCode(output));
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
// src/commands/init-docs.ts
|
|
1340
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1341
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
1342
|
+
import { join as join2, resolve as resolve2 } from "path";
|
|
986
1343
|
import { loadConfig as loadConfig5 } from "@forge-ts/core";
|
|
987
1344
|
import {
|
|
988
1345
|
DEFAULT_TARGET as DEFAULT_TARGET2,
|
|
989
1346
|
getAdapter as getAdapter2,
|
|
990
1347
|
getAvailableTargets
|
|
991
1348
|
} from "@forge-ts/gen";
|
|
992
|
-
import { defineCommand as
|
|
1349
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
993
1350
|
async function runInitDocs(args) {
|
|
994
1351
|
const start = Date.now();
|
|
995
1352
|
const rawTarget = args.target ?? DEFAULT_TARGET2;
|
|
@@ -1060,15 +1417,15 @@ async function runInitDocs(args) {
|
|
|
1060
1417
|
const manifest = adapter.scaffold(context);
|
|
1061
1418
|
const writtenFiles = [];
|
|
1062
1419
|
for (const file of manifest.files) {
|
|
1063
|
-
const filePath =
|
|
1420
|
+
const filePath = join2(outDir, file.path);
|
|
1064
1421
|
const fileDir = filePath.substring(0, filePath.lastIndexOf("/"));
|
|
1065
|
-
await
|
|
1066
|
-
await
|
|
1422
|
+
await mkdir2(fileDir, { recursive: true });
|
|
1423
|
+
await writeFile2(filePath, file.content, "utf8");
|
|
1067
1424
|
writtenFiles.push(file.path);
|
|
1068
1425
|
}
|
|
1069
1426
|
if (config.tsdoc.writeConfig) {
|
|
1070
|
-
const tsdocPath =
|
|
1071
|
-
if (
|
|
1427
|
+
const tsdocPath = join2(config.rootDir, "tsdoc.json");
|
|
1428
|
+
if (existsSync2(tsdocPath)) {
|
|
1072
1429
|
warnings.push({
|
|
1073
1430
|
code: "INIT_TSDOC_EXISTS",
|
|
1074
1431
|
message: "tsdoc.json already exists \u2014 skipping. Remove it and re-run to regenerate."
|
|
@@ -1077,13 +1434,13 @@ async function runInitDocs(args) {
|
|
|
1077
1434
|
const tsdocContent = JSON.stringify(
|
|
1078
1435
|
{
|
|
1079
1436
|
$schema: "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
|
|
1080
|
-
extends: ["@forge-ts/tsdoc-
|
|
1437
|
+
extends: ["@forge-ts/core/tsdoc-preset/tsdoc.json"]
|
|
1081
1438
|
},
|
|
1082
1439
|
null,
|
|
1083
1440
|
" "
|
|
1084
1441
|
);
|
|
1085
|
-
await
|
|
1086
|
-
await
|
|
1442
|
+
await mkdir2(config.rootDir, { recursive: true });
|
|
1443
|
+
await writeFile2(tsdocPath, `${tsdocContent}
|
|
1087
1444
|
`, "utf8");
|
|
1088
1445
|
writtenFiles.push("tsdoc.json");
|
|
1089
1446
|
}
|
|
@@ -1145,7 +1502,7 @@ function formatInitDocsHuman(result) {
|
|
|
1145
1502
|
);
|
|
1146
1503
|
return lines.join("\n");
|
|
1147
1504
|
}
|
|
1148
|
-
var initDocsCommand =
|
|
1505
|
+
var initDocsCommand = defineCommand7({
|
|
1149
1506
|
meta: {
|
|
1150
1507
|
name: "init",
|
|
1151
1508
|
description: "Scaffold a documentation site"
|
|
@@ -1204,9 +1561,8 @@ var initDocsCommand = defineCommand6({
|
|
|
1204
1561
|
};
|
|
1205
1562
|
emitResult(output, flags, (data, cmd) => {
|
|
1206
1563
|
if (!cmd.success) {
|
|
1207
|
-
const logger = createLogger();
|
|
1208
1564
|
const msg = cmd.errors?.[0]?.message ?? "Scaffold failed";
|
|
1209
|
-
|
|
1565
|
+
forgeLogger.error(msg);
|
|
1210
1566
|
return "";
|
|
1211
1567
|
}
|
|
1212
1568
|
return formatInitDocsHuman(data);
|
|
@@ -1214,7 +1570,7 @@ var initDocsCommand = defineCommand6({
|
|
|
1214
1570
|
process.exit(resolveExitCode(output));
|
|
1215
1571
|
}
|
|
1216
1572
|
});
|
|
1217
|
-
var initCommand =
|
|
1573
|
+
var initCommand = defineCommand7({
|
|
1218
1574
|
meta: {
|
|
1219
1575
|
name: "init",
|
|
1220
1576
|
description: "Scaffold project artefacts"
|
|
@@ -1225,23 +1581,23 @@ var initCommand = defineCommand6({
|
|
|
1225
1581
|
});
|
|
1226
1582
|
|
|
1227
1583
|
// src/commands/init-hooks.ts
|
|
1228
|
-
import { existsSync as
|
|
1229
|
-
import { mkdir as
|
|
1230
|
-
import { join as
|
|
1231
|
-
import { defineCommand as
|
|
1584
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
1585
|
+
import { mkdir as mkdir3, readFile, writeFile as writeFile3 } from "fs/promises";
|
|
1586
|
+
import { join as join3 } from "path";
|
|
1587
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
1232
1588
|
function detectHookManager(rootDir) {
|
|
1233
|
-
const huskyDir =
|
|
1234
|
-
if (
|
|
1589
|
+
const huskyDir = join3(rootDir, ".husky");
|
|
1590
|
+
if (existsSync3(huskyDir)) {
|
|
1235
1591
|
return "husky";
|
|
1236
1592
|
}
|
|
1237
|
-
const lefthookYml =
|
|
1238
|
-
if (
|
|
1593
|
+
const lefthookYml = join3(rootDir, "lefthook.yml");
|
|
1594
|
+
if (existsSync3(lefthookYml)) {
|
|
1239
1595
|
return "lefthook";
|
|
1240
1596
|
}
|
|
1241
|
-
const pkgJsonPath =
|
|
1242
|
-
if (
|
|
1597
|
+
const pkgJsonPath = join3(rootDir, "package.json");
|
|
1598
|
+
if (existsSync3(pkgJsonPath)) {
|
|
1243
1599
|
try {
|
|
1244
|
-
const raw =
|
|
1600
|
+
const raw = readFileSync2(pkgJsonPath, "utf8");
|
|
1245
1601
|
const pkg2 = JSON.parse(raw);
|
|
1246
1602
|
const allDeps = { ...pkg2.dependencies, ...pkg2.devDependencies };
|
|
1247
1603
|
if ("husky" in allDeps) return "husky";
|
|
@@ -1270,10 +1626,10 @@ async function runInitHooks(args) {
|
|
|
1270
1626
|
const warnings = [];
|
|
1271
1627
|
const instructions = [];
|
|
1272
1628
|
if (hookManager === "husky" || hookManager === "none") {
|
|
1273
|
-
const huskyDir =
|
|
1274
|
-
const hookPath =
|
|
1629
|
+
const huskyDir = join3(rootDir, ".husky");
|
|
1630
|
+
const hookPath = join3(huskyDir, "pre-commit");
|
|
1275
1631
|
const relativePath = ".husky/pre-commit";
|
|
1276
|
-
if (
|
|
1632
|
+
if (existsSync3(hookPath) && !args.force) {
|
|
1277
1633
|
const existing = await readFile(hookPath, "utf8");
|
|
1278
1634
|
if (existing.includes("forge-ts check")) {
|
|
1279
1635
|
skippedFiles.push(relativePath);
|
|
@@ -1286,12 +1642,12 @@ async function runInitHooks(args) {
|
|
|
1286
1642
|
|
|
1287
1643
|
npx forge-ts check
|
|
1288
1644
|
`;
|
|
1289
|
-
await
|
|
1645
|
+
await writeFile3(hookPath, appended, { mode: 493 });
|
|
1290
1646
|
writtenFiles.push(relativePath);
|
|
1291
1647
|
}
|
|
1292
1648
|
} else {
|
|
1293
|
-
await
|
|
1294
|
-
await
|
|
1649
|
+
await mkdir3(huskyDir, { recursive: true });
|
|
1650
|
+
await writeFile3(hookPath, HUSKY_PRE_COMMIT, { mode: 493 });
|
|
1295
1651
|
writtenFiles.push(relativePath);
|
|
1296
1652
|
}
|
|
1297
1653
|
if (hookManager === "none") {
|
|
@@ -1303,9 +1659,9 @@ npx forge-ts check
|
|
|
1303
1659
|
instructions.push("Husky pre-commit hook configured to run forge-ts check.");
|
|
1304
1660
|
}
|
|
1305
1661
|
} else if (hookManager === "lefthook") {
|
|
1306
|
-
const lefthookPath =
|
|
1662
|
+
const lefthookPath = join3(rootDir, "lefthook.yml");
|
|
1307
1663
|
const relativePath = "lefthook.yml";
|
|
1308
|
-
if (
|
|
1664
|
+
if (existsSync3(lefthookPath)) {
|
|
1309
1665
|
const existing = await readFile(lefthookPath, "utf8");
|
|
1310
1666
|
if (existing.includes("forge-ts check") && !args.force) {
|
|
1311
1667
|
skippedFiles.push(relativePath);
|
|
@@ -1318,17 +1674,17 @@ npx forge-ts check
|
|
|
1318
1674
|
forge-ts-check:
|
|
1319
1675
|
run: npx forge-ts check
|
|
1320
1676
|
`;
|
|
1321
|
-
await
|
|
1677
|
+
await writeFile3(lefthookPath, appended, "utf8");
|
|
1322
1678
|
writtenFiles.push(relativePath);
|
|
1323
1679
|
} else {
|
|
1324
1680
|
const appended = `${existing.trimEnd()}
|
|
1325
1681
|
|
|
1326
1682
|
${LEFTHOOK_BLOCK}`;
|
|
1327
|
-
await
|
|
1683
|
+
await writeFile3(lefthookPath, appended, "utf8");
|
|
1328
1684
|
writtenFiles.push(relativePath);
|
|
1329
1685
|
}
|
|
1330
1686
|
} else {
|
|
1331
|
-
await
|
|
1687
|
+
await writeFile3(lefthookPath, LEFTHOOK_BLOCK, "utf8");
|
|
1332
1688
|
writtenFiles.push(relativePath);
|
|
1333
1689
|
}
|
|
1334
1690
|
instructions.push("Lefthook pre-commit hook configured to run forge-ts check.");
|
|
@@ -1373,7 +1729,7 @@ function formatInitHooksHuman(result) {
|
|
|
1373
1729
|
${result.summary.filesWritten} file(s) written.`);
|
|
1374
1730
|
return lines.join("\n");
|
|
1375
1731
|
}
|
|
1376
|
-
var initHooksCommand =
|
|
1732
|
+
var initHooksCommand = defineCommand8({
|
|
1377
1733
|
meta: {
|
|
1378
1734
|
name: "hooks",
|
|
1379
1735
|
description: "Scaffold git hook integration (husky/lefthook)"
|
|
@@ -1422,9 +1778,8 @@ var initHooksCommand = defineCommand7({
|
|
|
1422
1778
|
};
|
|
1423
1779
|
emitResult(output, flags, (data, cmd) => {
|
|
1424
1780
|
if (!cmd.success) {
|
|
1425
|
-
const logger = createLogger();
|
|
1426
1781
|
const msg = cmd.errors?.[0]?.message ?? "Hook scaffolding failed";
|
|
1427
|
-
|
|
1782
|
+
forgeLogger.error(msg);
|
|
1428
1783
|
return "";
|
|
1429
1784
|
}
|
|
1430
1785
|
return formatInitHooksHuman(data);
|
|
@@ -1441,7 +1796,7 @@ import {
|
|
|
1441
1796
|
readLockFile,
|
|
1442
1797
|
writeLockFile
|
|
1443
1798
|
} from "@forge-ts/core";
|
|
1444
|
-
import { defineCommand as
|
|
1799
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
1445
1800
|
async function runLock(args) {
|
|
1446
1801
|
const config = await loadConfig6(args.cwd);
|
|
1447
1802
|
const rootDir = config.rootDir;
|
|
@@ -1500,7 +1855,7 @@ function formatLockHuman(result) {
|
|
|
1500
1855
|
To modify locked settings, run: forge-ts unlock --reason="..."`);
|
|
1501
1856
|
return lines.join("\n");
|
|
1502
1857
|
}
|
|
1503
|
-
var lockCommand =
|
|
1858
|
+
var lockCommand = defineCommand9({
|
|
1504
1859
|
meta: {
|
|
1505
1860
|
name: "lock",
|
|
1506
1861
|
description: "Lock current config to prevent silent weakening"
|
|
@@ -1539,7 +1894,7 @@ var lockCommand = defineCommand8({
|
|
|
1539
1894
|
});
|
|
1540
1895
|
|
|
1541
1896
|
// src/commands/prepublish.ts
|
|
1542
|
-
import { defineCommand as
|
|
1897
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
1543
1898
|
async function runPrepublish(args) {
|
|
1544
1899
|
const start = Date.now();
|
|
1545
1900
|
const allErrors = [];
|
|
@@ -1643,7 +1998,7 @@ function formatPrepublishHuman(result) {
|
|
|
1643
1998
|
}
|
|
1644
1999
|
return lines.join("\n");
|
|
1645
2000
|
}
|
|
1646
|
-
var prepublishCommand =
|
|
2001
|
+
var prepublishCommand = defineCommand10({
|
|
1647
2002
|
meta: {
|
|
1648
2003
|
name: "prepublish",
|
|
1649
2004
|
description: "Safety gate: check + build before npm publish"
|
|
@@ -1692,8 +2047,7 @@ var prepublishCommand = defineCommand9({
|
|
|
1692
2047
|
};
|
|
1693
2048
|
emitResult(output, flags, (data, cmd) => {
|
|
1694
2049
|
if (!cmd.success) {
|
|
1695
|
-
|
|
1696
|
-
logger.error("Prepublish gate failed");
|
|
2050
|
+
forgeLogger.error("Prepublish gate failed");
|
|
1697
2051
|
}
|
|
1698
2052
|
return formatPrepublishHuman(data);
|
|
1699
2053
|
});
|
|
@@ -1704,7 +2058,7 @@ var prepublishCommand = defineCommand9({
|
|
|
1704
2058
|
// src/commands/test.ts
|
|
1705
2059
|
import { loadConfig as loadConfig7 } from "@forge-ts/core";
|
|
1706
2060
|
import { doctest } from "@forge-ts/doctest";
|
|
1707
|
-
import { defineCommand as
|
|
2061
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
1708
2062
|
async function runTest(args) {
|
|
1709
2063
|
const config = await loadConfig7(args.cwd);
|
|
1710
2064
|
const result = await doctest(config);
|
|
@@ -1747,7 +2101,7 @@ async function runTest(args) {
|
|
|
1747
2101
|
duration: result.duration
|
|
1748
2102
|
};
|
|
1749
2103
|
}
|
|
1750
|
-
var testCommand =
|
|
2104
|
+
var testCommand = defineCommand11({
|
|
1751
2105
|
meta: {
|
|
1752
2106
|
name: "test",
|
|
1753
2107
|
description: "Run @example blocks as doctests"
|
|
@@ -1808,7 +2162,7 @@ import {
|
|
|
1808
2162
|
readLockFile as readLockFile2,
|
|
1809
2163
|
removeLockFile
|
|
1810
2164
|
} from "@forge-ts/core";
|
|
1811
|
-
import { defineCommand as
|
|
2165
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
1812
2166
|
async function runUnlock(args) {
|
|
1813
2167
|
const config = await loadConfig8(args.cwd);
|
|
1814
2168
|
const rootDir = config.rootDir;
|
|
@@ -1893,7 +2247,7 @@ function formatUnlockHuman(result) {
|
|
|
1893
2247
|
lines.push(" Run `forge-ts lock` to re-lock after changes.");
|
|
1894
2248
|
return lines.join("\n");
|
|
1895
2249
|
}
|
|
1896
|
-
var unlockCommand =
|
|
2250
|
+
var unlockCommand = defineCommand12({
|
|
1897
2251
|
meta: {
|
|
1898
2252
|
name: "unlock",
|
|
1899
2253
|
description: "Remove config lock (requires --reason)"
|
|
@@ -1926,9 +2280,7 @@ var unlockCommand = defineCommand11({
|
|
|
1926
2280
|
},
|
|
1927
2281
|
async run({ args }) {
|
|
1928
2282
|
if (!args.reason) {
|
|
1929
|
-
|
|
1930
|
-
"[forge-ts] error: --reason is required. Provide a reason for unlocking the config."
|
|
1931
|
-
);
|
|
2283
|
+
forgeLogger.error("--reason is required. Provide a reason for unlocking the config.");
|
|
1932
2284
|
process.exit(1);
|
|
1933
2285
|
}
|
|
1934
2286
|
const output = await runUnlock({
|
|
@@ -1948,7 +2300,7 @@ var unlockCommand = defineCommand11({
|
|
|
1948
2300
|
// src/index.ts
|
|
1949
2301
|
var require2 = createRequire(import.meta.url);
|
|
1950
2302
|
var pkg = require2("../package.json");
|
|
1951
|
-
var docsCommand =
|
|
2303
|
+
var docsCommand = defineCommand13({
|
|
1952
2304
|
meta: {
|
|
1953
2305
|
name: "docs",
|
|
1954
2306
|
description: "Documentation site management"
|
|
@@ -1958,17 +2310,119 @@ var docsCommand = defineCommand12({
|
|
|
1958
2310
|
dev: docsDevCommand
|
|
1959
2311
|
}
|
|
1960
2312
|
});
|
|
1961
|
-
var initCommand2 =
|
|
2313
|
+
var initCommand2 = defineCommand13({
|
|
1962
2314
|
meta: {
|
|
1963
2315
|
name: "init",
|
|
1964
|
-
description: "
|
|
2316
|
+
description: "Full project setup (bare) or scaffold artefacts (with subcommand)"
|
|
2317
|
+
},
|
|
2318
|
+
args: {
|
|
2319
|
+
cwd: {
|
|
2320
|
+
type: "string",
|
|
2321
|
+
description: "Project root directory"
|
|
2322
|
+
},
|
|
2323
|
+
json: {
|
|
2324
|
+
type: "boolean",
|
|
2325
|
+
description: "Output as LAFS JSON envelope",
|
|
2326
|
+
default: false
|
|
2327
|
+
},
|
|
2328
|
+
human: {
|
|
2329
|
+
type: "boolean",
|
|
2330
|
+
description: "Output as formatted text",
|
|
2331
|
+
default: false
|
|
2332
|
+
},
|
|
2333
|
+
quiet: {
|
|
2334
|
+
type: "boolean",
|
|
2335
|
+
description: "Suppress non-essential output",
|
|
2336
|
+
default: false
|
|
2337
|
+
},
|
|
2338
|
+
mvi: {
|
|
2339
|
+
type: "string",
|
|
2340
|
+
description: "MVI verbosity level: minimal, standard, full"
|
|
2341
|
+
}
|
|
1965
2342
|
},
|
|
1966
2343
|
subCommands: {
|
|
1967
2344
|
docs: initDocsCommand,
|
|
1968
|
-
hooks: initHooksCommand
|
|
2345
|
+
hooks: initHooksCommand,
|
|
2346
|
+
setup: initProjectCommand
|
|
2347
|
+
},
|
|
2348
|
+
async run({ rawArgs, args }) {
|
|
2349
|
+
const subCommandNames = /* @__PURE__ */ new Set(["docs", "hooks", "setup"]);
|
|
2350
|
+
const hasSubCommand = rawArgs.some((arg) => subCommandNames.has(arg));
|
|
2351
|
+
if (hasSubCommand) {
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
const { runInitProject: runInitProject2 } = await import("./init-project-5AWZ2LAI.js");
|
|
2355
|
+
const { emitResult: emitResult2, resolveExitCode: resolveExitCode2 } = await import("./output-OSCHMPOX.js");
|
|
2356
|
+
const { forgeLogger: forgeLogger2 } = await import("./forge-logger-RTOBEKWH.js");
|
|
2357
|
+
const output = await runInitProject2({
|
|
2358
|
+
cwd: args.cwd,
|
|
2359
|
+
mvi: args.mvi
|
|
2360
|
+
});
|
|
2361
|
+
const flags = {
|
|
2362
|
+
json: args.json,
|
|
2363
|
+
human: args.human,
|
|
2364
|
+
quiet: args.quiet,
|
|
2365
|
+
mvi: args.mvi
|
|
2366
|
+
};
|
|
2367
|
+
emitResult2(output, flags, (data, cmd) => {
|
|
2368
|
+
if (!cmd.success) {
|
|
2369
|
+
const msg = cmd.errors?.[0]?.message ?? "Init failed";
|
|
2370
|
+
forgeLogger2.error(msg);
|
|
2371
|
+
return "";
|
|
2372
|
+
}
|
|
2373
|
+
const lines = [];
|
|
2374
|
+
lines.push("\nforge-ts init: project setup complete\n");
|
|
2375
|
+
if (data.created.length > 0) {
|
|
2376
|
+
lines.push(" Created:");
|
|
2377
|
+
for (const file of data.created) {
|
|
2378
|
+
lines.push(` ${file}`);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
if (data.skipped.length > 0) {
|
|
2382
|
+
lines.push("");
|
|
2383
|
+
lines.push(" Already exists (skipped):");
|
|
2384
|
+
for (const file of data.skipped) {
|
|
2385
|
+
lines.push(` ${file}`);
|
|
2386
|
+
}
|
|
2387
|
+
} else if (data.created.length > 0) {
|
|
2388
|
+
lines.push("");
|
|
2389
|
+
lines.push(" Already exists (skipped):");
|
|
2390
|
+
lines.push(" (none)");
|
|
2391
|
+
}
|
|
2392
|
+
if (data.warnings.length > 0) {
|
|
2393
|
+
lines.push("");
|
|
2394
|
+
lines.push(" Warnings:");
|
|
2395
|
+
for (const warning of data.warnings) {
|
|
2396
|
+
lines.push(` ${warning}`);
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
const env = data.environment;
|
|
2400
|
+
lines.push("");
|
|
2401
|
+
lines.push(" Environment:");
|
|
2402
|
+
lines.push(` TypeScript: ${env.typescriptVersion ?? "not detected"}`);
|
|
2403
|
+
lines.push(` Biome: ${env.biomeDetected ? "detected" : "not detected"}`);
|
|
2404
|
+
const hookLabel = env.hookManager === "none" ? "not detected" : `${env.hookManager} detected`;
|
|
2405
|
+
lines.push(` Git hooks: ${hookLabel}`);
|
|
2406
|
+
if (env.monorepo) {
|
|
2407
|
+
lines.push(
|
|
2408
|
+
` Monorepo: ${env.monorepoType === "pnpm" ? "pnpm workspaces" : "npm/yarn workspaces"}`
|
|
2409
|
+
);
|
|
2410
|
+
} else {
|
|
2411
|
+
lines.push(" Monorepo: no");
|
|
2412
|
+
}
|
|
2413
|
+
if (data.nextSteps.length > 0) {
|
|
2414
|
+
lines.push("");
|
|
2415
|
+
lines.push(" Next steps:");
|
|
2416
|
+
for (const [idx, step] of data.nextSteps.entries()) {
|
|
2417
|
+
lines.push(` ${idx + 1}. ${step}`);
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
return lines.join("\n");
|
|
2421
|
+
});
|
|
2422
|
+
process.exit(resolveExitCode2(output));
|
|
1969
2423
|
}
|
|
1970
2424
|
});
|
|
1971
|
-
var main =
|
|
2425
|
+
var main = defineCommand13({
|
|
1972
2426
|
meta: {
|
|
1973
2427
|
name: "forge-ts",
|
|
1974
2428
|
version: pkg.version,
|
|
@@ -1984,7 +2438,8 @@ var main = defineCommand12({
|
|
|
1984
2438
|
unlock: unlockCommand,
|
|
1985
2439
|
bypass: bypassCommand,
|
|
1986
2440
|
audit: auditCommand,
|
|
1987
|
-
prepublish: prepublishCommand
|
|
2441
|
+
prepublish: prepublishCommand,
|
|
2442
|
+
doctor: doctorCommand
|
|
1988
2443
|
}
|
|
1989
2444
|
});
|
|
1990
2445
|
runMain(main);
|
|
@@ -1993,18 +2448,23 @@ export {
|
|
|
1993
2448
|
buildCommand,
|
|
1994
2449
|
bypassCommand,
|
|
1995
2450
|
checkCommand,
|
|
1996
|
-
|
|
2451
|
+
configureLogger,
|
|
1997
2452
|
docsDevCommand,
|
|
2453
|
+
doctorCommand,
|
|
1998
2454
|
emitResult,
|
|
2455
|
+
forgeLogger,
|
|
1999
2456
|
initDocsCommand,
|
|
2000
2457
|
initHooksCommand,
|
|
2458
|
+
initProjectCommand,
|
|
2001
2459
|
lockCommand,
|
|
2002
2460
|
prepublishCommand,
|
|
2003
2461
|
resolveExitCode,
|
|
2004
2462
|
runBypassCreate,
|
|
2005
2463
|
runBypassStatus,
|
|
2006
2464
|
runDocsDev,
|
|
2465
|
+
runDoctor,
|
|
2007
2466
|
runInitHooks,
|
|
2467
|
+
runInitProject,
|
|
2008
2468
|
runLock,
|
|
2009
2469
|
runPrepublish,
|
|
2010
2470
|
runUnlock,
|