@forge-ts/cli 0.8.0 → 0.14.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/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as citty from 'citty';
2
+ import { AuditEvent, BypassRecord } from '@forge-ts/core';
2
3
  import { SSGTarget } from '@forge-ts/gen';
3
4
 
4
5
  /**
@@ -93,6 +94,54 @@ declare function emitResult<T>(output: CommandOutput<T>, flags: OutputFlags, hum
93
94
  */
94
95
  declare function resolveExitCode(output: CommandOutput<unknown>): number;
95
96
 
97
+ /**
98
+ * Typed result for the `audit` command.
99
+ *
100
+ * @public
101
+ */
102
+ interface AuditResult {
103
+ /** Whether the audit log was read successfully. */
104
+ success: boolean;
105
+ /** Number of events returned. */
106
+ count: number;
107
+ /** The audit events, newest first. */
108
+ events: AuditEvent[];
109
+ }
110
+ /**
111
+ * Citty command definition for `forge-ts audit`.
112
+ *
113
+ * @public
114
+ */
115
+ declare const auditCommand: citty.CommandDef<{
116
+ readonly cwd: {
117
+ readonly type: "string";
118
+ readonly description: "Project root directory";
119
+ };
120
+ readonly limit: {
121
+ readonly type: "string";
122
+ readonly description: "Maximum events to display (default: 20)";
123
+ };
124
+ readonly type: {
125
+ readonly type: "string";
126
+ readonly description: "Filter by event type (config.lock, config.unlock, config.drift, bypass.create, bypass.expire, rule.change)";
127
+ };
128
+ readonly json: {
129
+ readonly type: "boolean";
130
+ readonly description: "Output as LAFS JSON envelope (agent-friendly)";
131
+ readonly default: false;
132
+ };
133
+ readonly human: {
134
+ readonly type: "boolean";
135
+ readonly description: "Output as formatted text (default for TTY)";
136
+ readonly default: false;
137
+ };
138
+ readonly quiet: {
139
+ readonly type: "boolean";
140
+ readonly description: "Suppress non-essential output";
141
+ readonly default: false;
142
+ };
143
+ }>;
144
+
96
145
  /**
97
146
  * A single step in the build pipeline.
98
147
  * @public
@@ -177,6 +226,113 @@ declare const buildCommand: citty.CommandDef<{
177
226
  };
178
227
  }>;
179
228
 
229
+ /**
230
+ * Typed result for the `bypass` command when creating a bypass.
231
+ * @public
232
+ */
233
+ interface BypassCreateResult {
234
+ /** Whether the bypass was successfully created. */
235
+ success: boolean;
236
+ /** The bypass record that was created. */
237
+ bypass: BypassRecord;
238
+ /** Number of remaining bypass slots for today after creation. */
239
+ remainingBudget: number;
240
+ /** The configured daily budget. */
241
+ dailyBudget: number;
242
+ }
243
+ /**
244
+ * Typed result for the `bypass --status` command.
245
+ * @public
246
+ */
247
+ interface BypassStatusResult {
248
+ /** Always true for status queries. */
249
+ success: boolean;
250
+ /** Active (non-expired) bypass records. */
251
+ activeBypasses: BypassRecord[];
252
+ /** Number of remaining bypass slots for today. */
253
+ remainingBudget: number;
254
+ /** The configured daily budget. */
255
+ dailyBudget: number;
256
+ /** Number of expired bypasses that were cleaned up. */
257
+ expiredRemoved: number;
258
+ }
259
+ /**
260
+ * Runs the bypass creation: creates a new bypass record with budget enforcement.
261
+ *
262
+ * @param args - CLI arguments for the bypass command.
263
+ * @returns A typed `CommandOutput<BypassCreateResult>`.
264
+ * @example
265
+ * ```typescript
266
+ * import { runBypassCreate } from "@forge-ts/cli/commands/bypass";
267
+ * const output = await runBypassCreate({
268
+ * cwd: process.cwd(),
269
+ * reason: "hotfix for release",
270
+ * rule: "E009",
271
+ * });
272
+ * console.log(output.data.remainingBudget);
273
+ * ```
274
+ * @public
275
+ */
276
+ declare function runBypassCreate(args: {
277
+ cwd?: string;
278
+ reason: string;
279
+ rule?: string;
280
+ }): Promise<CommandOutput<BypassCreateResult>>;
281
+ /**
282
+ * Runs the bypass status query: shows active bypasses and remaining budget.
283
+ *
284
+ * @param args - CLI arguments for the bypass status command.
285
+ * @returns A typed `CommandOutput<BypassStatusResult>`.
286
+ * @example
287
+ * ```typescript
288
+ * import { runBypassStatus } from "@forge-ts/cli/commands/bypass";
289
+ * const output = await runBypassStatus({ cwd: process.cwd() });
290
+ * console.log(output.data.activeBypasses.length);
291
+ * ```
292
+ * @public
293
+ */
294
+ declare function runBypassStatus(args: {
295
+ cwd?: string;
296
+ }): Promise<CommandOutput<BypassStatusResult>>;
297
+ /**
298
+ * Citty command definition for `forge-ts bypass`.
299
+ * @public
300
+ */
301
+ declare const bypassCommand: citty.CommandDef<{
302
+ readonly cwd: {
303
+ readonly type: "string";
304
+ readonly description: "Project root directory";
305
+ };
306
+ readonly reason: {
307
+ readonly type: "string";
308
+ readonly description: "Mandatory justification for bypassing rules";
309
+ };
310
+ readonly rule: {
311
+ readonly type: "string";
312
+ readonly description: "Specific rule code to bypass (e.g., \"E009\"). Defaults to \"all\"";
313
+ };
314
+ readonly status: {
315
+ readonly type: "boolean";
316
+ readonly description: "Show active bypasses and remaining budget";
317
+ readonly default: false;
318
+ };
319
+ readonly json: {
320
+ readonly type: "boolean";
321
+ readonly description: "Output as LAFS JSON envelope (agent-friendly)";
322
+ readonly default: false;
323
+ };
324
+ readonly human: {
325
+ readonly type: "boolean";
326
+ readonly description: "Output as formatted text (default for TTY)";
327
+ readonly default: false;
328
+ };
329
+ readonly quiet: {
330
+ readonly type: "boolean";
331
+ readonly description: "Suppress non-essential output";
332
+ readonly default: false;
333
+ };
334
+ }>;
335
+
180
336
  /**
181
337
  * A single error entry within a file group.
182
338
  * @public
@@ -504,6 +660,305 @@ declare const initDocsCommand: citty.CommandDef<{
504
660
  };
505
661
  }>;
506
662
 
663
+ /**
664
+ * Detected hook manager in the project.
665
+ * @public
666
+ */
667
+ type HookManager = "husky" | "lefthook" | "none";
668
+ /**
669
+ * Result of the `init hooks` command.
670
+ *
671
+ * @example
672
+ * ```typescript
673
+ * import { runInitHooks } from "@forge-ts/cli/commands/init-hooks";
674
+ * const output = await runInitHooks({ cwd: process.cwd() });
675
+ * console.log(output.data.hookManager); // "husky" | "lefthook" | "none"
676
+ * ```
677
+ * @public
678
+ */
679
+ interface InitHooksResult {
680
+ /** Whether the hook scaffolding succeeded. */
681
+ success: boolean;
682
+ /** The detected or chosen hook manager. */
683
+ hookManager: HookManager;
684
+ /** Summary of what was created. */
685
+ summary: {
686
+ /** Number of files written or updated. */
687
+ filesWritten: number;
688
+ /** Number of files skipped (already existed). */
689
+ filesSkipped: number;
690
+ };
691
+ /** Relative paths of all files written. */
692
+ files: string[];
693
+ /** Post-scaffold instructions for the user. */
694
+ instructions: string[];
695
+ }
696
+ /**
697
+ * Arguments for the `init hooks` command.
698
+ * @internal
699
+ */
700
+ interface InitHooksArgs {
701
+ /** Project root directory (default: cwd). */
702
+ cwd?: string;
703
+ /** Force overwrite existing hook files. */
704
+ force?: boolean;
705
+ /** MVI verbosity level for structured output. */
706
+ mvi?: string;
707
+ }
708
+ /**
709
+ * Scaffolds git hook integration for the project.
710
+ *
711
+ * Detects the hook manager (husky or lefthook), generates appropriate
712
+ * hook files, and reports what was written.
713
+ *
714
+ * @param args - CLI arguments for the init hooks command.
715
+ * @returns A typed `CommandOutput<InitHooksResult>`.
716
+ * @example
717
+ * ```typescript
718
+ * import { runInitHooks } from "@forge-ts/cli/commands/init-hooks";
719
+ * const output = await runInitHooks({ cwd: "/my/project" });
720
+ * console.log(output.data.files); // [".husky/pre-commit"]
721
+ * ```
722
+ * @public
723
+ */
724
+ declare function runInitHooks(args: InitHooksArgs): Promise<CommandOutput<InitHooksResult>>;
725
+ /**
726
+ * Citty command definition for `forge-ts init hooks`.
727
+ *
728
+ * Scaffolds git hook integration for the project by detecting the
729
+ * hook manager (husky or lefthook) and generating pre-commit hooks.
730
+ *
731
+ * @example
732
+ * ```typescript
733
+ * import { initHooksCommand } from "@forge-ts/cli/commands/init-hooks";
734
+ * // Registered as a subcommand of `forge-ts init`
735
+ * ```
736
+ * @public
737
+ */
738
+ declare const initHooksCommand: citty.CommandDef<{
739
+ readonly cwd: {
740
+ readonly type: "string";
741
+ readonly description: "Project root directory";
742
+ };
743
+ readonly force: {
744
+ readonly type: "boolean";
745
+ readonly description: "Overwrite existing hook files";
746
+ readonly default: false;
747
+ };
748
+ readonly json: {
749
+ readonly type: "boolean";
750
+ readonly description: "Output as LAFS JSON envelope";
751
+ readonly default: false;
752
+ };
753
+ readonly human: {
754
+ readonly type: "boolean";
755
+ readonly description: "Output as formatted text";
756
+ readonly default: false;
757
+ };
758
+ readonly quiet: {
759
+ readonly type: "boolean";
760
+ readonly description: "Suppress non-essential output";
761
+ readonly default: false;
762
+ };
763
+ readonly mvi: {
764
+ readonly type: "string";
765
+ readonly description: "MVI verbosity level: minimal, standard, full";
766
+ };
767
+ }>;
768
+
769
+ /**
770
+ * Typed result for the `lock` command.
771
+ * @public
772
+ */
773
+ interface LockResult {
774
+ /** Whether the lock was successfully created. */
775
+ success: boolean;
776
+ /** Path to the lock file that was written. */
777
+ lockFile: string;
778
+ /** ISO-8601 timestamp when the lock was created. */
779
+ lockedAt: string;
780
+ /** Identifier of who created the lock. */
781
+ lockedBy: string;
782
+ /** Summary of what was locked. */
783
+ locked: {
784
+ /** Number of enforce rules captured. */
785
+ rules: number;
786
+ /** Whether tsconfig guard settings were captured. */
787
+ tsconfig: boolean;
788
+ /** Whether biome guard settings were captured. */
789
+ biome: boolean;
790
+ };
791
+ /** Whether a previous lock file was overwritten. */
792
+ overwrote: boolean;
793
+ }
794
+ /**
795
+ * Runs the lock command: reads current config and creates `.forge-lock.json`.
796
+ *
797
+ * @param args - CLI arguments for the lock command.
798
+ * @returns A typed `CommandOutput<LockResult>`.
799
+ * @example
800
+ * ```typescript
801
+ * import { runLock } from "@forge-ts/cli/commands/lock";
802
+ * const output = await runLock({ cwd: process.cwd() });
803
+ * console.log(output.data.locked.rules); // number of rules locked
804
+ * ```
805
+ * @public
806
+ */
807
+ declare function runLock(args: {
808
+ cwd?: string;
809
+ }): Promise<CommandOutput<LockResult>>;
810
+ /**
811
+ * Citty command definition for `forge-ts lock`.
812
+ * @public
813
+ */
814
+ declare const lockCommand: citty.CommandDef<{
815
+ readonly cwd: {
816
+ readonly type: "string";
817
+ readonly description: "Project root directory";
818
+ };
819
+ readonly json: {
820
+ readonly type: "boolean";
821
+ readonly description: "Output as LAFS JSON envelope (agent-friendly)";
822
+ readonly default: false;
823
+ };
824
+ readonly human: {
825
+ readonly type: "boolean";
826
+ readonly description: "Output as formatted text (default for TTY)";
827
+ readonly default: false;
828
+ };
829
+ readonly quiet: {
830
+ readonly type: "boolean";
831
+ readonly description: "Suppress non-essential output";
832
+ readonly default: false;
833
+ };
834
+ }>;
835
+
836
+ /**
837
+ * Typed result for the `prepublish` command.
838
+ *
839
+ * @example
840
+ * ```typescript
841
+ * import { runPrepublish } from "@forge-ts/cli/commands/prepublish";
842
+ * const output = await runPrepublish({ cwd: process.cwd() });
843
+ * console.log(output.data.check.success); // true if check passed
844
+ * console.log(output.data.build?.success); // true if build passed
845
+ * ```
846
+ * @public
847
+ */
848
+ interface PrepublishResult {
849
+ /** Whether both check and build passed. */
850
+ success: boolean;
851
+ /** Summary of the prepublish pipeline. */
852
+ summary: {
853
+ /** Number of pipeline steps run. */
854
+ steps: number;
855
+ /** Number of steps that passed. */
856
+ passed: number;
857
+ /** Number of steps that failed. */
858
+ failed: number;
859
+ /** Wall-clock duration of the entire pipeline in milliseconds. */
860
+ duration: number;
861
+ };
862
+ /** Result of the check step. */
863
+ check: {
864
+ /** Whether the check passed. */
865
+ success: boolean;
866
+ /** Error count from the check. */
867
+ errors: number;
868
+ /** Warning count from the check. */
869
+ warnings: number;
870
+ /** Duration of the check step in milliseconds. */
871
+ duration: number;
872
+ };
873
+ /** Result of the build step (absent if check failed and build was skipped). */
874
+ build?: {
875
+ /** Whether the build passed. */
876
+ success: boolean;
877
+ /** Number of build pipeline steps. */
878
+ steps: number;
879
+ /** Number of build steps that succeeded. */
880
+ succeeded: number;
881
+ /** Number of build steps that failed. */
882
+ failed: number;
883
+ /** Duration of the build step in milliseconds. */
884
+ duration: number;
885
+ };
886
+ /** If check failed, the reason build was skipped. */
887
+ skippedReason?: string;
888
+ }
889
+ /**
890
+ * Arguments for the `prepublish` command.
891
+ * @internal
892
+ */
893
+ interface PrepublishArgs {
894
+ /** Project root directory (default: cwd). */
895
+ cwd?: string;
896
+ /** Treat warnings as errors during the check step. */
897
+ strict?: boolean;
898
+ /** MVI verbosity level for structured output. */
899
+ mvi?: string;
900
+ }
901
+ /**
902
+ * Runs the prepublish safety gate: check then build.
903
+ *
904
+ * If the check step fails, the build step is skipped entirely.
905
+ * Both steps use the same project root (cwd).
906
+ *
907
+ * @param args - CLI arguments for the prepublish command.
908
+ * @returns A typed `CommandOutput<PrepublishResult>`.
909
+ * @example
910
+ * ```typescript
911
+ * import { runPrepublish } from "@forge-ts/cli/commands/prepublish";
912
+ * const output = await runPrepublish({ cwd: process.cwd() });
913
+ * if (!output.success) process.exit(1);
914
+ * ```
915
+ * @public
916
+ */
917
+ declare function runPrepublish(args: PrepublishArgs): Promise<CommandOutput<PrepublishResult>>;
918
+ /**
919
+ * Citty command definition for `forge-ts prepublish`.
920
+ *
921
+ * Runs check then build as a publish safety gate. Add to package.json as:
922
+ * `"prepublishOnly": "forge-ts prepublish"`
923
+ *
924
+ * @example
925
+ * ```typescript
926
+ * import { prepublishCommand } from "@forge-ts/cli/commands/prepublish";
927
+ * // Registered as `forge-ts prepublish`
928
+ * ```
929
+ * @public
930
+ */
931
+ declare const prepublishCommand: citty.CommandDef<{
932
+ readonly cwd: {
933
+ readonly type: "string";
934
+ readonly description: "Project root directory";
935
+ };
936
+ readonly strict: {
937
+ readonly type: "boolean";
938
+ readonly description: "Treat warnings as errors during check";
939
+ readonly default: false;
940
+ };
941
+ readonly json: {
942
+ readonly type: "boolean";
943
+ readonly description: "Output as LAFS JSON envelope (agent-friendly)";
944
+ readonly default: false;
945
+ };
946
+ readonly human: {
947
+ readonly type: "boolean";
948
+ readonly description: "Output as formatted text (default for TTY)";
949
+ readonly default: false;
950
+ };
951
+ readonly quiet: {
952
+ readonly type: "boolean";
953
+ readonly description: "Suppress non-essential output";
954
+ readonly default: false;
955
+ };
956
+ readonly mvi: {
957
+ readonly type: "string";
958
+ readonly description: "MVI verbosity level: minimal, standard, full";
959
+ };
960
+ }>;
961
+
507
962
  /**
508
963
  * A single test failure entry, included at standard and full MVI levels.
509
964
  * @public
@@ -569,6 +1024,68 @@ declare const testCommand: citty.CommandDef<{
569
1024
  };
570
1025
  }>;
571
1026
 
1027
+ /**
1028
+ * Typed result for the `unlock` command.
1029
+ * @public
1030
+ */
1031
+ interface UnlockResult {
1032
+ /** Whether the unlock was successful. */
1033
+ success: boolean;
1034
+ /** The reason provided for unlocking. */
1035
+ reason: string;
1036
+ /** Who originally locked the config, if known. */
1037
+ previousLockedBy: string | null;
1038
+ /** When the config was originally locked, if known. */
1039
+ previousLockedAt: string | null;
1040
+ }
1041
+ /**
1042
+ * Runs the unlock command: removes `.forge-lock.json` with a mandatory reason.
1043
+ *
1044
+ * @param args - CLI arguments for the unlock command.
1045
+ * @returns A typed `CommandOutput<UnlockResult>`.
1046
+ * @example
1047
+ * ```typescript
1048
+ * import { runUnlock } from "@forge-ts/cli/commands/unlock";
1049
+ * const output = await runUnlock({ cwd: process.cwd(), reason: "Relaxing rules for migration" });
1050
+ * console.log(output.data.success); // true
1051
+ * ```
1052
+ * @public
1053
+ */
1054
+ declare function runUnlock(args: {
1055
+ cwd?: string;
1056
+ reason: string;
1057
+ }): Promise<CommandOutput<UnlockResult>>;
1058
+ /**
1059
+ * Citty command definition for `forge-ts unlock`.
1060
+ * @public
1061
+ */
1062
+ declare const unlockCommand: citty.CommandDef<{
1063
+ readonly cwd: {
1064
+ readonly type: "string";
1065
+ readonly description: "Project root directory";
1066
+ };
1067
+ readonly reason: {
1068
+ readonly type: "string";
1069
+ readonly description: "Mandatory reason for unlocking (audit trail)";
1070
+ readonly required: true;
1071
+ };
1072
+ readonly json: {
1073
+ readonly type: "boolean";
1074
+ readonly description: "Output as LAFS JSON envelope (agent-friendly)";
1075
+ readonly default: false;
1076
+ };
1077
+ readonly human: {
1078
+ readonly type: "boolean";
1079
+ readonly description: "Output as formatted text (default for TTY)";
1080
+ readonly default: false;
1081
+ };
1082
+ readonly quiet: {
1083
+ readonly type: "boolean";
1084
+ readonly description: "Suppress non-essential output";
1085
+ readonly default: false;
1086
+ };
1087
+ }>;
1088
+
572
1089
  /**
573
1090
  * Simple TTY-aware logger for forge-ts CLI output.
574
1091
  *
@@ -611,4 +1128,4 @@ declare function createLogger(options?: {
611
1128
  colors?: boolean;
612
1129
  }): Logger;
613
1130
 
614
- export { type BuildResult, type BuildStep, type CheckFileError, type CheckFileGroup, type CheckFileWarning, type CheckPage, type CheckResult, type CheckRuleCount, type CheckTriage, type CommandOutput, type ForgeCliError, type ForgeCliWarning, type InitDocsResult, type Logger, type OutputFlags, type TestFailure, type TestResult, buildCommand, checkCommand, createLogger, docsDevCommand, emitResult, initDocsCommand, resolveExitCode, runDocsDev, testCommand };
1131
+ export { type AuditResult, type BuildResult, type BuildStep, type BypassCreateResult, type BypassStatusResult, type CheckFileError, type CheckFileGroup, type CheckFileWarning, type CheckPage, type CheckResult, type CheckRuleCount, type CheckTriage, type CommandOutput, type ForgeCliError, type ForgeCliWarning, type HookManager, type InitDocsResult, type InitHooksResult, type LockResult, type Logger, type OutputFlags, type PrepublishResult, type TestFailure, type TestResult, type UnlockResult, auditCommand, buildCommand, bypassCommand, checkCommand, createLogger, docsDevCommand, emitResult, initDocsCommand, initHooksCommand, lockCommand, prepublishCommand, resolveExitCode, runBypassCreate, runBypassStatus, runDocsDev, runInitHooks, runLock, runPrepublish, runUnlock, testCommand, unlockCommand };