gut-cli 0.1.22 → 0.1.23
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/.gut/config.json +2 -1
- package/.gut/ja/branch.md +11 -0
- package/.gut/ja/changelog.md +18 -0
- package/.gut/ja/checkout.md +17 -0
- package/.gut/ja/commit.md +12 -0
- package/.gut/ja/explain-file.md +13 -0
- package/.gut/ja/explain.md +12 -0
- package/.gut/ja/find.md +13 -0
- package/.gut/ja/gitignore.md +16 -0
- package/.gut/ja/merge.md +12 -0
- package/.gut/ja/pr.md +14 -0
- package/.gut/ja/review.md +11 -0
- package/.gut/ja/stash.md +10 -0
- package/.gut/ja/summary.md +11 -0
- package/dist/index.js +1910 -1695
- package/dist/index.js.map +1 -1
- package/dist/lib/index.d.ts +5 -5
- package/dist/lib/index.js +266 -186
- package/dist/lib/index.js.map +1 -1
- package/package.json +22 -10
package/dist/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
type Language = 'en' | 'ja';
|
|
4
|
+
declare function getLanguage(): Language;
|
|
5
|
+
declare function setLanguage(lang: Language, local?: boolean): void;
|
|
6
|
+
declare function getLanguageInstruction(lang: Language): string;
|
|
7
|
+
|
|
3
8
|
type Provider = 'gemini' | 'openai' | 'anthropic' | 'ollama';
|
|
4
9
|
declare function saveApiKey(provider: Provider, apiKey: string): Promise<void>;
|
|
5
10
|
declare function getApiKey(provider: Provider): Promise<string | null>;
|
|
@@ -9,11 +14,6 @@ declare function listProviders(): Promise<{
|
|
|
9
14
|
hasKey: boolean;
|
|
10
15
|
}[]>;
|
|
11
16
|
|
|
12
|
-
type Language = 'en' | 'ja';
|
|
13
|
-
declare function getLanguage(): Language;
|
|
14
|
-
declare function setLanguage(lang: Language, local?: boolean): void;
|
|
15
|
-
declare function getLanguageInstruction(lang: Language): string;
|
|
16
|
-
|
|
17
17
|
interface AIOptions {
|
|
18
18
|
provider: Provider;
|
|
19
19
|
model?: string;
|
package/dist/lib/index.js
CHANGED
|
@@ -1,14 +1,121 @@
|
|
|
1
1
|
// src/lib/ai.ts
|
|
2
|
-
import {
|
|
2
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
3
|
+
import { homedir as homedir2 } from "os";
|
|
4
|
+
import { dirname, join as join2 } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
3
7
|
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
4
8
|
import { createOpenAI } from "@ai-sdk/openai";
|
|
5
|
-
import {
|
|
9
|
+
import { generateObject, generateText } from "ai";
|
|
6
10
|
import { createOllama } from "ollama-ai-provider";
|
|
7
11
|
import { z } from "zod";
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
|
|
13
|
+
// src/lib/config.ts
|
|
14
|
+
import { execSync } from "child_process";
|
|
15
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
10
16
|
import { homedir } from "os";
|
|
11
|
-
import {
|
|
17
|
+
import { join } from "path";
|
|
18
|
+
var DEFAULT_CONFIG = {
|
|
19
|
+
lang: "en"
|
|
20
|
+
};
|
|
21
|
+
var DEFAULT_MODELS = {
|
|
22
|
+
gemini: "gemini-2.5-flash",
|
|
23
|
+
openai: "gpt-4.1-mini",
|
|
24
|
+
anthropic: "claude-sonnet-4-5",
|
|
25
|
+
ollama: "llama3.3"
|
|
26
|
+
};
|
|
27
|
+
function getGlobalConfigPath() {
|
|
28
|
+
const configDir = join(homedir(), ".config", "gut");
|
|
29
|
+
return join(configDir, "config.json");
|
|
30
|
+
}
|
|
31
|
+
function getRepoRoot() {
|
|
32
|
+
try {
|
|
33
|
+
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function getLocalConfigPath() {
|
|
39
|
+
const repoRoot = getRepoRoot();
|
|
40
|
+
if (!repoRoot) return null;
|
|
41
|
+
return join(repoRoot, ".gut", "config.json");
|
|
42
|
+
}
|
|
43
|
+
function ensureGlobalConfigDir() {
|
|
44
|
+
const configDir = join(homedir(), ".config", "gut");
|
|
45
|
+
if (!existsSync(configDir)) {
|
|
46
|
+
mkdirSync(configDir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function ensureLocalConfigDir() {
|
|
50
|
+
const repoRoot = getRepoRoot();
|
|
51
|
+
if (!repoRoot) return;
|
|
52
|
+
const gutDir = join(repoRoot, ".gut");
|
|
53
|
+
if (!existsSync(gutDir)) {
|
|
54
|
+
mkdirSync(gutDir, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function readConfigFile(path) {
|
|
58
|
+
if (!existsSync(path)) return {};
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
61
|
+
} catch {
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function getGlobalConfig() {
|
|
66
|
+
const globalPath = getGlobalConfigPath();
|
|
67
|
+
return { ...DEFAULT_CONFIG, ...readConfigFile(globalPath) };
|
|
68
|
+
}
|
|
69
|
+
function getLocalConfig() {
|
|
70
|
+
const localPath = getLocalConfigPath();
|
|
71
|
+
if (!localPath) return {};
|
|
72
|
+
return readConfigFile(localPath);
|
|
73
|
+
}
|
|
74
|
+
function getConfig() {
|
|
75
|
+
const globalConfig = getGlobalConfig();
|
|
76
|
+
const localConfig = getLocalConfig();
|
|
77
|
+
return { ...globalConfig, ...localConfig };
|
|
78
|
+
}
|
|
79
|
+
function setGlobalConfig(key, value) {
|
|
80
|
+
ensureGlobalConfigDir();
|
|
81
|
+
const config = getGlobalConfig();
|
|
82
|
+
config[key] = value;
|
|
83
|
+
writeFileSync(getGlobalConfigPath(), JSON.stringify(config, null, 2));
|
|
84
|
+
}
|
|
85
|
+
function setLocalConfig(key, value) {
|
|
86
|
+
const localPath = getLocalConfigPath();
|
|
87
|
+
if (!localPath) {
|
|
88
|
+
throw new Error("Not in a git repository");
|
|
89
|
+
}
|
|
90
|
+
ensureLocalConfigDir();
|
|
91
|
+
const config = getLocalConfig();
|
|
92
|
+
config[key] = value;
|
|
93
|
+
writeFileSync(localPath, JSON.stringify(config, null, 2));
|
|
94
|
+
}
|
|
95
|
+
function getLanguage() {
|
|
96
|
+
return getConfig().lang;
|
|
97
|
+
}
|
|
98
|
+
function setLanguage(lang, local = false) {
|
|
99
|
+
if (local) {
|
|
100
|
+
setLocalConfig("lang", lang);
|
|
101
|
+
} else {
|
|
102
|
+
setGlobalConfig("lang", lang);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function getLanguageInstruction(lang) {
|
|
106
|
+
switch (lang) {
|
|
107
|
+
case "ja":
|
|
108
|
+
return "\n\nIMPORTANT: Respond in Japanese (\u65E5\u672C\u8A9E\u3067\u56DE\u7B54\u3057\u3066\u304F\u3060\u3055\u3044).";
|
|
109
|
+
default:
|
|
110
|
+
return "";
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function getConfiguredModel() {
|
|
114
|
+
return getConfig().model;
|
|
115
|
+
}
|
|
116
|
+
function getDefaultModel(provider) {
|
|
117
|
+
return DEFAULT_MODELS[provider] || DEFAULT_MODELS.gemini;
|
|
118
|
+
}
|
|
12
119
|
|
|
13
120
|
// src/lib/credentials.ts
|
|
14
121
|
import { createRequire } from "module";
|
|
@@ -86,36 +193,36 @@ var __dirname = dirname(__filename);
|
|
|
86
193
|
function findGutRoot() {
|
|
87
194
|
let current = __dirname;
|
|
88
195
|
for (let i = 0; i < 5; i++) {
|
|
89
|
-
const gutPath =
|
|
90
|
-
if (
|
|
196
|
+
const gutPath = join2(current, ".gut");
|
|
197
|
+
if (existsSync2(gutPath)) {
|
|
91
198
|
return current;
|
|
92
199
|
}
|
|
93
200
|
current = dirname(current);
|
|
94
201
|
}
|
|
95
|
-
return
|
|
202
|
+
return join2(__dirname, "..");
|
|
96
203
|
}
|
|
97
204
|
var GUT_ROOT = findGutRoot();
|
|
98
205
|
function loadTemplate(name) {
|
|
99
|
-
const templatePath =
|
|
100
|
-
if (
|
|
101
|
-
return
|
|
206
|
+
const templatePath = join2(GUT_ROOT, ".gut", `${name}.md`);
|
|
207
|
+
if (existsSync2(templatePath)) {
|
|
208
|
+
return readFileSync2(templatePath, "utf-8");
|
|
102
209
|
}
|
|
103
210
|
throw new Error(`Template not found: ${templatePath}`);
|
|
104
211
|
}
|
|
105
212
|
function getGlobalTemplatesDir() {
|
|
106
|
-
return
|
|
213
|
+
return join2(homedir2(), ".config", "gut", "templates");
|
|
107
214
|
}
|
|
108
215
|
function findGlobalTemplate(templateName) {
|
|
109
|
-
const templatePath =
|
|
110
|
-
if (
|
|
111
|
-
return
|
|
216
|
+
const templatePath = join2(getGlobalTemplatesDir(), `${templateName}.md`);
|
|
217
|
+
if (existsSync2(templatePath)) {
|
|
218
|
+
return readFileSync2(templatePath, "utf-8");
|
|
112
219
|
}
|
|
113
220
|
return null;
|
|
114
221
|
}
|
|
115
222
|
function findTemplate(repoRoot, templateName) {
|
|
116
|
-
const projectTemplatePath =
|
|
117
|
-
if (
|
|
118
|
-
return
|
|
223
|
+
const projectTemplatePath = join2(repoRoot, ".gut", `${templateName}.md`);
|
|
224
|
+
if (existsSync2(projectTemplatePath)) {
|
|
225
|
+
return readFileSync2(projectTemplatePath, "utf-8");
|
|
119
226
|
}
|
|
120
227
|
const globalTemplate = findGlobalTemplate(templateName);
|
|
121
228
|
if (globalTemplate) {
|
|
@@ -141,16 +248,12 @@ ${value}
|
|
|
141
248
|
<output-format>
|
|
142
249
|
${outputFormat}
|
|
143
250
|
</output-format>` : "";
|
|
144
|
-
return contextXml
|
|
251
|
+
return `${contextXml}<instructions>
|
|
252
|
+
${template}${langInstruction}
|
|
253
|
+
</instructions>${outputSection}`;
|
|
145
254
|
}
|
|
146
|
-
var DEFAULT_MODELS = {
|
|
147
|
-
gemini: "gemini-2.0-flash",
|
|
148
|
-
openai: "gpt-4o-mini",
|
|
149
|
-
anthropic: "claude-sonnet-4-20250514",
|
|
150
|
-
ollama: "llama3.2"
|
|
151
|
-
};
|
|
152
255
|
async function getModel(options) {
|
|
153
|
-
const modelName = options.model ||
|
|
256
|
+
const modelName = options.model || getConfiguredModel() || getDefaultModel(options.provider);
|
|
154
257
|
async function resolveApiKey() {
|
|
155
258
|
if (options.apiKey) return options.apiKey;
|
|
156
259
|
return getApiKey(options.provider);
|
|
@@ -189,9 +292,15 @@ async function getModel(options) {
|
|
|
189
292
|
}
|
|
190
293
|
async function generateCommitMessage(diff, options, template) {
|
|
191
294
|
const model = await getModel(options);
|
|
192
|
-
const prompt = buildPrompt(
|
|
193
|
-
|
|
194
|
-
|
|
295
|
+
const prompt = buildPrompt(
|
|
296
|
+
template,
|
|
297
|
+
"commit",
|
|
298
|
+
{
|
|
299
|
+
diff: diff.slice(0, 8e3)
|
|
300
|
+
},
|
|
301
|
+
options.language,
|
|
302
|
+
"Respond with ONLY the commit message, nothing else."
|
|
303
|
+
);
|
|
195
304
|
const result = await generateText({
|
|
196
305
|
model,
|
|
197
306
|
prompt,
|
|
@@ -201,18 +310,24 @@ async function generateCommitMessage(diff, options, template) {
|
|
|
201
310
|
}
|
|
202
311
|
async function generatePRDescription(context, options, template) {
|
|
203
312
|
const model = await getModel(options);
|
|
204
|
-
const prompt = buildPrompt(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
313
|
+
const prompt = buildPrompt(
|
|
314
|
+
template,
|
|
315
|
+
"pr",
|
|
316
|
+
{
|
|
317
|
+
baseBranch: context.baseBranch,
|
|
318
|
+
currentBranch: context.currentBranch,
|
|
319
|
+
commits: context.commits.map((c) => `- ${c}`).join("\n"),
|
|
320
|
+
diff: context.diff.slice(0, 6e3)
|
|
321
|
+
},
|
|
322
|
+
options.language,
|
|
323
|
+
`Respond in JSON format:
|
|
210
324
|
\`\`\`json
|
|
211
325
|
{
|
|
212
326
|
"title": "...",
|
|
213
327
|
"body": "..."
|
|
214
328
|
}
|
|
215
|
-
\`\`\``
|
|
329
|
+
\`\`\``
|
|
330
|
+
);
|
|
216
331
|
const result = await generateText({
|
|
217
332
|
model,
|
|
218
333
|
prompt,
|
|
@@ -243,9 +358,14 @@ var CodeReviewSchema = z.object({
|
|
|
243
358
|
});
|
|
244
359
|
async function generateCodeReview(diff, options, template) {
|
|
245
360
|
const model = await getModel(options);
|
|
246
|
-
const prompt = buildPrompt(
|
|
247
|
-
|
|
248
|
-
|
|
361
|
+
const prompt = buildPrompt(
|
|
362
|
+
template,
|
|
363
|
+
"review",
|
|
364
|
+
{
|
|
365
|
+
diff: diff.slice(0, 1e4)
|
|
366
|
+
},
|
|
367
|
+
options.language
|
|
368
|
+
);
|
|
249
369
|
const result = await generateObject({
|
|
250
370
|
model,
|
|
251
371
|
schema: CodeReviewSchema,
|
|
@@ -267,13 +387,18 @@ var ChangelogSchema = z.object({
|
|
|
267
387
|
async function generateChangelog(context, options, template) {
|
|
268
388
|
const model = await getModel(options);
|
|
269
389
|
const commitList = context.commits.map((c) => `- ${c.hash.slice(0, 7)} ${c.message} (${c.author})`).join("\n");
|
|
270
|
-
const prompt = buildPrompt(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
390
|
+
const prompt = buildPrompt(
|
|
391
|
+
template,
|
|
392
|
+
"changelog",
|
|
393
|
+
{
|
|
394
|
+
fromRef: context.fromRef,
|
|
395
|
+
toRef: context.toRef,
|
|
396
|
+
commits: commitList,
|
|
397
|
+
diff: context.diff.slice(0, 8e3),
|
|
398
|
+
todayDate: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
399
|
+
},
|
|
400
|
+
options.language
|
|
401
|
+
);
|
|
277
402
|
const result = await generateObject({
|
|
278
403
|
model,
|
|
279
404
|
schema: ChangelogSchema,
|
|
@@ -301,10 +426,15 @@ var ExplanationSchema = z.object({
|
|
|
301
426
|
async function generateExplanation(context, options, template) {
|
|
302
427
|
const model = await getModel(options);
|
|
303
428
|
if (context.type === "file-content") {
|
|
304
|
-
const prompt2 = buildPrompt(
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
429
|
+
const prompt2 = buildPrompt(
|
|
430
|
+
template,
|
|
431
|
+
"explain-file",
|
|
432
|
+
{
|
|
433
|
+
filePath: context.metadata.filePath || "",
|
|
434
|
+
content: context.content?.slice(0, 15e3) || ""
|
|
435
|
+
},
|
|
436
|
+
options.language
|
|
437
|
+
);
|
|
308
438
|
const result2 = await generateObject({
|
|
309
439
|
model,
|
|
310
440
|
schema: ExplanationSchema,
|
|
@@ -338,11 +468,16 @@ Author: ${context.metadata.author}
|
|
|
338
468
|
Date: ${context.metadata.date}`;
|
|
339
469
|
targetType = "commit";
|
|
340
470
|
}
|
|
341
|
-
const prompt = buildPrompt(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
471
|
+
const prompt = buildPrompt(
|
|
472
|
+
template,
|
|
473
|
+
"explain",
|
|
474
|
+
{
|
|
475
|
+
targetType,
|
|
476
|
+
contextInfo,
|
|
477
|
+
diff: context.diff?.slice(0, 12e3) || ""
|
|
478
|
+
},
|
|
479
|
+
options.language
|
|
480
|
+
);
|
|
346
481
|
const result = await generateObject({
|
|
347
482
|
model,
|
|
348
483
|
schema: ExplanationSchema,
|
|
@@ -361,12 +496,19 @@ var CommitSearchSchema = z.object({
|
|
|
361
496
|
});
|
|
362
497
|
async function searchCommits(query, commits, options, maxResults = 5, template) {
|
|
363
498
|
const model = await getModel(options);
|
|
364
|
-
const commitList = commits.map(
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
499
|
+
const commitList = commits.map(
|
|
500
|
+
(c) => `${c.hash.slice(0, 7)} | ${c.author} | ${c.date.split("T")[0]} | ${c.message.split("\n")[0]}`
|
|
501
|
+
).join("\n");
|
|
502
|
+
const prompt = buildPrompt(
|
|
503
|
+
template,
|
|
504
|
+
"find",
|
|
505
|
+
{
|
|
506
|
+
query,
|
|
507
|
+
commits: commitList,
|
|
508
|
+
maxResults: String(maxResults)
|
|
509
|
+
},
|
|
510
|
+
options.language
|
|
511
|
+
);
|
|
370
512
|
const result = await generateObject({
|
|
371
513
|
model,
|
|
372
514
|
schema: CommitSearchSchema,
|
|
@@ -395,11 +537,17 @@ async function searchCommits(query, commits, options, maxResults = 5, template)
|
|
|
395
537
|
}
|
|
396
538
|
async function generateBranchName(description, options, context, template) {
|
|
397
539
|
const model = await getModel(options);
|
|
398
|
-
const prompt = buildPrompt(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
540
|
+
const prompt = buildPrompt(
|
|
541
|
+
template,
|
|
542
|
+
"branch",
|
|
543
|
+
{
|
|
544
|
+
description,
|
|
545
|
+
type: context?.type,
|
|
546
|
+
issue: context?.issue
|
|
547
|
+
},
|
|
548
|
+
options.language,
|
|
549
|
+
"Respond with ONLY the branch name, nothing else."
|
|
550
|
+
);
|
|
403
551
|
const result = await generateText({
|
|
404
552
|
model,
|
|
405
553
|
prompt,
|
|
@@ -409,9 +557,15 @@ async function generateBranchName(description, options, context, template) {
|
|
|
409
557
|
}
|
|
410
558
|
async function generateBranchNameFromDiff(diff, options, template) {
|
|
411
559
|
const model = await getModel(options);
|
|
412
|
-
const prompt = buildPrompt(
|
|
413
|
-
|
|
414
|
-
|
|
560
|
+
const prompt = buildPrompt(
|
|
561
|
+
template,
|
|
562
|
+
"checkout",
|
|
563
|
+
{
|
|
564
|
+
diff: diff.slice(0, 8e3)
|
|
565
|
+
},
|
|
566
|
+
options.language,
|
|
567
|
+
"Respond with ONLY the branch name, nothing else."
|
|
568
|
+
);
|
|
415
569
|
const result = await generateText({
|
|
416
570
|
model,
|
|
417
571
|
prompt,
|
|
@@ -421,9 +575,15 @@ async function generateBranchNameFromDiff(diff, options, template) {
|
|
|
421
575
|
}
|
|
422
576
|
async function generateStashName(diff, options, template) {
|
|
423
577
|
const model = await getModel(options);
|
|
424
|
-
const prompt = buildPrompt(
|
|
425
|
-
|
|
426
|
-
|
|
578
|
+
const prompt = buildPrompt(
|
|
579
|
+
template,
|
|
580
|
+
"stash",
|
|
581
|
+
{
|
|
582
|
+
diff: diff.slice(0, 4e3)
|
|
583
|
+
},
|
|
584
|
+
options.language,
|
|
585
|
+
"Respond with ONLY the stash name, nothing else."
|
|
586
|
+
);
|
|
427
587
|
const result = await generateText({
|
|
428
588
|
model,
|
|
429
589
|
prompt,
|
|
@@ -453,13 +613,18 @@ async function generateWorkSummary(context, options, format = "custom", template
|
|
|
453
613
|
const commitList = context.commits.map((c) => `- ${c.hash.slice(0, 7)} ${c.message.split("\n")[0]} (${c.date.split("T")[0]})`).join("\n");
|
|
454
614
|
const formatHint = format === "daily" ? "This is a daily report. Focus on today's accomplishments." : format === "weekly" ? "This is a weekly report. Summarize the week's work at a higher level." : `This is a summary from ${context.since}${context.until ? ` to ${context.until}` : ""}.`;
|
|
455
615
|
const period = `${context.since}${context.until ? ` to ${context.until}` : " to now"}`;
|
|
456
|
-
const prompt = buildPrompt(
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
616
|
+
const prompt = buildPrompt(
|
|
617
|
+
template,
|
|
618
|
+
"summary",
|
|
619
|
+
{
|
|
620
|
+
author: context.author,
|
|
621
|
+
period,
|
|
622
|
+
format: formatHint,
|
|
623
|
+
commits: commitList,
|
|
624
|
+
diff: context.diff?.slice(0, 6e3)
|
|
625
|
+
},
|
|
626
|
+
options.language
|
|
627
|
+
);
|
|
463
628
|
const result = await generateObject({
|
|
464
629
|
model,
|
|
465
630
|
schema: WorkSummarySchema,
|
|
@@ -475,12 +640,17 @@ async function generateWorkSummary(context, options, format = "custom", template
|
|
|
475
640
|
}
|
|
476
641
|
async function resolveConflict(conflictedContent, context, options, template) {
|
|
477
642
|
const model = await getModel(options);
|
|
478
|
-
const prompt = buildPrompt(
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
643
|
+
const prompt = buildPrompt(
|
|
644
|
+
template,
|
|
645
|
+
"merge",
|
|
646
|
+
{
|
|
647
|
+
filename: context.filename,
|
|
648
|
+
oursRef: context.oursRef,
|
|
649
|
+
theirsRef: context.theirsRef,
|
|
650
|
+
content: conflictedContent
|
|
651
|
+
},
|
|
652
|
+
options.language
|
|
653
|
+
);
|
|
484
654
|
const result = await generateObject({
|
|
485
655
|
model,
|
|
486
656
|
schema: ConflictResolutionSchema,
|
|
@@ -490,11 +660,17 @@ async function resolveConflict(conflictedContent, context, options, template) {
|
|
|
490
660
|
}
|
|
491
661
|
async function generateGitignore(context, options, template) {
|
|
492
662
|
const model = await getModel(options);
|
|
493
|
-
const prompt = buildPrompt(
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
663
|
+
const prompt = buildPrompt(
|
|
664
|
+
template,
|
|
665
|
+
"gitignore",
|
|
666
|
+
{
|
|
667
|
+
files: context.files,
|
|
668
|
+
configFiles: context.configFiles,
|
|
669
|
+
existingGitignore: context.existingGitignore
|
|
670
|
+
},
|
|
671
|
+
options.language,
|
|
672
|
+
"Respond with ONLY the .gitignore content, nothing else. No explanations or markdown code blocks."
|
|
673
|
+
);
|
|
498
674
|
const result = await generateText({
|
|
499
675
|
model,
|
|
500
676
|
prompt,
|
|
@@ -502,102 +678,6 @@ async function generateGitignore(context, options, template) {
|
|
|
502
678
|
});
|
|
503
679
|
return result.text.trim();
|
|
504
680
|
}
|
|
505
|
-
|
|
506
|
-
// src/lib/config.ts
|
|
507
|
-
import { homedir as homedir2 } from "os";
|
|
508
|
-
import { join as join2 } from "path";
|
|
509
|
-
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
510
|
-
import { execSync } from "child_process";
|
|
511
|
-
var DEFAULT_CONFIG = {
|
|
512
|
-
lang: "en"
|
|
513
|
-
};
|
|
514
|
-
function getGlobalConfigPath() {
|
|
515
|
-
const configDir = join2(homedir2(), ".config", "gut");
|
|
516
|
-
return join2(configDir, "config.json");
|
|
517
|
-
}
|
|
518
|
-
function getRepoRoot() {
|
|
519
|
-
try {
|
|
520
|
-
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
521
|
-
} catch {
|
|
522
|
-
return null;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
function getLocalConfigPath() {
|
|
526
|
-
const repoRoot = getRepoRoot();
|
|
527
|
-
if (!repoRoot) return null;
|
|
528
|
-
return join2(repoRoot, ".gut", "config.json");
|
|
529
|
-
}
|
|
530
|
-
function ensureGlobalConfigDir() {
|
|
531
|
-
const configDir = join2(homedir2(), ".config", "gut");
|
|
532
|
-
if (!existsSync2(configDir)) {
|
|
533
|
-
mkdirSync(configDir, { recursive: true });
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
function ensureLocalConfigDir() {
|
|
537
|
-
const repoRoot = getRepoRoot();
|
|
538
|
-
if (!repoRoot) return;
|
|
539
|
-
const gutDir = join2(repoRoot, ".gut");
|
|
540
|
-
if (!existsSync2(gutDir)) {
|
|
541
|
-
mkdirSync(gutDir, { recursive: true });
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
function readConfigFile(path) {
|
|
545
|
-
if (!existsSync2(path)) return {};
|
|
546
|
-
try {
|
|
547
|
-
return JSON.parse(readFileSync2(path, "utf-8"));
|
|
548
|
-
} catch {
|
|
549
|
-
return {};
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
function getGlobalConfig() {
|
|
553
|
-
const globalPath = getGlobalConfigPath();
|
|
554
|
-
return { ...DEFAULT_CONFIG, ...readConfigFile(globalPath) };
|
|
555
|
-
}
|
|
556
|
-
function getLocalConfig() {
|
|
557
|
-
const localPath = getLocalConfigPath();
|
|
558
|
-
if (!localPath) return {};
|
|
559
|
-
return readConfigFile(localPath);
|
|
560
|
-
}
|
|
561
|
-
function getConfig() {
|
|
562
|
-
const globalConfig = getGlobalConfig();
|
|
563
|
-
const localConfig = getLocalConfig();
|
|
564
|
-
return { ...globalConfig, ...localConfig };
|
|
565
|
-
}
|
|
566
|
-
function setGlobalConfig(key, value) {
|
|
567
|
-
ensureGlobalConfigDir();
|
|
568
|
-
const config = getGlobalConfig();
|
|
569
|
-
config[key] = value;
|
|
570
|
-
writeFileSync(getGlobalConfigPath(), JSON.stringify(config, null, 2));
|
|
571
|
-
}
|
|
572
|
-
function setLocalConfig(key, value) {
|
|
573
|
-
const localPath = getLocalConfigPath();
|
|
574
|
-
if (!localPath) {
|
|
575
|
-
throw new Error("Not in a git repository");
|
|
576
|
-
}
|
|
577
|
-
ensureLocalConfigDir();
|
|
578
|
-
const config = getLocalConfig();
|
|
579
|
-
config[key] = value;
|
|
580
|
-
writeFileSync(localPath, JSON.stringify(config, null, 2));
|
|
581
|
-
}
|
|
582
|
-
function getLanguage() {
|
|
583
|
-
return getConfig().lang;
|
|
584
|
-
}
|
|
585
|
-
function setLanguage(lang, local = false) {
|
|
586
|
-
if (local) {
|
|
587
|
-
setLocalConfig("lang", lang);
|
|
588
|
-
} else {
|
|
589
|
-
setGlobalConfig("lang", lang);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
function getLanguageInstruction(lang) {
|
|
593
|
-
switch (lang) {
|
|
594
|
-
case "ja":
|
|
595
|
-
return "\n\nIMPORTANT: Respond in Japanese (\u65E5\u672C\u8A9E\u3067\u56DE\u7B54\u3057\u3066\u304F\u3060\u3055\u3044).";
|
|
596
|
-
case "en":
|
|
597
|
-
default:
|
|
598
|
-
return "";
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
681
|
export {
|
|
602
682
|
deleteApiKey,
|
|
603
683
|
findTemplate,
|