repomeld 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.
- package/README.md +147 -0
- package/bin/cli.js +311 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# repomeld 🔥
|
|
2
|
+
|
|
3
|
+
> Meld your entire repo into a single file — perfect for AI context, code reviews & sharing.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g repomeld
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd your-project
|
|
15
|
+
repomeld
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Creates `repomeld_output.txt` with all your files combined, a table of contents, and file metadata.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## All Options
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Usage: repomeld [options]
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
-V, --version Show version
|
|
29
|
+
-h, --help Show help
|
|
30
|
+
|
|
31
|
+
Output:
|
|
32
|
+
-o, --output <filename> Output file name (default: "repomeld_output.txt")
|
|
33
|
+
|
|
34
|
+
Filtering:
|
|
35
|
+
-e, --ext <exts...> Only include specific extensions
|
|
36
|
+
--include <patterns...> Only include files matching patterns
|
|
37
|
+
--exclude <patterns...> Exclude files matching patterns
|
|
38
|
+
-i, --ignore <names...> Extra folders/files to ignore
|
|
39
|
+
--max-size <kb> Skip files larger than N KB (default: 500)
|
|
40
|
+
|
|
41
|
+
Formatting:
|
|
42
|
+
-s, --style <style> Header style: banner | markdown | minimal (default: banner)
|
|
43
|
+
--no-toc Disable table of contents
|
|
44
|
+
--no-meta Hide file metadata (lines, size, lang)
|
|
45
|
+
--trim Trim leading/trailing whitespace per file
|
|
46
|
+
|
|
47
|
+
Advanced:
|
|
48
|
+
--lines-before <n> Skip first N lines of each file
|
|
49
|
+
--lines-after <n> Skip last N lines of each file
|
|
50
|
+
--dry-run Preview what would be included — don't write output
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Examples
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Basic usage
|
|
59
|
+
repomeld
|
|
60
|
+
|
|
61
|
+
# Only JavaScript and TypeScript files
|
|
62
|
+
repomeld --ext js ts jsx tsx
|
|
63
|
+
|
|
64
|
+
# Only files inside src/ folder
|
|
65
|
+
repomeld --include src/
|
|
66
|
+
|
|
67
|
+
# Exclude test files
|
|
68
|
+
repomeld --exclude test spec __tests__
|
|
69
|
+
|
|
70
|
+
# Markdown output style (great for AI prompts)
|
|
71
|
+
repomeld --style markdown --output context.md
|
|
72
|
+
|
|
73
|
+
# Minimal headers
|
|
74
|
+
repomeld --style minimal
|
|
75
|
+
|
|
76
|
+
# Custom output file
|
|
77
|
+
repomeld --output all_code.txt
|
|
78
|
+
|
|
79
|
+
# Skip large files (over 100 KB)
|
|
80
|
+
repomeld --max-size 100
|
|
81
|
+
|
|
82
|
+
# No table of contents, no metadata
|
|
83
|
+
repomeld --no-toc --no-meta
|
|
84
|
+
|
|
85
|
+
# Dry run — see what would be included without writing
|
|
86
|
+
repomeld --dry-run
|
|
87
|
+
|
|
88
|
+
# Ignore extra folders
|
|
89
|
+
repomeld --ignore dist .next coverage
|
|
90
|
+
|
|
91
|
+
# Combine options
|
|
92
|
+
repomeld --ext ts tsx --include src/ --style markdown --output ai_context.md
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Output Format (banner style — default)
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
# Generated by repomeld v1.0.0
|
|
101
|
+
# Date : 2024-01-01T00:00:00.000Z
|
|
102
|
+
# Source : /your/project
|
|
103
|
+
# Files : 12
|
|
104
|
+
# Lines : 843
|
|
105
|
+
|
|
106
|
+
TABLE OF CONTENTS
|
|
107
|
+
════════════════════════════════════════════════════════════
|
|
108
|
+
1. src/index.ts
|
|
109
|
+
2. src/utils/helper.ts
|
|
110
|
+
...
|
|
111
|
+
════════════════════════════════════════════════════════════
|
|
112
|
+
|
|
113
|
+
────────────────────────────────────────────────────────────
|
|
114
|
+
FILE: src/index.ts [42 lines | 1.2 KB | typescript]
|
|
115
|
+
────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
<file contents>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Output Format (markdown style)
|
|
121
|
+
|
|
122
|
+
````
|
|
123
|
+
## 📄 src/index.ts [42 lines | 1.2 KB | typescript]
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
<file contents>
|
|
127
|
+
```
|
|
128
|
+
````
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Auto-ignored (always skipped)
|
|
133
|
+
|
|
134
|
+
- `node_modules/`, `.git/`, `dist/`, `build/`, `.next/`, `.nuxt/`, `.cache/`
|
|
135
|
+
- `.env`, `.env.local`, `.env.production`
|
|
136
|
+
- `.DS_Store`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`
|
|
137
|
+
- Binary files (images, executables, etc.)
|
|
138
|
+
- The output file itself
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Publish / Update
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm version patch # 1.0.0 → 1.0.1
|
|
146
|
+
npm publish
|
|
147
|
+
```
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { program } = require("commander");
|
|
6
|
+
|
|
7
|
+
const VERSION = "1.0.0";
|
|
8
|
+
|
|
9
|
+
const DEFAULT_IGNORE = [
|
|
10
|
+
"node_modules",
|
|
11
|
+
".git",
|
|
12
|
+
".env",
|
|
13
|
+
".env.local",
|
|
14
|
+
".env.production",
|
|
15
|
+
".DS_Store",
|
|
16
|
+
"package-lock.json",
|
|
17
|
+
"yarn.lock",
|
|
18
|
+
"pnpm-lock.yaml",
|
|
19
|
+
".next",
|
|
20
|
+
".nuxt",
|
|
21
|
+
"dist",
|
|
22
|
+
"build",
|
|
23
|
+
".cache",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const LANGUAGE_MAP = {
|
|
27
|
+
js: "javascript", jsx: "javascript", ts: "typescript", tsx: "typescript",
|
|
28
|
+
py: "python", rb: "ruby", java: "java", cpp: "cpp", c: "c",
|
|
29
|
+
cs: "csharp", go: "go", rs: "rust", php: "php", swift: "swift",
|
|
30
|
+
kt: "kotlin", html: "html", css: "css", scss: "scss", json: "json",
|
|
31
|
+
yaml: "yaml", yml: "yaml", md: "markdown", sh: "bash", bash: "bash",
|
|
32
|
+
toml: "toml", xml: "xml", sql: "sql", graphql: "graphql",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function getLanguage(filePath) {
|
|
36
|
+
const ext = path.extname(filePath).slice(1).toLowerCase();
|
|
37
|
+
return LANGUAGE_MAP[ext] || "";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getAllFiles(dirPath, ignoreList, fileList = []) {
|
|
41
|
+
let entries;
|
|
42
|
+
try {
|
|
43
|
+
entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
44
|
+
} catch {
|
|
45
|
+
return fileList;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
50
|
+
const relativePath = path.relative(process.cwd(), fullPath);
|
|
51
|
+
|
|
52
|
+
if (
|
|
53
|
+
ignoreList.some(
|
|
54
|
+
(ig) => entry.name === ig || relativePath.startsWith(ig + path.sep) || relativePath === ig
|
|
55
|
+
)
|
|
56
|
+
) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (entry.isDirectory()) {
|
|
61
|
+
getAllFiles(fullPath, ignoreList, fileList);
|
|
62
|
+
} else if (entry.isFile()) {
|
|
63
|
+
fileList.push(fullPath);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return fileList;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isBinaryFile(filePath) {
|
|
71
|
+
try {
|
|
72
|
+
const buffer = Buffer.alloc(512);
|
|
73
|
+
const fd = fs.openSync(filePath, "r");
|
|
74
|
+
const bytesRead = fs.readSync(fd, buffer, 0, 512, 0);
|
|
75
|
+
fs.closeSync(fd);
|
|
76
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
77
|
+
if (buffer[i] === 0) return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
} catch {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function matchesExtensions(filePath, exts) {
|
|
86
|
+
if (!exts || exts.length === 0) return true;
|
|
87
|
+
const ext = path.extname(filePath).slice(1).toLowerCase();
|
|
88
|
+
return exts.map((e) => e.replace(/^\./, "").toLowerCase()).includes(ext);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function matchesPattern(filePath, patterns) {
|
|
92
|
+
if (!patterns || patterns.length === 0) return false;
|
|
93
|
+
const rel = path.relative(process.cwd(), filePath);
|
|
94
|
+
return patterns.some((p) => rel.includes(p) || path.basename(filePath).includes(p));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function formatSize(bytes) {
|
|
98
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
99
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
100
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function printBanner() {
|
|
104
|
+
console.log(`
|
|
105
|
+
╔══════════════════════════════════╗
|
|
106
|
+
║ repomeld v${VERSION} ║
|
|
107
|
+
║ Meld your repo into one file ║
|
|
108
|
+
╚══════════════════════════════════╝`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildHeader(style, relativePath, filePath, lineCount, showMeta) {
|
|
112
|
+
const lang = getLanguage(filePath);
|
|
113
|
+
const size = formatSize(fs.statSync(filePath).size);
|
|
114
|
+
const meta = showMeta ? ` [${lineCount} lines | ${size}${lang ? " | " + lang : ""}]` : "";
|
|
115
|
+
|
|
116
|
+
if (style === "markdown") {
|
|
117
|
+
return `\n## 📄 ${relativePath}${meta}\n\n\`\`\`${lang}\n`;
|
|
118
|
+
}
|
|
119
|
+
if (style === "minimal") {
|
|
120
|
+
return `\n# ${relativePath}\n`;
|
|
121
|
+
}
|
|
122
|
+
// default: banner style
|
|
123
|
+
const divider = "─".repeat(60);
|
|
124
|
+
return `\n${divider}\n FILE: ${relativePath}${meta}\n${divider}\n\n`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function buildFooter(style) {
|
|
128
|
+
if (style === "markdown") return "\n```\n";
|
|
129
|
+
return "\n";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function buildTableOfContents(files, cwd) {
|
|
133
|
+
let toc = "TABLE OF CONTENTS\n" + "═".repeat(60) + "\n";
|
|
134
|
+
files.forEach((f, i) => {
|
|
135
|
+
const rel = path.relative(cwd, f);
|
|
136
|
+
toc += ` ${String(i + 1).padStart(3, " ")}. ${rel}\n`;
|
|
137
|
+
});
|
|
138
|
+
toc += "═".repeat(60) + "\n\n";
|
|
139
|
+
return toc;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function repomeld(options) {
|
|
143
|
+
printBanner();
|
|
144
|
+
|
|
145
|
+
const cwd = process.cwd();
|
|
146
|
+
const outputFile = path.resolve(cwd, options.output);
|
|
147
|
+
const ignoreList = [...DEFAULT_IGNORE, ...(options.ignore || [])];
|
|
148
|
+
const filterExts = options.ext || [];
|
|
149
|
+
const maxFileSizeBytes = (parseFloat(options.maxSize) || 500) * 1024;
|
|
150
|
+
const headerStyle = options.style || "banner";
|
|
151
|
+
const showMeta = !options.noMeta;
|
|
152
|
+
const showToc = !options.noToc;
|
|
153
|
+
const dryRun = options.dryRun || false;
|
|
154
|
+
const include = options.include || [];
|
|
155
|
+
const exclude = options.exclude || [];
|
|
156
|
+
const linesBefore = parseInt(options.linesBefore) || 0;
|
|
157
|
+
const linesAfter = parseInt(options.linesAfter) || 0;
|
|
158
|
+
|
|
159
|
+
console.log(`\n 📂 Source : ${cwd}`);
|
|
160
|
+
console.log(` 📄 Output : ${path.relative(cwd, outputFile)}`);
|
|
161
|
+
console.log(` 🎨 Style : ${headerStyle}`);
|
|
162
|
+
if (filterExts.length) console.log(` 🔍 Filter : .${filterExts.join(", .")}`);
|
|
163
|
+
if (dryRun) console.log(` 🧪 Dry run : no file will be written`);
|
|
164
|
+
console.log();
|
|
165
|
+
|
|
166
|
+
let allFiles = getAllFiles(cwd, ignoreList);
|
|
167
|
+
|
|
168
|
+
// Filter by extension
|
|
169
|
+
if (filterExts.length) {
|
|
170
|
+
allFiles = allFiles.filter((f) => matchesExtensions(f, filterExts));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Include pattern filter
|
|
174
|
+
if (include.length) {
|
|
175
|
+
allFiles = allFiles.filter((f) => matchesPattern(f, include));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Exclude pattern filter
|
|
179
|
+
if (exclude.length) {
|
|
180
|
+
allFiles = allFiles.filter((f) => !matchesPattern(f, exclude));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Remove output file from list
|
|
184
|
+
allFiles = allFiles.filter((f) => path.resolve(f) !== outputFile);
|
|
185
|
+
|
|
186
|
+
if (allFiles.length === 0) {
|
|
187
|
+
console.log(" ⚠️ No matching files found.\n");
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
let combinedContent = "";
|
|
192
|
+
let skipped = 0;
|
|
193
|
+
let included = 0;
|
|
194
|
+
let totalLines = 0;
|
|
195
|
+
const includedFiles = [];
|
|
196
|
+
|
|
197
|
+
for (const filePath of allFiles) {
|
|
198
|
+
const relativePath = path.relative(cwd, filePath);
|
|
199
|
+
|
|
200
|
+
if (isBinaryFile(filePath)) {
|
|
201
|
+
console.log(` ⏭ Binary : ${relativePath}`);
|
|
202
|
+
skipped++;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const stat = fs.statSync(filePath);
|
|
207
|
+
if (stat.size > maxFileSizeBytes) {
|
|
208
|
+
console.log(` ⏭ Too large: ${relativePath} (${formatSize(stat.size)})`);
|
|
209
|
+
skipped++;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
let content = fs.readFileSync(filePath, "utf8");
|
|
215
|
+
|
|
216
|
+
// Trim leading/trailing blank lines if requested
|
|
217
|
+
if (options.trim) {
|
|
218
|
+
content = content.trim();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Slice specific lines
|
|
222
|
+
if (linesBefore > 0 || linesAfter > 0) {
|
|
223
|
+
const lines = content.split("\n");
|
|
224
|
+
const start = linesBefore;
|
|
225
|
+
const end = linesAfter > 0 ? lines.length - linesAfter : lines.length;
|
|
226
|
+
content = lines.slice(start, end).join("\n");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const lineCount = content.split("\n").length;
|
|
230
|
+
totalLines += lineCount;
|
|
231
|
+
includedFiles.push(filePath);
|
|
232
|
+
|
|
233
|
+
combinedContent += buildHeader(headerStyle, relativePath, filePath, lineCount, showMeta);
|
|
234
|
+
combinedContent += content;
|
|
235
|
+
combinedContent += buildFooter(headerStyle);
|
|
236
|
+
|
|
237
|
+
console.log(` ✅ ${relativePath}`);
|
|
238
|
+
included++;
|
|
239
|
+
} catch (err) {
|
|
240
|
+
console.log(` ❌ Error: ${relativePath} — ${err.message}`);
|
|
241
|
+
skipped++;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Build final output
|
|
246
|
+
let finalOutput = "";
|
|
247
|
+
|
|
248
|
+
// Top-level comment
|
|
249
|
+
const timestamp = new Date().toISOString();
|
|
250
|
+
finalOutput += `# Generated by repomeld v${VERSION}\n`;
|
|
251
|
+
finalOutput += `# Date : ${timestamp}\n`;
|
|
252
|
+
finalOutput += `# Source : ${cwd}\n`;
|
|
253
|
+
finalOutput += `# Files : ${included}\n`;
|
|
254
|
+
finalOutput += `# Lines : ${totalLines}\n\n`;
|
|
255
|
+
|
|
256
|
+
if (showToc) {
|
|
257
|
+
finalOutput += buildTableOfContents(includedFiles, cwd);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
finalOutput += combinedContent;
|
|
261
|
+
|
|
262
|
+
if (!dryRun) {
|
|
263
|
+
fs.writeFileSync(outputFile, finalOutput, "utf8");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const outputSize = formatSize(Buffer.byteLength(finalOutput, "utf8"));
|
|
267
|
+
|
|
268
|
+
console.log(`
|
|
269
|
+
✨ repomeld complete!
|
|
270
|
+
─────────────────────────────
|
|
271
|
+
✅ Included : ${included} files
|
|
272
|
+
⏭ Skipped : ${skipped} files
|
|
273
|
+
📏 Lines : ${totalLines}
|
|
274
|
+
💾 Size : ${outputSize}
|
|
275
|
+
📄 Output : ${options.output}${dryRun ? " (dry run — not written)" : ""}
|
|
276
|
+
`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ─── CLI Definition ───────────────────────────────────────────
|
|
280
|
+
|
|
281
|
+
program
|
|
282
|
+
.name("repomeld")
|
|
283
|
+
.description("Meld your entire repo into a single file — perfect for AI context, code reviews & sharing")
|
|
284
|
+
.version(VERSION)
|
|
285
|
+
|
|
286
|
+
// Output
|
|
287
|
+
.option("-o, --output <filename>", "Output file name", "repomeld_output.txt")
|
|
288
|
+
|
|
289
|
+
// Filtering
|
|
290
|
+
.option("-e, --ext <exts...>", "Only include specific extensions e.g. --ext js ts jsx")
|
|
291
|
+
.option("--include <patterns...>", "Only include files matching patterns e.g. --include src/")
|
|
292
|
+
.option("--exclude <patterns...>", "Exclude files matching patterns e.g. --exclude test spec")
|
|
293
|
+
.option("-i, --ignore <names...>", "Extra folders/files to ignore e.g. --ignore dist .next")
|
|
294
|
+
.option("--max-size <kb>", "Skip files larger than N KB (default 500)","500")
|
|
295
|
+
|
|
296
|
+
// Formatting
|
|
297
|
+
.option("-s, --style <style>", "Header style: banner | markdown | minimal (default: banner)", "banner")
|
|
298
|
+
.option("--no-toc", "Disable table of contents")
|
|
299
|
+
.option("--no-meta", "Hide file metadata (lines, size, lang)")
|
|
300
|
+
.option("--trim", "Trim leading/trailing whitespace per file")
|
|
301
|
+
|
|
302
|
+
// Advanced
|
|
303
|
+
.option("--lines-before <n>", "Skip first N lines of each file")
|
|
304
|
+
.option("--lines-after <n>", "Skip last N lines of each file")
|
|
305
|
+
.option("--dry-run", "Preview what would be included — don't write output")
|
|
306
|
+
|
|
307
|
+
.action((options) => {
|
|
308
|
+
repomeld(options);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "repomeld",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Meld your entire repo into a single file — perfect for AI context, code reviews & sharing",
|
|
5
|
+
"main": "bin/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"repomeld": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"cli",
|
|
14
|
+
"repo",
|
|
15
|
+
"file",
|
|
16
|
+
"combiner",
|
|
17
|
+
"merge",
|
|
18
|
+
"concat",
|
|
19
|
+
"ai-context",
|
|
20
|
+
"code-review",
|
|
21
|
+
"repomeld"
|
|
22
|
+
],
|
|
23
|
+
"author": "",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"commander": "^11.0.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=14.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|