gut-cli 0.1.17 → 0.1.19
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 +49 -17
- package/dist/index.js +134 -44
- package/dist/index.js.map +1 -1
- package/dist/lib/index.js +24 -8
- package/dist/lib/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -361,6 +361,15 @@ gut config set lang en --local
|
|
|
361
361
|
|
|
362
362
|
# Get current language
|
|
363
363
|
gut config get lang
|
|
364
|
+
|
|
365
|
+
# Open global config folder
|
|
366
|
+
gut config open
|
|
367
|
+
|
|
368
|
+
# Open global templates folder
|
|
369
|
+
gut config open --templates
|
|
370
|
+
|
|
371
|
+
# Open project's .gut/ folder
|
|
372
|
+
gut config open --local
|
|
364
373
|
```
|
|
365
374
|
|
|
366
375
|
**Available settings:**
|
|
@@ -372,12 +381,19 @@ gut config get lang
|
|
|
372
381
|
|
|
373
382
|
### `gut init`
|
|
374
383
|
|
|
375
|
-
Initialize
|
|
384
|
+
Initialize templates for customization (project-level or global).
|
|
376
385
|
|
|
377
386
|
```bash
|
|
378
387
|
# Copy all templates to .gut/ (translates if language is not English)
|
|
379
388
|
gut init
|
|
380
389
|
|
|
390
|
+
# Initialize global templates (~/.config/gut/templates/)
|
|
391
|
+
gut init --global
|
|
392
|
+
|
|
393
|
+
# Initialize and open folder
|
|
394
|
+
gut init --open
|
|
395
|
+
gut init --global --open
|
|
396
|
+
|
|
381
397
|
# Force overwrite existing templates
|
|
382
398
|
gut init --force
|
|
383
399
|
|
|
@@ -388,8 +404,15 @@ gut init --no-translate
|
|
|
388
404
|
gut init --provider openai
|
|
389
405
|
```
|
|
390
406
|
|
|
407
|
+
To open templates folder without initializing, use `gut config open --templates`.
|
|
408
|
+
|
|
391
409
|
Templates are automatically translated to your configured language (set via `gut lang`).
|
|
392
410
|
|
|
411
|
+
**Template precedence:**
|
|
412
|
+
1. Project templates: `.gut/` (highest priority)
|
|
413
|
+
2. Global templates: `~/.config/gut/templates/`
|
|
414
|
+
3. Built-in templates (lowest priority)
|
|
415
|
+
|
|
393
416
|
### `gut gitignore`
|
|
394
417
|
|
|
395
418
|
Generate a .gitignore file by analyzing your project structure.
|
|
@@ -466,26 +489,35 @@ API keys are stored securely using your operating system's native credential sto
|
|
|
466
489
|
|
|
467
490
|
Keys are never stored in plain text files or configuration files. When you run `gut auth login`, the key is encrypted and managed by your OS.
|
|
468
491
|
|
|
469
|
-
##
|
|
492
|
+
## Template Configuration
|
|
493
|
+
|
|
494
|
+
gut supports customizable templates at two levels:
|
|
495
|
+
|
|
496
|
+
**Project templates** (`.gut/`): Repository-specific customizations that apply only to the current project.
|
|
497
|
+
|
|
498
|
+
**Global templates** (`~/.config/gut/templates/`): User-wide defaults that apply across all projects.
|
|
499
|
+
|
|
500
|
+
**Precedence**: Project > Global > Built-in
|
|
470
501
|
|
|
471
|
-
|
|
502
|
+
Each template uses `{{variable}}` syntax for dynamic content.
|
|
472
503
|
|
|
473
504
|
| File | Purpose |
|
|
474
505
|
|------|---------|
|
|
475
|
-
|
|
|
476
|
-
|
|
|
477
|
-
|
|
|
478
|
-
|
|
|
479
|
-
|
|
|
480
|
-
|
|
|
481
|
-
|
|
|
482
|
-
|
|
|
483
|
-
|
|
|
484
|
-
|
|
|
485
|
-
|
|
|
486
|
-
|
|
|
487
|
-
|
|
|
488
|
-
|
|
506
|
+
| `commit.md` | Commit message prompt |
|
|
507
|
+
| `pr.md` | PR description prompt |
|
|
508
|
+
| `branch.md` | Branch naming rules |
|
|
509
|
+
| `checkout.md` | Checkout branch name prompt |
|
|
510
|
+
| `merge.md` | Merge conflict resolution rules |
|
|
511
|
+
| `review.md` | Code review criteria |
|
|
512
|
+
| `explain.md` | Explanation context |
|
|
513
|
+
| `explain-file.md` | File explanation context |
|
|
514
|
+
| `find.md` | Commit search context |
|
|
515
|
+
| `changelog.md` | Changelog format |
|
|
516
|
+
| `stash.md` | Stash name prompt |
|
|
517
|
+
| `summary.md` | Work summary format |
|
|
518
|
+
| `gitignore.md` | Gitignore generation prompt |
|
|
519
|
+
|
|
520
|
+
**Special case**: `.github/pull_request_template.md` is prioritized over `pr.md` for PR descriptions.
|
|
489
521
|
|
|
490
522
|
## Development
|
|
491
523
|
|
package/dist/index.js
CHANGED
|
@@ -90,6 +90,7 @@ import { Command as Command2 } from "commander";
|
|
|
90
90
|
import chalk2 from "chalk";
|
|
91
91
|
|
|
92
92
|
// src/lib/credentials.ts
|
|
93
|
+
import { createRequire } from "module";
|
|
93
94
|
var SERVICE_NAME = "gut-cli";
|
|
94
95
|
var PROVIDER_KEY_MAP = {
|
|
95
96
|
gemini: "gemini-api-key",
|
|
@@ -106,10 +107,10 @@ var FALLBACK_ENV_MAP = {
|
|
|
106
107
|
openai: "OPENAI_API_KEY",
|
|
107
108
|
anthropic: "ANTHROPIC_API_KEY"
|
|
108
109
|
};
|
|
109
|
-
|
|
110
|
+
function getKeytar() {
|
|
110
111
|
try {
|
|
111
|
-
const
|
|
112
|
-
return keytar
|
|
112
|
+
const require2 = createRequire(import.meta.url);
|
|
113
|
+
return require2("keytar");
|
|
113
114
|
} catch {
|
|
114
115
|
return null;
|
|
115
116
|
}
|
|
@@ -118,7 +119,7 @@ async function saveApiKey(provider, apiKey) {
|
|
|
118
119
|
if (provider === "ollama") {
|
|
119
120
|
throw new Error("Ollama does not require an API key");
|
|
120
121
|
}
|
|
121
|
-
const keytar =
|
|
122
|
+
const keytar = getKeytar();
|
|
122
123
|
if (!keytar) {
|
|
123
124
|
throw new Error("Keychain not available. Set environment variable instead.");
|
|
124
125
|
}
|
|
@@ -132,7 +133,7 @@ async function getApiKey(provider) {
|
|
|
132
133
|
if (envKey) return envKey;
|
|
133
134
|
const fallbackKey = process.env[FALLBACK_ENV_MAP[provider]];
|
|
134
135
|
if (fallbackKey) return fallbackKey;
|
|
135
|
-
const keytar =
|
|
136
|
+
const keytar = getKeytar();
|
|
136
137
|
if (!keytar) return null;
|
|
137
138
|
return keytar.getPassword(SERVICE_NAME, PROVIDER_KEY_MAP[provider]);
|
|
138
139
|
}
|
|
@@ -140,7 +141,7 @@ async function deleteApiKey(provider) {
|
|
|
140
141
|
if (provider === "ollama") {
|
|
141
142
|
throw new Error("Ollama does not use an API key");
|
|
142
143
|
}
|
|
143
|
-
const keytar =
|
|
144
|
+
const keytar = getKeytar();
|
|
144
145
|
if (!keytar) {
|
|
145
146
|
throw new Error("Keychain not available.");
|
|
146
147
|
}
|
|
@@ -289,6 +290,7 @@ import { createOllama } from "ollama-ai-provider";
|
|
|
289
290
|
import { z } from "zod";
|
|
290
291
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
291
292
|
import { join as join2, dirname } from "path";
|
|
293
|
+
import { homedir as homedir2 } from "os";
|
|
292
294
|
import { fileURLToPath } from "url";
|
|
293
295
|
|
|
294
296
|
// src/lib/config.ts
|
|
@@ -413,13 +415,27 @@ function loadTemplate(name) {
|
|
|
413
415
|
}
|
|
414
416
|
throw new Error(`Template not found: ${templatePath}`);
|
|
415
417
|
}
|
|
416
|
-
function
|
|
417
|
-
|
|
418
|
+
function getGlobalTemplatesDir() {
|
|
419
|
+
return join2(homedir2(), ".config", "gut", "templates");
|
|
420
|
+
}
|
|
421
|
+
function findGlobalTemplate(templateName) {
|
|
422
|
+
const templatePath = join2(getGlobalTemplatesDir(), `${templateName}.md`);
|
|
418
423
|
if (existsSync2(templatePath)) {
|
|
419
424
|
return readFileSync2(templatePath, "utf-8");
|
|
420
425
|
}
|
|
421
426
|
return null;
|
|
422
427
|
}
|
|
428
|
+
function findTemplate(repoRoot, templateName) {
|
|
429
|
+
const projectTemplatePath = join2(repoRoot, ".gut", `${templateName}.md`);
|
|
430
|
+
if (existsSync2(projectTemplatePath)) {
|
|
431
|
+
return readFileSync2(projectTemplatePath, "utf-8");
|
|
432
|
+
}
|
|
433
|
+
const globalTemplate = findGlobalTemplate(templateName);
|
|
434
|
+
if (globalTemplate) {
|
|
435
|
+
return globalTemplate;
|
|
436
|
+
}
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
423
439
|
function applyTemplate(userTemplate, templateName, variables) {
|
|
424
440
|
const langInstruction = getLanguageInstruction(getLanguage());
|
|
425
441
|
let result = userTemplate || loadTemplate(templateName);
|
|
@@ -851,14 +867,14 @@ var commitCommand = new Command3("commit").description("Generate a commit messag
|
|
|
851
867
|
console.log(chalk3.green("\u2713 Committed successfully"));
|
|
852
868
|
} else if (answer.toLowerCase() === "e") {
|
|
853
869
|
console.log(chalk3.gray("Opening editor..."));
|
|
854
|
-
const { execSync:
|
|
870
|
+
const { execSync: execSync9 } = await import("child_process");
|
|
855
871
|
const editor = process.env.EDITOR || process.env.VISUAL || "vi";
|
|
856
872
|
const fs2 = await import("fs");
|
|
857
873
|
const os = await import("os");
|
|
858
874
|
const path2 = await import("path");
|
|
859
875
|
const tmpFile = path2.join(os.tmpdir(), "gut-commit-msg.txt");
|
|
860
876
|
fs2.writeFileSync(tmpFile, message);
|
|
861
|
-
|
|
877
|
+
execSync9(`${editor} "${tmpFile}"`, { stdio: "inherit" });
|
|
862
878
|
const editedMessage = fs2.readFileSync(tmpFile, "utf-8").trim();
|
|
863
879
|
fs2.unlinkSync(tmpFile);
|
|
864
880
|
if (editedMessage) {
|
|
@@ -2196,9 +2212,9 @@ var summaryCommand = new Command14("summary").description("Generate a work summa
|
|
|
2196
2212
|
const output = options.markdown ? formatMarkdown(summary, author, since, options.until) : null;
|
|
2197
2213
|
if (options.copy) {
|
|
2198
2214
|
const textToCopy = output || formatMarkdown(summary, author, since, options.until);
|
|
2199
|
-
const { execSync:
|
|
2215
|
+
const { execSync: execSync9 } = await import("child_process");
|
|
2200
2216
|
try {
|
|
2201
|
-
|
|
2217
|
+
execSync9("pbcopy", { input: textToCopy });
|
|
2202
2218
|
console.log(chalk15.green("Summary copied to clipboard!"));
|
|
2203
2219
|
console.log();
|
|
2204
2220
|
} catch {
|
|
@@ -2314,6 +2330,16 @@ function printSummary(summary, author, since, until) {
|
|
|
2314
2330
|
// src/commands/config.ts
|
|
2315
2331
|
import { Command as Command15 } from "commander";
|
|
2316
2332
|
import chalk16 from "chalk";
|
|
2333
|
+
import { execSync as execSync7 } from "child_process";
|
|
2334
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
2335
|
+
import { join as join5 } from "path";
|
|
2336
|
+
import { homedir as homedir3 } from "os";
|
|
2337
|
+
import { simpleGit as simpleGit14 } from "simple-git";
|
|
2338
|
+
function openFolder(path2) {
|
|
2339
|
+
const platform = process.platform;
|
|
2340
|
+
const cmd = platform === "darwin" ? "open" : platform === "win32" ? 'start ""' : "xdg-open";
|
|
2341
|
+
execSync7(`${cmd} "${path2}"`);
|
|
2342
|
+
}
|
|
2317
2343
|
var configCommand = new Command15("config").description("Manage gut configuration");
|
|
2318
2344
|
configCommand.command("set <key> <value>").description("Set a configuration value").option("--local", "Set for current repository only").action((key, value, options) => {
|
|
2319
2345
|
if (key === "lang") {
|
|
@@ -2362,6 +2388,39 @@ configCommand.command("list").description("List all configuration values").actio
|
|
|
2362
2388
|
console.log(chalk16.gray("Local config: .gut/config.json"));
|
|
2363
2389
|
}
|
|
2364
2390
|
});
|
|
2391
|
+
configCommand.command("open").description("Open configuration or templates folder").option("-t, --templates", "Open templates folder instead of config").option("-g, --global", "Open global folder (default)").option("-l, --local", "Open local/project folder").action(async (options) => {
|
|
2392
|
+
const git = simpleGit14();
|
|
2393
|
+
const isLocal = options.local === true;
|
|
2394
|
+
let targetPath;
|
|
2395
|
+
if (isLocal) {
|
|
2396
|
+
const isRepo = await git.checkIsRepo();
|
|
2397
|
+
if (!isRepo) {
|
|
2398
|
+
console.error(chalk16.red("Error: Not a git repository"));
|
|
2399
|
+
console.error(chalk16.gray("Use --global to open global config folder"));
|
|
2400
|
+
process.exit(1);
|
|
2401
|
+
}
|
|
2402
|
+
const repoRoot = await git.revparse(["--show-toplevel"]).catch(() => process.cwd());
|
|
2403
|
+
targetPath = join5(repoRoot.trim(), ".gut");
|
|
2404
|
+
} else {
|
|
2405
|
+
if (options.templates) {
|
|
2406
|
+
targetPath = join5(homedir3(), ".config", "gut", "templates");
|
|
2407
|
+
} else {
|
|
2408
|
+
targetPath = join5(homedir3(), ".config", "gut");
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
if (!existsSync5(targetPath)) {
|
|
2412
|
+
mkdirSync2(targetPath, { recursive: true });
|
|
2413
|
+
console.log(chalk16.green(`Created ${targetPath}`));
|
|
2414
|
+
}
|
|
2415
|
+
try {
|
|
2416
|
+
openFolder(targetPath);
|
|
2417
|
+
console.log(chalk16.green(`Opened: ${targetPath}`));
|
|
2418
|
+
} catch (error) {
|
|
2419
|
+
console.error(chalk16.red(`Failed to open folder: ${targetPath}`));
|
|
2420
|
+
console.error(chalk16.gray(error.message));
|
|
2421
|
+
process.exit(1);
|
|
2422
|
+
}
|
|
2423
|
+
});
|
|
2365
2424
|
|
|
2366
2425
|
// src/commands/lang.ts
|
|
2367
2426
|
import { Command as Command16 } from "commander";
|
|
@@ -2394,17 +2453,24 @@ var langCommand = new Command16("lang").description("Set or show output language
|
|
|
2394
2453
|
import { Command as Command17 } from "commander";
|
|
2395
2454
|
import chalk18 from "chalk";
|
|
2396
2455
|
import ora14 from "ora";
|
|
2397
|
-
import { simpleGit as
|
|
2398
|
-
import { existsSync as
|
|
2399
|
-
import { join as
|
|
2456
|
+
import { simpleGit as simpleGit15 } from "simple-git";
|
|
2457
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
2458
|
+
import { join as join6, dirname as dirname2 } from "path";
|
|
2459
|
+
import { homedir as homedir4 } from "os";
|
|
2400
2460
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2461
|
+
import { execSync as execSync8 } from "child_process";
|
|
2401
2462
|
import { generateText as generateText2 } from "ai";
|
|
2402
2463
|
import { createGoogleGenerativeAI as createGoogleGenerativeAI2 } from "@ai-sdk/google";
|
|
2403
2464
|
import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
|
|
2404
2465
|
import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
|
|
2466
|
+
function openFolder2(path2) {
|
|
2467
|
+
const platform = process.platform;
|
|
2468
|
+
const cmd = platform === "darwin" ? "open" : platform === "win32" ? 'start ""' : "xdg-open";
|
|
2469
|
+
execSync8(`${cmd} "${path2}"`);
|
|
2470
|
+
}
|
|
2405
2471
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2406
2472
|
var __dirname2 = dirname2(__filename2);
|
|
2407
|
-
var GUT_ROOT2 =
|
|
2473
|
+
var GUT_ROOT2 = join6(__dirname2, "..");
|
|
2408
2474
|
var TEMPLATE_FILES = [
|
|
2409
2475
|
"branch.md",
|
|
2410
2476
|
"changelog.md",
|
|
@@ -2467,20 +2533,28 @@ Translated template:`
|
|
|
2467
2533
|
});
|
|
2468
2534
|
return text.trim();
|
|
2469
2535
|
}
|
|
2470
|
-
var initCommand = new Command17("init").description("Initialize .gut/ templates in your project").option("-p, --provider <provider>", "AI provider for translation (gemini, openai, anthropic)", "gemini").option("-f, --force", "Overwrite existing templates").option("--no-translate", "Skip translation even if language is not English").action(async (options) => {
|
|
2471
|
-
const
|
|
2472
|
-
const
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2536
|
+
var initCommand = new Command17("init").description("Initialize .gut/ templates in your project or globally").option("-p, --provider <provider>", "AI provider for translation (gemini, openai, anthropic)", "gemini").option("-f, --force", "Overwrite existing templates").option("-g, --global", "Initialize templates globally (~/.config/gut/templates/)").option("-o, --open", "Open the templates folder (can be used alone)").option("--no-translate", "Skip translation even if language is not English").action(async (options) => {
|
|
2537
|
+
const isGlobal = options.global === true;
|
|
2538
|
+
const git = simpleGit15();
|
|
2539
|
+
let targetDir;
|
|
2540
|
+
if (isGlobal) {
|
|
2541
|
+
targetDir = join6(homedir4(), ".config", "gut", "templates");
|
|
2542
|
+
} else {
|
|
2543
|
+
const isRepo = await git.checkIsRepo();
|
|
2544
|
+
if (!isRepo) {
|
|
2545
|
+
console.error(chalk18.red("Error: Not a git repository"));
|
|
2546
|
+
console.error(chalk18.gray("Use --global to initialize templates globally"));
|
|
2547
|
+
process.exit(1);
|
|
2548
|
+
}
|
|
2549
|
+
const repoRoot = await git.revparse(["--show-toplevel"]).catch(() => process.cwd());
|
|
2550
|
+
targetDir = join6(repoRoot.trim(), ".gut");
|
|
2476
2551
|
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
const sourceDir = join5(GUT_ROOT2, ".gut");
|
|
2480
|
-
if (!existsSync5(targetDir)) {
|
|
2481
|
-
mkdirSync2(targetDir, { recursive: true });
|
|
2552
|
+
if (!existsSync6(targetDir)) {
|
|
2553
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
2482
2554
|
console.log(chalk18.green(`Created ${targetDir}`));
|
|
2483
2555
|
}
|
|
2556
|
+
console.log(chalk18.blue(isGlobal ? "Initializing global templates...\n" : "Initializing project templates...\n"));
|
|
2557
|
+
const sourceDir = join6(GUT_ROOT2, ".gut");
|
|
2484
2558
|
const lang = getLanguage();
|
|
2485
2559
|
const needsTranslation = options.translate !== false && lang !== "en";
|
|
2486
2560
|
const provider = options.provider.toLowerCase();
|
|
@@ -2492,12 +2566,12 @@ var initCommand = new Command17("init").description("Initialize .gut/ templates
|
|
|
2492
2566
|
let copied = 0;
|
|
2493
2567
|
let skipped = 0;
|
|
2494
2568
|
for (const filename of TEMPLATE_FILES) {
|
|
2495
|
-
const sourcePath =
|
|
2496
|
-
const targetPath =
|
|
2497
|
-
if (!
|
|
2569
|
+
const sourcePath = join6(sourceDir, filename);
|
|
2570
|
+
const targetPath = join6(targetDir, filename);
|
|
2571
|
+
if (!existsSync6(sourcePath)) {
|
|
2498
2572
|
continue;
|
|
2499
2573
|
}
|
|
2500
|
-
if (
|
|
2574
|
+
if (existsSync6(targetPath) && !options.force) {
|
|
2501
2575
|
console.log(chalk18.gray(` Skipped: ${filename} (already exists)`));
|
|
2502
2576
|
skipped++;
|
|
2503
2577
|
continue;
|
|
@@ -2521,21 +2595,37 @@ var initCommand = new Command17("init").description("Initialize .gut/ templates
|
|
|
2521
2595
|
}
|
|
2522
2596
|
console.log();
|
|
2523
2597
|
if (copied > 0) {
|
|
2524
|
-
|
|
2598
|
+
const location = isGlobal ? "~/.config/gut/templates/" : ".gut/";
|
|
2599
|
+
console.log(chalk18.green(`\u2713 ${copied} template(s) initialized in ${location}`));
|
|
2525
2600
|
}
|
|
2526
2601
|
if (skipped > 0) {
|
|
2527
2602
|
console.log(chalk18.gray(` ${skipped} template(s) skipped (use --force to overwrite)`));
|
|
2528
2603
|
}
|
|
2529
|
-
|
|
2604
|
+
if (isGlobal) {
|
|
2605
|
+
console.log(chalk18.gray("\nGlobal templates will be used as fallback for all projects."));
|
|
2606
|
+
console.log(chalk18.gray("Project-level templates (.gut/) take priority over global templates."));
|
|
2607
|
+
} else {
|
|
2608
|
+
console.log(chalk18.gray("\nYou can now customize these templates for your project."));
|
|
2609
|
+
}
|
|
2610
|
+
if (options.open) {
|
|
2611
|
+
try {
|
|
2612
|
+
openFolder2(targetDir);
|
|
2613
|
+
console.log(chalk18.green(`
|
|
2614
|
+
Opened: ${targetDir}`));
|
|
2615
|
+
} catch (error) {
|
|
2616
|
+
console.error(chalk18.red(`
|
|
2617
|
+
Failed to open folder: ${targetDir}`));
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2530
2620
|
});
|
|
2531
2621
|
|
|
2532
2622
|
// src/commands/gitignore.ts
|
|
2533
2623
|
import { Command as Command18 } from "commander";
|
|
2534
2624
|
import chalk19 from "chalk";
|
|
2535
2625
|
import ora15 from "ora";
|
|
2536
|
-
import { simpleGit as
|
|
2537
|
-
import { readdirSync as readdirSync2, readFileSync as readFileSync7, existsSync as
|
|
2538
|
-
import { join as
|
|
2626
|
+
import { simpleGit as simpleGit16 } from "simple-git";
|
|
2627
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync7, existsSync as existsSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
2628
|
+
import { join as join7 } from "path";
|
|
2539
2629
|
var CONFIG_FILES = [
|
|
2540
2630
|
// JavaScript/TypeScript
|
|
2541
2631
|
"package.json",
|
|
@@ -2588,7 +2678,7 @@ function getFiles(dir, maxDepth = 3, currentDepth = 0) {
|
|
|
2588
2678
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "vendor" || entry.name === "target" || entry.name === "__pycache__" || entry.name === "venv" || entry.name === ".venv") {
|
|
2589
2679
|
continue;
|
|
2590
2680
|
}
|
|
2591
|
-
const fullPath =
|
|
2681
|
+
const fullPath = join7(dir, entry.name);
|
|
2592
2682
|
if (entry.isDirectory()) {
|
|
2593
2683
|
files.push(entry.name + "/");
|
|
2594
2684
|
const subFiles = getFiles(fullPath, maxDepth, currentDepth + 1);
|
|
@@ -2610,15 +2700,15 @@ function findConfigFiles(repoRoot) {
|
|
|
2610
2700
|
const entries = readdirSync2(repoRoot);
|
|
2611
2701
|
for (const entry of entries) {
|
|
2612
2702
|
if (entry.endsWith(ext)) {
|
|
2613
|
-
const content = readFileSync7(
|
|
2703
|
+
const content = readFileSync7(join7(repoRoot, entry), "utf-8");
|
|
2614
2704
|
found.set(entry, content.slice(0, 2e3));
|
|
2615
2705
|
}
|
|
2616
2706
|
}
|
|
2617
2707
|
} catch {
|
|
2618
2708
|
}
|
|
2619
2709
|
} else {
|
|
2620
|
-
const filePath =
|
|
2621
|
-
if (
|
|
2710
|
+
const filePath = join7(repoRoot, configFile);
|
|
2711
|
+
if (existsSync7(filePath)) {
|
|
2622
2712
|
try {
|
|
2623
2713
|
const content = readFileSync7(filePath, "utf-8");
|
|
2624
2714
|
found.set(configFile, content.slice(0, 2e3));
|
|
@@ -2630,7 +2720,7 @@ function findConfigFiles(repoRoot) {
|
|
|
2630
2720
|
return found;
|
|
2631
2721
|
}
|
|
2632
2722
|
var gitignoreCommand = new Command18("gitignore").description("Generate .gitignore from current codebase").option("-p, --provider <provider>", "AI provider (gemini, openai, anthropic)", "gemini").option("-m, --model <model>", "Model to use (provider-specific)").option("-o, --output <file>", "Output file (default: .gitignore)", ".gitignore").option("--stdout", "Print to stdout instead of file").option("-y, --yes", "Overwrite existing .gitignore without confirmation").action(async (options) => {
|
|
2633
|
-
const git =
|
|
2723
|
+
const git = simpleGit16();
|
|
2634
2724
|
const repoRoot = await git.revparse(["--show-toplevel"]).catch(() => process.cwd());
|
|
2635
2725
|
const root = repoRoot.trim();
|
|
2636
2726
|
const provider = options.provider.toLowerCase();
|
|
@@ -2641,9 +2731,9 @@ var gitignoreCommand = new Command18("gitignore").description("Generate .gitigno
|
|
|
2641
2731
|
const spinner = ora15("Analyzing project structure...").start();
|
|
2642
2732
|
const files = getFiles(root);
|
|
2643
2733
|
const configFiles = findConfigFiles(root);
|
|
2644
|
-
const gitignorePath =
|
|
2734
|
+
const gitignorePath = join7(root, options.output);
|
|
2645
2735
|
let existingGitignore;
|
|
2646
|
-
if (
|
|
2736
|
+
if (existsSync7(gitignorePath)) {
|
|
2647
2737
|
existingGitignore = readFileSync7(gitignorePath, "utf-8");
|
|
2648
2738
|
}
|
|
2649
2739
|
let configFilesStr = "";
|
|
@@ -2678,7 +2768,7 @@ ${content}
|
|
|
2678
2768
|
console.log(gitignoreContent);
|
|
2679
2769
|
console.log(chalk19.gray("\u2500".repeat(50)));
|
|
2680
2770
|
console.log();
|
|
2681
|
-
if (
|
|
2771
|
+
if (existsSync7(gitignorePath) && !options.yes) {
|
|
2682
2772
|
const readline = await import("readline");
|
|
2683
2773
|
const rl = readline.createInterface({
|
|
2684
2774
|
input: process.stdin,
|