bilt-toolkit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +168 -0
  3. package/bin/bilt.js +2 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +161 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/doctor.d.ts +13 -0
  9. package/dist/commands/doctor.d.ts.map +1 -0
  10. package/dist/commands/doctor.js +194 -0
  11. package/dist/commands/doctor.js.map +1 -0
  12. package/dist/commands/fix.d.ts +12 -0
  13. package/dist/commands/fix.d.ts.map +1 -0
  14. package/dist/commands/fix.js +273 -0
  15. package/dist/commands/fix.js.map +1 -0
  16. package/dist/commands/init.d.ts +11 -0
  17. package/dist/commands/init.d.ts.map +1 -0
  18. package/dist/commands/init.js +101 -0
  19. package/dist/commands/init.js.map +1 -0
  20. package/dist/commands/scan.d.ts +17 -0
  21. package/dist/commands/scan.d.ts.map +1 -0
  22. package/dist/commands/scan.js +267 -0
  23. package/dist/commands/scan.js.map +1 -0
  24. package/dist/commands/undo.d.ts +12 -0
  25. package/dist/commands/undo.d.ts.map +1 -0
  26. package/dist/commands/undo.js +90 -0
  27. package/dist/commands/undo.js.map +1 -0
  28. package/dist/commands/watch.d.ts +10 -0
  29. package/dist/commands/watch.d.ts.map +1 -0
  30. package/dist/commands/watch.js +68 -0
  31. package/dist/commands/watch.js.map +1 -0
  32. package/dist/config/config.d.ts +8 -0
  33. package/dist/config/config.d.ts.map +1 -0
  34. package/dist/config/config.js +75 -0
  35. package/dist/config/config.js.map +1 -0
  36. package/dist/core/fix/confirm.d.ts +21 -0
  37. package/dist/core/fix/confirm.d.ts.map +1 -0
  38. package/dist/core/fix/confirm.js +60 -0
  39. package/dist/core/fix/confirm.js.map +1 -0
  40. package/dist/core/fix/env-fix.d.ts +40 -0
  41. package/dist/core/fix/env-fix.d.ts.map +1 -0
  42. package/dist/core/fix/env-fix.js +186 -0
  43. package/dist/core/fix/env-fix.js.map +1 -0
  44. package/dist/core/fix/snapshot.d.ts +30 -0
  45. package/dist/core/fix/snapshot.d.ts.map +1 -0
  46. package/dist/core/fix/snapshot.js +186 -0
  47. package/dist/core/fix/snapshot.js.map +1 -0
  48. package/dist/core/rules/entropy.d.ts +29 -0
  49. package/dist/core/rules/entropy.d.ts.map +1 -0
  50. package/dist/core/rules/entropy.js +117 -0
  51. package/dist/core/rules/entropy.js.map +1 -0
  52. package/dist/core/rules/providers.d.ts +12 -0
  53. package/dist/core/rules/providers.d.ts.map +1 -0
  54. package/dist/core/rules/providers.js +184 -0
  55. package/dist/core/rules/providers.js.map +1 -0
  56. package/dist/core/rules/secret-rules.d.ts +3 -0
  57. package/dist/core/rules/secret-rules.d.ts.map +1 -0
  58. package/dist/core/rules/secret-rules.js +205 -0
  59. package/dist/core/rules/secret-rules.js.map +1 -0
  60. package/dist/core/scan/env.d.ts +47 -0
  61. package/dist/core/scan/env.d.ts.map +1 -0
  62. package/dist/core/scan/env.js +301 -0
  63. package/dist/core/scan/env.js.map +1 -0
  64. package/dist/core/scan/framework.d.ts +26 -0
  65. package/dist/core/scan/framework.d.ts.map +1 -0
  66. package/dist/core/scan/framework.js +254 -0
  67. package/dist/core/scan/framework.js.map +1 -0
  68. package/dist/core/scan/gitignore.d.ts +20 -0
  69. package/dist/core/scan/gitignore.d.ts.map +1 -0
  70. package/dist/core/scan/gitignore.js +217 -0
  71. package/dist/core/scan/gitignore.js.map +1 -0
  72. package/dist/core/scan/secrets.d.ts +27 -0
  73. package/dist/core/scan/secrets.d.ts.map +1 -0
  74. package/dist/core/scan/secrets.js +247 -0
  75. package/dist/core/scan/secrets.js.map +1 -0
  76. package/dist/core/score/health.d.ts +29 -0
  77. package/dist/core/score/health.d.ts.map +1 -0
  78. package/dist/core/score/health.js +73 -0
  79. package/dist/core/score/health.js.map +1 -0
  80. package/dist/core/watch/watcher.d.ts +29 -0
  81. package/dist/core/watch/watcher.d.ts.map +1 -0
  82. package/dist/core/watch/watcher.js +176 -0
  83. package/dist/core/watch/watcher.js.map +1 -0
  84. package/dist/index.d.ts +2 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +2 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/plugins/interface.d.ts +12 -0
  89. package/dist/plugins/interface.d.ts.map +1 -0
  90. package/dist/plugins/interface.js +91 -0
  91. package/dist/plugins/interface.js.map +1 -0
  92. package/dist/plugins/loader.d.ts +10 -0
  93. package/dist/plugins/loader.d.ts.map +1 -0
  94. package/dist/plugins/loader.js +104 -0
  95. package/dist/plugins/loader.js.map +1 -0
  96. package/dist/plugins/official/docker.d.ts +4 -0
  97. package/dist/plugins/official/docker.d.ts.map +1 -0
  98. package/dist/plugins/official/docker.js +111 -0
  99. package/dist/plugins/official/docker.js.map +1 -0
  100. package/dist/plugins/official/terraform.d.ts +4 -0
  101. package/dist/plugins/official/terraform.d.ts.map +1 -0
  102. package/dist/plugins/official/terraform.js +131 -0
  103. package/dist/plugins/official/terraform.js.map +1 -0
  104. package/dist/types/index.d.ts +158 -0
  105. package/dist/types/index.d.ts.map +1 -0
  106. package/dist/types/index.js +3 -0
  107. package/dist/types/index.js.map +1 -0
  108. package/dist/ui/format.d.ts +43 -0
  109. package/dist/ui/format.d.ts.map +1 -0
  110. package/dist/ui/format.js +207 -0
  111. package/dist/ui/format.js.map +1 -0
  112. package/dist/ui/reporter.d.ts +39 -0
  113. package/dist/ui/reporter.d.ts.map +1 -0
  114. package/dist/ui/reporter.js +254 -0
  115. package/dist/ui/reporter.js.map +1 -0
  116. package/package.json +65 -0
@@ -0,0 +1,60 @@
1
+ // ─── Fix Confirmation Prompts ────────────────────────────────────────────────
2
+ //
3
+ // Interactive prompts to confirm destructive or irreversible fix actions.
4
+ // Uses enquirer for terminal prompts.
5
+ //
6
+ // Three tiers:
7
+ // 1. Safe actions → no prompt, returns true immediately.
8
+ // 2. Destructive → simple y/n confirmation.
9
+ // 3. Irreversible → user must type the filename to confirm.
10
+ // ─────────────────────────────────────────────────────────────────────────────
11
+ import Enquirer from "enquirer";
12
+ // enquirer's CJS default export needs this accessor in ESM
13
+ const enquirer = new Enquirer();
14
+ // ─── Public API ──────────────────────────────────────────────────────────────
15
+ /**
16
+ * Prompt the user to type the filename to confirm an **irreversible** action.
17
+ *
18
+ * Used for actions like permanently deleting a file or rewriting git history
19
+ * where a simple y/n is not enough assurance.
20
+ *
21
+ * @param action The fix action being applied.
22
+ * @param fileName The filename the user must type to confirm.
23
+ * @returns `true` if the user typed the correct filename, `false` otherwise.
24
+ */
25
+ export async function requireTypedConfirmation(action, fileName) {
26
+ // Safe actions and test environment need no confirmation
27
+ if (action.type === "safe" ||
28
+ process.env.NODE_ENV === "test" ||
29
+ process.env.VITEST)
30
+ return true;
31
+ const response = (await enquirer.prompt({
32
+ type: "input",
33
+ name: "confirm",
34
+ message: `⚠️ This action is IRREVERSIBLE: ${action.description}\n` +
35
+ ` Type "${fileName}" to confirm:`,
36
+ }));
37
+ return response.confirm.trim() === fileName;
38
+ }
39
+ /**
40
+ * Prompt the user for a simple y/n confirmation for a **destructive**
41
+ * action.
42
+ *
43
+ * @param action The fix action being applied.
44
+ * @returns `true` if the user confirms, `false` otherwise.
45
+ */
46
+ export async function requireSimpleConfirmation(action) {
47
+ // Safe actions and test environment need no confirmation
48
+ if (action.type === "safe" ||
49
+ process.env.NODE_ENV === "test" ||
50
+ process.env.VITEST)
51
+ return true;
52
+ const response = (await enquirer.prompt({
53
+ type: "confirm",
54
+ name: "confirm",
55
+ message: `Apply fix: ${action.description}?`,
56
+ initial: false,
57
+ }));
58
+ return response.confirm;
59
+ }
60
+ //# sourceMappingURL=confirm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../../src/core/fix/confirm.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,0EAA0E;AAC1E,sCAAsC;AACtC,EAAE;AACF,eAAe;AACf,gEAAgE;AAChE,oDAAoD;AACpD,mEAAmE;AACnE,gFAAgF;AAEhF,OAAO,QAAQ,MAAM,UAAU,CAAC;AAGhC,2DAA2D;AAC3D,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;AAEhC,gFAAgF;AAEhF;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,MAAiB,EACjB,QAAgB;IAEhB,yDAAyD;IACzD,IACE,MAAM,CAAC,IAAI,KAAK,MAAM;QACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM;QAC/B,OAAO,CAAC,GAAG,CAAC,MAAM;QAElB,OAAO,IAAI,CAAC;IAEd,MAAM,QAAQ,GAAG,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC;QACtC,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,SAAS;QACf,OAAO,EACL,oCAAoC,MAAM,CAAC,WAAW,IAAI;YAC1D,YAAY,QAAQ,eAAe;KACtC,CAAC,CAAwB,CAAC;IAE3B,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,QAAQ,CAAC;AAC9C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,MAAiB;IAEjB,yDAAyD;IACzD,IACE,MAAM,CAAC,IAAI,KAAK,MAAM;QACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM;QAC/B,OAAO,CAAC,GAAG,CAAC,MAAM;QAElB,OAAO,IAAI,CAAC;IAEd,MAAM,QAAQ,GAAG,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC;QACtC,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,cAAc,MAAM,CAAC,WAAW,GAAG;QAC5C,OAAO,EAAE,KAAK;KACf,CAAC,CAAyB,CAAC;IAE5B,OAAO,QAAQ,CAAC,OAAO,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,40 @@
1
+ import type { ParsedEnvFile, SecretRule } from "../../types/index.js";
2
+ /**
3
+ * Generate an `.env.example` file from a parsed env file.
4
+ *
5
+ * Secret values are replaced with descriptive placeholders while
6
+ * preserving the overall structure — comments, blank lines, ordering,
7
+ * and non-secret values are kept intact.
8
+ *
9
+ * @param envFile Parsed env file to use as the template.
10
+ * @param rules Secret rules for value classification.
11
+ * @param entropyThreshold Entropy threshold for detecting secrets.
12
+ * @returns The generated `.env.example` content as a string.
13
+ */
14
+ export declare function generateEnvExample(envFile: ParsedEnvFile, rules: SecretRule[], entropyThreshold: number): string;
15
+ /**
16
+ * Add entries to a `.gitignore` file, skipping any that already exist.
17
+ *
18
+ * Reads the current content from disk, appends missing entries with a
19
+ * header comment, and returns the new content string.
20
+ *
21
+ * **Does NOT write to disk** — the caller is responsible for writing
22
+ * (this enables dry-run mode).
23
+ *
24
+ * @param entries Patterns to add (e.g. `.env`, `.env.local`).
25
+ * @param gitignorePath Absolute path to the `.gitignore` file.
26
+ * @returns The updated `.gitignore` content.
27
+ */
28
+ export declare function addToGitignore(entries: string[], gitignorePath: string): Promise<string>;
29
+ /**
30
+ * Append stub entries for missing env vars to existing env file content.
31
+ *
32
+ * Adds a comment header and empty variable declarations for each
33
+ * missing variable.
34
+ *
35
+ * @param targetContent Existing env file content.
36
+ * @param missingVars Variable names to add.
37
+ * @returns Updated env file content with missing vars appended.
38
+ */
39
+ export declare function addMissingEnvVars(targetContent: string, missingVars: string[]): string;
40
+ //# sourceMappingURL=env-fix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-fix.d.ts","sourceRoot":"","sources":["../../../src/core/fix/env-fix.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AA6DtE;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,UAAU,EAAE,EACnB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAiDR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CAmCjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EAAE,GACpB,MAAM,CAoBR"}
@@ -0,0 +1,186 @@
1
+ // ─── Env Fix Utilities ───────────────────────────────────────────────────────
2
+ //
3
+ // Automated fixes for common .env-related findings:
4
+ // • Generate a safe .env.example with secrets stripped
5
+ // • Add missing entries to .gitignore
6
+ // • Append stub entries for missing env vars
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+ import fs from "node:fs/promises";
9
+ import { isHighEntropy } from "../rules/entropy.js";
10
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
11
+ /**
12
+ * Check if a value matches any secret rule pattern.
13
+ */
14
+ function matchesSecretRule(value, rules) {
15
+ for (const rule of rules) {
16
+ rule.pattern.lastIndex = 0;
17
+ if (rule.pattern.test(value))
18
+ return true;
19
+ }
20
+ return false;
21
+ }
22
+ /**
23
+ * Determine if a value should be treated as a secret and therefore
24
+ * stripped from the example file.
25
+ */
26
+ function isSecretValue(value, rules, entropyThreshold) {
27
+ if (!value || value.length === 0)
28
+ return false;
29
+ if (matchesSecretRule(value, rules))
30
+ return true;
31
+ if (isHighEntropy(value, entropyThreshold))
32
+ return true;
33
+ return false;
34
+ }
35
+ /**
36
+ * Generate an appropriate placeholder based on the variable name.
37
+ */
38
+ function placeholderFor(key) {
39
+ const lower = key.toLowerCase();
40
+ if (lower.includes("url") || lower.includes("uri"))
41
+ return "https://example.com";
42
+ if (lower.includes("host"))
43
+ return "localhost";
44
+ if (lower.includes("port"))
45
+ return "3000";
46
+ if (lower.includes("email") || lower.includes("mail"))
47
+ return "user@example.com";
48
+ if (lower.includes("password") || lower.includes("passwd"))
49
+ return "your-password-here";
50
+ if (lower.includes("key") ||
51
+ lower.includes("token") ||
52
+ lower.includes("secret"))
53
+ return "your-secret-here";
54
+ if (lower.includes("database") || lower.includes("db"))
55
+ return "your-database-here";
56
+ if (lower.includes("region"))
57
+ return "us-east-1";
58
+ if (lower.includes("bucket"))
59
+ return "your-bucket-name";
60
+ if (lower.includes("name"))
61
+ return "your-name-here";
62
+ return "your-value-here";
63
+ }
64
+ // ─── Public API ──────────────────────────────────────────────────────────────
65
+ /**
66
+ * Generate an `.env.example` file from a parsed env file.
67
+ *
68
+ * Secret values are replaced with descriptive placeholders while
69
+ * preserving the overall structure — comments, blank lines, ordering,
70
+ * and non-secret values are kept intact.
71
+ *
72
+ * @param envFile Parsed env file to use as the template.
73
+ * @param rules Secret rules for value classification.
74
+ * @param entropyThreshold Entropy threshold for detecting secrets.
75
+ * @returns The generated `.env.example` content as a string.
76
+ */
77
+ export function generateEnvExample(envFile, rules, entropyThreshold) {
78
+ const outputLines = [];
79
+ // Process each raw line to preserve structure
80
+ for (const rawLine of envFile.rawLines) {
81
+ const trimmed = rawLine.trim();
82
+ // Preserve empty lines and comments as-is
83
+ if (trimmed === "" || trimmed.startsWith("#")) {
84
+ outputLines.push(rawLine);
85
+ continue;
86
+ }
87
+ // Strip optional `export ` prefix for parsing
88
+ const line = trimmed.startsWith("export ")
89
+ ? trimmed.slice(7).trim()
90
+ : trimmed;
91
+ const eqIdx = line.indexOf("=");
92
+ if (eqIdx === -1) {
93
+ // Not a key=value line — preserve as-is
94
+ outputLines.push(rawLine);
95
+ continue;
96
+ }
97
+ const key = line.slice(0, eqIdx).trim();
98
+ const entry = envFile.entries.get(key);
99
+ if (!entry) {
100
+ // Key not in entries map (shouldn't happen) — preserve line
101
+ outputLines.push(rawLine);
102
+ continue;
103
+ }
104
+ // Determine leading whitespace / export prefix for reconstruction
105
+ const prefix = trimmed.startsWith("export ") ? "export " : "";
106
+ if (isSecretValue(entry.value, rules, entropyThreshold)) {
107
+ // Replace the value with a placeholder
108
+ const placeholder = placeholderFor(key);
109
+ const comment = entry.comment ? ` # ${entry.comment}` : "";
110
+ outputLines.push(`${prefix}${key}=${placeholder}${comment}`);
111
+ }
112
+ else {
113
+ // Keep the original line
114
+ outputLines.push(rawLine);
115
+ }
116
+ }
117
+ return outputLines.join("\n");
118
+ }
119
+ /**
120
+ * Add entries to a `.gitignore` file, skipping any that already exist.
121
+ *
122
+ * Reads the current content from disk, appends missing entries with a
123
+ * header comment, and returns the new content string.
124
+ *
125
+ * **Does NOT write to disk** — the caller is responsible for writing
126
+ * (this enables dry-run mode).
127
+ *
128
+ * @param entries Patterns to add (e.g. `.env`, `.env.local`).
129
+ * @param gitignorePath Absolute path to the `.gitignore` file.
130
+ * @returns The updated `.gitignore` content.
131
+ */
132
+ export async function addToGitignore(entries, gitignorePath) {
133
+ let currentContent = "";
134
+ try {
135
+ currentContent = await fs.readFile(gitignorePath, "utf-8");
136
+ }
137
+ catch {
138
+ // File doesn't exist yet — start fresh
139
+ }
140
+ const existingLines = new Set(currentContent
141
+ .split("\n")
142
+ .map((l) => l.trim())
143
+ .filter((l) => l.length > 0 && !l.startsWith("#")));
144
+ const toAdd = entries.filter((entry) => !existingLines.has(entry));
145
+ if (toAdd.length === 0)
146
+ return currentContent;
147
+ // Build the addition block
148
+ const lines = [];
149
+ // Add a newline separator if the file doesn't end with one
150
+ if (currentContent.length > 0 && !currentContent.endsWith("\n")) {
151
+ lines.push("");
152
+ }
153
+ lines.push("# Added by bilt — environment files");
154
+ for (const entry of toAdd) {
155
+ lines.push(entry);
156
+ }
157
+ lines.push(""); // trailing newline
158
+ return currentContent + lines.join("\n");
159
+ }
160
+ /**
161
+ * Append stub entries for missing env vars to existing env file content.
162
+ *
163
+ * Adds a comment header and empty variable declarations for each
164
+ * missing variable.
165
+ *
166
+ * @param targetContent Existing env file content.
167
+ * @param missingVars Variable names to add.
168
+ * @returns Updated env file content with missing vars appended.
169
+ */
170
+ export function addMissingEnvVars(targetContent, missingVars) {
171
+ if (missingVars.length === 0)
172
+ return targetContent;
173
+ const lines = [];
174
+ // Ensure we start on a new line
175
+ if (targetContent.length > 0 && !targetContent.endsWith("\n")) {
176
+ lines.push("");
177
+ }
178
+ lines.push("");
179
+ lines.push("# Added by bilt — missing variables");
180
+ for (const varName of missingVars) {
181
+ lines.push(`${varName}=`);
182
+ }
183
+ lines.push(""); // trailing newline
184
+ return targetContent + lines.join("\n");
185
+ }
186
+ //# sourceMappingURL=env-fix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-fix.js","sourceRoot":"","sources":["../../../src/core/fix/env-fix.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { Snapshot } from "../../types/index.js";
2
+ /**
3
+ * Create a snapshot of the specified files.
4
+ *
5
+ * Copies each file into `.bilt/snapshots/<id>/files/` preserving relative
6
+ * paths, and writes a `manifest.json` describing the snapshot.
7
+ *
8
+ * @param files Absolute paths of files to snapshot.
9
+ * @param description Human-readable description (e.g. "Before gitignore fix").
10
+ * @param projectDir Root directory of the project.
11
+ * @returns The created Snapshot.
12
+ */
13
+ export declare function createSnapshot(files: string[], description: string, projectDir: string): Promise<Snapshot>;
14
+ /**
15
+ * List all existing snapshots, sorted by timestamp (newest first).
16
+ */
17
+ export declare function listSnapshots(projectDir: string): Promise<Snapshot[]>;
18
+ /**
19
+ * Get the most recent snapshot, or `null` if none exist.
20
+ */
21
+ export declare function getLatestSnapshot(projectDir: string): Promise<Snapshot | null>;
22
+ /**
23
+ * Restore files from a snapshot back to their original locations.
24
+ *
25
+ * @param snapshotId The snapshot ID to restore.
26
+ * @param projectDir Root directory of the project.
27
+ * @returns `true` if the snapshot was found and restored, `false` otherwise.
28
+ */
29
+ export declare function restoreSnapshot(snapshotId: string, projectDir: string): Promise<boolean>;
30
+ //# sourceMappingURL=snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../../src/core/fix/snapshot.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,QAAQ,EAGT,MAAM,sBAAsB,CAAC;AAkF9B;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EAAE,EACf,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,CAAC,CAiDnB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAmC3E;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAG1B;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAelB"}
@@ -0,0 +1,186 @@
1
+ // ─── Snapshot System ─────────────────────────────────────────────────────────
2
+ //
3
+ // Before applying destructive or irreversible fixes, Bilt creates a
4
+ // snapshot of the affected files so the user can roll back. Snapshots
5
+ // are stored under `.bilt/snapshots/<id>/` with a `manifest.json` and
6
+ // copies of every affected file.
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+ import fs from "node:fs/promises";
9
+ import path from "node:path";
10
+ import crypto from "node:crypto";
11
+ // ─── Constants ───────────────────────────────────────────────────────────────
12
+ const SNAPSHOTS_DIR = ".bilt/snapshots";
13
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
14
+ /**
15
+ * Resolve the snapshots root directory for a project.
16
+ */
17
+ function snapshotsRoot(projectDir) {
18
+ return path.join(projectDir, SNAPSHOTS_DIR);
19
+ }
20
+ /**
21
+ * Resolve the directory for a specific snapshot.
22
+ */
23
+ function snapshotDir(projectDir, id) {
24
+ return path.join(snapshotsRoot(projectDir), id);
25
+ }
26
+ /**
27
+ * Generate a short, unique snapshot ID based on timestamp + random suffix.
28
+ */
29
+ function generateId() {
30
+ const ts = new Date().toISOString().replace(/[-:T]/g, "").slice(0, 14); // YYYYMMDDHHmmss
31
+ const rand = crypto.randomBytes(4).toString("hex");
32
+ return `${ts}-${rand}`;
33
+ }
34
+ /**
35
+ * Ensure a directory exists, creating it (and parents) if necessary.
36
+ */
37
+ async function ensureDir(dir) {
38
+ await fs.mkdir(dir, { recursive: true });
39
+ }
40
+ /**
41
+ * Read the manifest.json from a snapshot directory.
42
+ * Returns null if the manifest is missing or invalid.
43
+ */
44
+ async function readManifest(dir) {
45
+ try {
46
+ const raw = await fs.readFile(path.join(dir, "manifest.json"), "utf-8");
47
+ return JSON.parse(raw);
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ /**
54
+ * Convert a manifest + its on-disk files into a full `Snapshot` object.
55
+ */
56
+ async function hydrateSnapshot(manifest, dir) {
57
+ const files = [];
58
+ for (const relativePath of manifest.filePaths) {
59
+ try {
60
+ const content = await fs.readFile(path.join(dir, "files", relativePath), "utf-8");
61
+ files.push({ path: relativePath, content });
62
+ }
63
+ catch {
64
+ // File may have been deleted from the snapshot store — skip
65
+ }
66
+ }
67
+ return {
68
+ id: manifest.id,
69
+ timestamp: new Date(manifest.timestamp),
70
+ description: manifest.description,
71
+ files,
72
+ };
73
+ }
74
+ // ─── Public API ──────────────────────────────────────────────────────────────
75
+ /**
76
+ * Create a snapshot of the specified files.
77
+ *
78
+ * Copies each file into `.bilt/snapshots/<id>/files/` preserving relative
79
+ * paths, and writes a `manifest.json` describing the snapshot.
80
+ *
81
+ * @param files Absolute paths of files to snapshot.
82
+ * @param description Human-readable description (e.g. "Before gitignore fix").
83
+ * @param projectDir Root directory of the project.
84
+ * @returns The created Snapshot.
85
+ */
86
+ export async function createSnapshot(files, description, projectDir) {
87
+ const id = generateId();
88
+ const dir = snapshotDir(projectDir, id);
89
+ const filesDir = path.join(dir, "files");
90
+ await ensureDir(filesDir);
91
+ const snapshotFiles = [];
92
+ const filePaths = [];
93
+ for (const absPath of files) {
94
+ const relativePath = path.relative(projectDir, absPath).replace(/\\/g, "/");
95
+ try {
96
+ const content = await fs.readFile(absPath, "utf-8");
97
+ // Preserve directory structure inside the snapshot
98
+ const destPath = path.join(filesDir, relativePath);
99
+ await ensureDir(path.dirname(destPath));
100
+ await fs.writeFile(destPath, content, "utf-8");
101
+ snapshotFiles.push({ path: relativePath, content });
102
+ filePaths.push(relativePath);
103
+ }
104
+ catch {
105
+ // File might not exist (e.g. about to be created) — skip
106
+ }
107
+ }
108
+ const timestamp = new Date();
109
+ // Write manifest
110
+ const manifest = {
111
+ id,
112
+ timestamp: timestamp.toISOString(),
113
+ description,
114
+ filePaths,
115
+ };
116
+ await fs.writeFile(path.join(dir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
117
+ return {
118
+ id,
119
+ timestamp,
120
+ description,
121
+ files: snapshotFiles,
122
+ };
123
+ }
124
+ /**
125
+ * List all existing snapshots, sorted by timestamp (newest first).
126
+ */
127
+ export async function listSnapshots(projectDir) {
128
+ const root = snapshotsRoot(projectDir);
129
+ let entries;
130
+ try {
131
+ entries = await fs.readdir(root);
132
+ }
133
+ catch {
134
+ // No snapshots directory — nothing to list
135
+ return [];
136
+ }
137
+ const snapshots = [];
138
+ for (const entry of entries) {
139
+ const dir = path.join(root, entry);
140
+ // Skip non-directories
141
+ try {
142
+ const stat = await fs.stat(dir);
143
+ if (!stat.isDirectory())
144
+ continue;
145
+ }
146
+ catch {
147
+ continue;
148
+ }
149
+ const manifest = await readManifest(dir);
150
+ if (!manifest)
151
+ continue;
152
+ const snapshot = await hydrateSnapshot(manifest, dir);
153
+ snapshots.push(snapshot);
154
+ }
155
+ // Newest first
156
+ snapshots.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
157
+ return snapshots;
158
+ }
159
+ /**
160
+ * Get the most recent snapshot, or `null` if none exist.
161
+ */
162
+ export async function getLatestSnapshot(projectDir) {
163
+ const all = await listSnapshots(projectDir);
164
+ return all[0] ?? null;
165
+ }
166
+ /**
167
+ * Restore files from a snapshot back to their original locations.
168
+ *
169
+ * @param snapshotId The snapshot ID to restore.
170
+ * @param projectDir Root directory of the project.
171
+ * @returns `true` if the snapshot was found and restored, `false` otherwise.
172
+ */
173
+ export async function restoreSnapshot(snapshotId, projectDir) {
174
+ const dir = snapshotDir(projectDir, snapshotId);
175
+ const manifest = await readManifest(dir);
176
+ if (!manifest)
177
+ return false;
178
+ const snapshot = await hydrateSnapshot(manifest, dir);
179
+ for (const file of snapshot.files) {
180
+ const destPath = path.join(projectDir, file.path);
181
+ await ensureDir(path.dirname(destPath));
182
+ await fs.writeFile(destPath, file.content, "utf-8");
183
+ }
184
+ return true;
185
+ }
186
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/core/fix/snapshot.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,oEAAoE;AACpE,uEAAuE;AACvE,sEAAsE;AACtE,iCAAiC;AACjC,gFAAgF;AAEhF,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AAOjC,gFAAgF;AAEhF,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAExC,gFAAgF;AAEhF;;GAEG;AACH,SAAS,aAAa,CAAC,UAAkB;IACvC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,UAAkB,EAAE,EAAU;IACjD,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,SAAS,UAAU;IACjB,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB;IACzF,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnD,OAAO,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,SAAS,CAAC,GAAW;IAClC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,YAAY,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,eAAe,CAC5B,QAA0B,EAC1B,GAAW;IAEX,MAAM,KAAK,GAAmB,EAAE,CAAC;IAEjC,KAAK,MAAM,YAAY,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAC/B,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,CAAC,EACrC,OAAO,CACR,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,4DAA4D;QAC9D,CAAC;IACH,CAAC;IAED,OAAO;QACL,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,SAAS,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;QACvC,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,KAAK;KACN,CAAC;AACJ,CAAC;AAED,gFAAgF;AAEhF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAe,EACf,WAAmB,EACnB,UAAkB;IAElB,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IACxB,MAAM,GAAG,GAAG,WAAW,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACzC,MAAM,SAAS,CAAC,QAAQ,CAAC,CAAC;IAE1B,MAAM,aAAa,GAAmB,EAAE,CAAC;IACzC,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAE5E,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAEpD,mDAAmD;YACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YACnD,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;YACxC,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAE/C,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC;YACpD,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,yDAAyD;QAC3D,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;IAE7B,iBAAiB;IACjB,MAAM,QAAQ,GAAqB;QACjC,EAAE;QACF,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE;QAClC,WAAW;QACX,SAAS;KACV,CAAC;IAEF,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,EAC/B,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EACjC,OAAO,CACR,CAAC;IAEF,OAAO;QACL,EAAE;QACF,SAAS;QACT,WAAW;QACX,KAAK,EAAE,aAAa;KACrB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB;IACpD,MAAM,IAAI,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAEvC,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,2CAA2C;QAC3C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,SAAS,GAAe,EAAE,CAAC;IAEjC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAEnC,uBAAuB;QACvB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;gBAAE,SAAS;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,CAAC,QAAQ;YAAE,SAAS;QAExB,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACtD,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3B,CAAC;IAED,eAAe;IACf,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;IAExE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC;IAC5C,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACxB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAAkB,EAClB,UAAkB;IAElB,MAAM,GAAG,GAAG,WAAW,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;IAEzC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAE5B,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAEtD,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QACxC,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Calculate the Shannon entropy (in bits-per-character) of a string.
3
+ *
4
+ * Shannon entropy is defined as:
5
+ * H = -Σ p(x) · log₂(p(x))
6
+ *
7
+ * where p(x) is the frequency of character x in the string.
8
+ *
9
+ * Higher values indicate more randomness — typical English prose scores
10
+ * around 3.5–4.0 while random hex/base64 strings score 4.5–6.0+.
11
+ */
12
+ export declare function calculateShannonEntropy(value: string): number;
13
+ /**
14
+ * Determine whether a value is "high entropy" — i.e. random enough to
15
+ * plausibly be a secret key / token.
16
+ *
17
+ * Before running the entropy check the function applies several
18
+ * heuristics to avoid false positives:
19
+ *
20
+ * 1. Values shorter than 8 characters are skipped (too short to be a key).
21
+ * 2. Pure UUID strings are skipped (high-entropy but not secret).
22
+ * 3. Values that match common configuration words are skipped.
23
+ *
24
+ * @param value The raw string value to analyse.
25
+ * @param threshold Minimum entropy (bits-per-char) to be considered
26
+ * "high". Defaults to **4.5**.
27
+ */
28
+ export declare function isHighEntropy(value: string, threshold?: number): boolean;
29
+ //# sourceMappingURL=entropy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entropy.d.ts","sourceRoot":"","sources":["../../../src/core/rules/entropy.ts"],"names":[],"mappings":"AA4DA;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAmB7D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAAM,GAAG,OAAO,CAgBrE"}