gut-cli 0.1.19 → 0.1.21
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/branch.md +1 -13
- package/.gut/changelog.md +1 -13
- package/.gut/checkout.md +1 -9
- package/.gut/commit.md +1 -11
- package/.gut/explain-file.md +1 -9
- package/.gut/explain.md +1 -11
- package/.gut/find.md +2 -8
- package/.gut/gitignore.md +2 -24
- package/.gut/merge.md +1 -12
- package/.gut/pr.md +1 -23
- package/.gut/review.md +1 -14
- package/.gut/stash.md +1 -7
- package/.gut/summary.md +1 -19
- package/dist/index.js +190 -185
- package/dist/index.js.map +1 -1
- package/dist/lib/index.d.ts +7 -6
- package/dist/lib/index.js +163 -151
- package/dist/lib/index.js.map +1 -1
- package/package.json +1 -1
package/dist/lib/index.d.ts
CHANGED
|
@@ -9,11 +9,17 @@ declare function listProviders(): Promise<{
|
|
|
9
9
|
hasKey: boolean;
|
|
10
10
|
}[]>;
|
|
11
11
|
|
|
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
|
+
|
|
12
17
|
interface AIOptions {
|
|
13
18
|
provider: Provider;
|
|
14
19
|
model?: string;
|
|
15
20
|
ollamaBaseUrl?: string;
|
|
16
21
|
apiKey?: string;
|
|
22
|
+
language?: Language;
|
|
17
23
|
}
|
|
18
24
|
/**
|
|
19
25
|
* Find a user's project template from .gut/ folder
|
|
@@ -292,9 +298,4 @@ declare function generateGitignore(context: {
|
|
|
292
298
|
existingGitignore?: string;
|
|
293
299
|
}, options: AIOptions, template?: string): Promise<string>;
|
|
294
300
|
|
|
295
|
-
type Language
|
|
296
|
-
declare function getLanguage(): Language;
|
|
297
|
-
declare function setLanguage(lang: Language, local?: boolean): void;
|
|
298
|
-
declare function getLanguageInstruction(lang: Language): string;
|
|
299
|
-
|
|
300
|
-
export { type AIOptions, type Changelog, type CodeReview, type CommitSearchResult, type ConflictResolution, type Explanation, type Provider, type WorkSummary, deleteApiKey, findTemplate, generateBranchName, generateBranchNameFromDiff, generateChangelog, generateCodeReview, generateCommitMessage, generateExplanation, generateGitignore, generatePRDescription, generateStashName, generateWorkSummary, getApiKey, getLanguage, getLanguageInstruction, listProviders, resolveConflict, saveApiKey, searchCommits, setLanguage };
|
|
301
|
+
export { type AIOptions, type Changelog, type CodeReview, type CommitSearchResult, type ConflictResolution, type Explanation, type Language, type Provider, type WorkSummary, deleteApiKey, findTemplate, generateBranchName, generateBranchNameFromDiff, generateChangelog, generateCodeReview, generateCommitMessage, generateExplanation, generateGitignore, generatePRDescription, generateStashName, generateWorkSummary, getApiKey, getLanguage, getLanguageInstruction, listProviders, resolveConflict, saveApiKey, searchCommits, setLanguage };
|
package/dist/lib/index.js
CHANGED
|
@@ -5,9 +5,9 @@ import { createOpenAI } from "@ai-sdk/openai";
|
|
|
5
5
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
6
6
|
import { createOllama } from "ollama-ai-provider";
|
|
7
7
|
import { z } from "zod";
|
|
8
|
-
import { existsSync
|
|
9
|
-
import { join
|
|
10
|
-
import { homedir
|
|
8
|
+
import { existsSync, readFileSync } from "fs";
|
|
9
|
+
import { join, dirname } from "path";
|
|
10
|
+
import { homedir } from "os";
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
12
|
|
|
13
13
|
// src/lib/credentials.ts
|
|
@@ -80,138 +80,42 @@ async function listProviders() {
|
|
|
80
80
|
return results;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
// src/lib/config.ts
|
|
84
|
-
import { homedir } from "os";
|
|
85
|
-
import { join } from "path";
|
|
86
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
87
|
-
import { execSync } from "child_process";
|
|
88
|
-
var DEFAULT_CONFIG = {
|
|
89
|
-
lang: "en"
|
|
90
|
-
};
|
|
91
|
-
function getGlobalConfigPath() {
|
|
92
|
-
const configDir = join(homedir(), ".config", "gut");
|
|
93
|
-
return join(configDir, "config.json");
|
|
94
|
-
}
|
|
95
|
-
function getRepoRoot() {
|
|
96
|
-
try {
|
|
97
|
-
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
98
|
-
} catch {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function getLocalConfigPath() {
|
|
103
|
-
const repoRoot = getRepoRoot();
|
|
104
|
-
if (!repoRoot) return null;
|
|
105
|
-
return join(repoRoot, ".gut", "config.json");
|
|
106
|
-
}
|
|
107
|
-
function ensureGlobalConfigDir() {
|
|
108
|
-
const configDir = join(homedir(), ".config", "gut");
|
|
109
|
-
if (!existsSync(configDir)) {
|
|
110
|
-
mkdirSync(configDir, { recursive: true });
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
function ensureLocalConfigDir() {
|
|
114
|
-
const repoRoot = getRepoRoot();
|
|
115
|
-
if (!repoRoot) return;
|
|
116
|
-
const gutDir = join(repoRoot, ".gut");
|
|
117
|
-
if (!existsSync(gutDir)) {
|
|
118
|
-
mkdirSync(gutDir, { recursive: true });
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
function readConfigFile(path) {
|
|
122
|
-
if (!existsSync(path)) return {};
|
|
123
|
-
try {
|
|
124
|
-
return JSON.parse(readFileSync(path, "utf-8"));
|
|
125
|
-
} catch {
|
|
126
|
-
return {};
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
function getGlobalConfig() {
|
|
130
|
-
const globalPath = getGlobalConfigPath();
|
|
131
|
-
return { ...DEFAULT_CONFIG, ...readConfigFile(globalPath) };
|
|
132
|
-
}
|
|
133
|
-
function getLocalConfig() {
|
|
134
|
-
const localPath = getLocalConfigPath();
|
|
135
|
-
if (!localPath) return {};
|
|
136
|
-
return readConfigFile(localPath);
|
|
137
|
-
}
|
|
138
|
-
function getConfig() {
|
|
139
|
-
const globalConfig = getGlobalConfig();
|
|
140
|
-
const localConfig = getLocalConfig();
|
|
141
|
-
return { ...globalConfig, ...localConfig };
|
|
142
|
-
}
|
|
143
|
-
function setGlobalConfig(key, value) {
|
|
144
|
-
ensureGlobalConfigDir();
|
|
145
|
-
const config = getGlobalConfig();
|
|
146
|
-
config[key] = value;
|
|
147
|
-
writeFileSync(getGlobalConfigPath(), JSON.stringify(config, null, 2));
|
|
148
|
-
}
|
|
149
|
-
function setLocalConfig(key, value) {
|
|
150
|
-
const localPath = getLocalConfigPath();
|
|
151
|
-
if (!localPath) {
|
|
152
|
-
throw new Error("Not in a git repository");
|
|
153
|
-
}
|
|
154
|
-
ensureLocalConfigDir();
|
|
155
|
-
const config = getLocalConfig();
|
|
156
|
-
config[key] = value;
|
|
157
|
-
writeFileSync(localPath, JSON.stringify(config, null, 2));
|
|
158
|
-
}
|
|
159
|
-
function getLanguage() {
|
|
160
|
-
return getConfig().lang;
|
|
161
|
-
}
|
|
162
|
-
function setLanguage(lang, local = false) {
|
|
163
|
-
if (local) {
|
|
164
|
-
setLocalConfig("lang", lang);
|
|
165
|
-
} else {
|
|
166
|
-
setGlobalConfig("lang", lang);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
function getLanguageInstruction(lang) {
|
|
170
|
-
switch (lang) {
|
|
171
|
-
case "ja":
|
|
172
|
-
return "\n\nIMPORTANT: Respond in Japanese (\u65E5\u672C\u8A9E\u3067\u56DE\u7B54\u3057\u3066\u304F\u3060\u3055\u3044).";
|
|
173
|
-
case "en":
|
|
174
|
-
default:
|
|
175
|
-
return "";
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
83
|
// src/lib/ai.ts
|
|
180
84
|
var __filename = fileURLToPath(import.meta.url);
|
|
181
85
|
var __dirname = dirname(__filename);
|
|
182
86
|
function findGutRoot() {
|
|
183
87
|
let current = __dirname;
|
|
184
88
|
for (let i = 0; i < 5; i++) {
|
|
185
|
-
const gutPath =
|
|
186
|
-
if (
|
|
89
|
+
const gutPath = join(current, ".gut");
|
|
90
|
+
if (existsSync(gutPath)) {
|
|
187
91
|
return current;
|
|
188
92
|
}
|
|
189
93
|
current = dirname(current);
|
|
190
94
|
}
|
|
191
|
-
return
|
|
95
|
+
return join(__dirname, "..");
|
|
192
96
|
}
|
|
193
97
|
var GUT_ROOT = findGutRoot();
|
|
194
98
|
function loadTemplate(name) {
|
|
195
|
-
const templatePath =
|
|
196
|
-
if (
|
|
197
|
-
return
|
|
99
|
+
const templatePath = join(GUT_ROOT, ".gut", `${name}.md`);
|
|
100
|
+
if (existsSync(templatePath)) {
|
|
101
|
+
return readFileSync(templatePath, "utf-8");
|
|
198
102
|
}
|
|
199
103
|
throw new Error(`Template not found: ${templatePath}`);
|
|
200
104
|
}
|
|
201
105
|
function getGlobalTemplatesDir() {
|
|
202
|
-
return
|
|
106
|
+
return join(homedir(), ".config", "gut", "templates");
|
|
203
107
|
}
|
|
204
108
|
function findGlobalTemplate(templateName) {
|
|
205
|
-
const templatePath =
|
|
206
|
-
if (
|
|
207
|
-
return
|
|
109
|
+
const templatePath = join(getGlobalTemplatesDir(), `${templateName}.md`);
|
|
110
|
+
if (existsSync(templatePath)) {
|
|
111
|
+
return readFileSync(templatePath, "utf-8");
|
|
208
112
|
}
|
|
209
113
|
return null;
|
|
210
114
|
}
|
|
211
115
|
function findTemplate(repoRoot, templateName) {
|
|
212
|
-
const projectTemplatePath =
|
|
213
|
-
if (
|
|
214
|
-
return
|
|
116
|
+
const projectTemplatePath = join(repoRoot, ".gut", `${templateName}.md`);
|
|
117
|
+
if (existsSync(projectTemplatePath)) {
|
|
118
|
+
return readFileSync(projectTemplatePath, "utf-8");
|
|
215
119
|
}
|
|
216
120
|
const globalTemplate = findGlobalTemplate(templateName);
|
|
217
121
|
if (globalTemplate) {
|
|
@@ -219,19 +123,25 @@ function findTemplate(repoRoot, templateName) {
|
|
|
219
123
|
}
|
|
220
124
|
return null;
|
|
221
125
|
}
|
|
222
|
-
function
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (langInstruction) {
|
|
232
|
-
result += langInstruction;
|
|
126
|
+
function buildPrompt(userTemplate, templateName, context, language, outputFormat) {
|
|
127
|
+
let contextXml = "<context>\n";
|
|
128
|
+
for (const [key, value] of Object.entries(context)) {
|
|
129
|
+
if (value) {
|
|
130
|
+
contextXml += `<${key}>
|
|
131
|
+
${value}
|
|
132
|
+
</${key}>
|
|
133
|
+
`;
|
|
134
|
+
}
|
|
233
135
|
}
|
|
234
|
-
|
|
136
|
+
contextXml += "</context>\n\n";
|
|
137
|
+
const template = userTemplate || loadTemplate(templateName);
|
|
138
|
+
const langInstruction = language === "ja" ? "\n\nIMPORTANT: Respond in Japanese (\u65E5\u672C\u8A9E\u3067\u56DE\u7B54\u3057\u3066\u304F\u3060\u3055\u3044)." : "";
|
|
139
|
+
const outputSection = outputFormat ? `
|
|
140
|
+
|
|
141
|
+
<output-format>
|
|
142
|
+
${outputFormat}
|
|
143
|
+
</output-format>` : "";
|
|
144
|
+
return contextXml + "<instructions>\n" + template + langInstruction + "\n</instructions>" + outputSection;
|
|
235
145
|
}
|
|
236
146
|
var DEFAULT_MODELS = {
|
|
237
147
|
gemini: "gemini-2.0-flash",
|
|
@@ -279,9 +189,9 @@ async function getModel(options) {
|
|
|
279
189
|
}
|
|
280
190
|
async function generateCommitMessage(diff, options, template) {
|
|
281
191
|
const model = await getModel(options);
|
|
282
|
-
const prompt =
|
|
192
|
+
const prompt = buildPrompt(template, "commit", {
|
|
283
193
|
diff: diff.slice(0, 8e3)
|
|
284
|
-
});
|
|
194
|
+
}, options.language, "Respond with ONLY the commit message, nothing else.");
|
|
285
195
|
const result = await generateText({
|
|
286
196
|
model,
|
|
287
197
|
prompt,
|
|
@@ -291,12 +201,18 @@ async function generateCommitMessage(diff, options, template) {
|
|
|
291
201
|
}
|
|
292
202
|
async function generatePRDescription(context, options, template) {
|
|
293
203
|
const model = await getModel(options);
|
|
294
|
-
const prompt =
|
|
204
|
+
const prompt = buildPrompt(template, "pr", {
|
|
295
205
|
baseBranch: context.baseBranch,
|
|
296
206
|
currentBranch: context.currentBranch,
|
|
297
207
|
commits: context.commits.map((c) => `- ${c}`).join("\n"),
|
|
298
208
|
diff: context.diff.slice(0, 6e3)
|
|
299
|
-
}
|
|
209
|
+
}, options.language, `Respond in JSON format:
|
|
210
|
+
\`\`\`json
|
|
211
|
+
{
|
|
212
|
+
"title": "...",
|
|
213
|
+
"body": "..."
|
|
214
|
+
}
|
|
215
|
+
\`\`\``);
|
|
300
216
|
const result = await generateText({
|
|
301
217
|
model,
|
|
302
218
|
prompt,
|
|
@@ -327,9 +243,9 @@ var CodeReviewSchema = z.object({
|
|
|
327
243
|
});
|
|
328
244
|
async function generateCodeReview(diff, options, template) {
|
|
329
245
|
const model = await getModel(options);
|
|
330
|
-
const prompt =
|
|
246
|
+
const prompt = buildPrompt(template, "review", {
|
|
331
247
|
diff: diff.slice(0, 1e4)
|
|
332
|
-
});
|
|
248
|
+
}, options.language);
|
|
333
249
|
const result = await generateObject({
|
|
334
250
|
model,
|
|
335
251
|
schema: CodeReviewSchema,
|
|
@@ -351,13 +267,13 @@ var ChangelogSchema = z.object({
|
|
|
351
267
|
async function generateChangelog(context, options, template) {
|
|
352
268
|
const model = await getModel(options);
|
|
353
269
|
const commitList = context.commits.map((c) => `- ${c.hash.slice(0, 7)} ${c.message} (${c.author})`).join("\n");
|
|
354
|
-
const prompt =
|
|
270
|
+
const prompt = buildPrompt(template, "changelog", {
|
|
355
271
|
fromRef: context.fromRef,
|
|
356
272
|
toRef: context.toRef,
|
|
357
273
|
commits: commitList,
|
|
358
274
|
diff: context.diff.slice(0, 8e3),
|
|
359
275
|
todayDate: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
360
|
-
});
|
|
276
|
+
}, options.language);
|
|
361
277
|
const result = await generateObject({
|
|
362
278
|
model,
|
|
363
279
|
schema: ChangelogSchema,
|
|
@@ -385,10 +301,10 @@ var ExplanationSchema = z.object({
|
|
|
385
301
|
async function generateExplanation(context, options, template) {
|
|
386
302
|
const model = await getModel(options);
|
|
387
303
|
if (context.type === "file-content") {
|
|
388
|
-
const prompt2 =
|
|
304
|
+
const prompt2 = buildPrompt(template, "explain-file", {
|
|
389
305
|
filePath: context.metadata.filePath || "",
|
|
390
306
|
content: context.content?.slice(0, 15e3) || ""
|
|
391
|
-
});
|
|
307
|
+
}, options.language);
|
|
392
308
|
const result2 = await generateObject({
|
|
393
309
|
model,
|
|
394
310
|
schema: ExplanationSchema,
|
|
@@ -422,11 +338,11 @@ Author: ${context.metadata.author}
|
|
|
422
338
|
Date: ${context.metadata.date}`;
|
|
423
339
|
targetType = "commit";
|
|
424
340
|
}
|
|
425
|
-
const prompt =
|
|
341
|
+
const prompt = buildPrompt(template, "explain", {
|
|
426
342
|
targetType,
|
|
427
|
-
|
|
343
|
+
contextInfo,
|
|
428
344
|
diff: context.diff?.slice(0, 12e3) || ""
|
|
429
|
-
});
|
|
345
|
+
}, options.language);
|
|
430
346
|
const result = await generateObject({
|
|
431
347
|
model,
|
|
432
348
|
schema: ExplanationSchema,
|
|
@@ -446,11 +362,11 @@ var CommitSearchSchema = z.object({
|
|
|
446
362
|
async function searchCommits(query, commits, options, maxResults = 5, template) {
|
|
447
363
|
const model = await getModel(options);
|
|
448
364
|
const commitList = commits.map((c) => `${c.hash.slice(0, 7)} | ${c.author} | ${c.date.split("T")[0]} | ${c.message.split("\n")[0]}`).join("\n");
|
|
449
|
-
const prompt =
|
|
365
|
+
const prompt = buildPrompt(template, "find", {
|
|
450
366
|
query,
|
|
451
367
|
commits: commitList,
|
|
452
368
|
maxResults: String(maxResults)
|
|
453
|
-
});
|
|
369
|
+
}, options.language);
|
|
454
370
|
const result = await generateObject({
|
|
455
371
|
model,
|
|
456
372
|
schema: CommitSearchSchema,
|
|
@@ -479,11 +395,11 @@ async function searchCommits(query, commits, options, maxResults = 5, template)
|
|
|
479
395
|
}
|
|
480
396
|
async function generateBranchName(description, options, context, template) {
|
|
481
397
|
const model = await getModel(options);
|
|
482
|
-
const prompt =
|
|
398
|
+
const prompt = buildPrompt(template, "branch", {
|
|
483
399
|
description,
|
|
484
400
|
type: context?.type,
|
|
485
401
|
issue: context?.issue
|
|
486
|
-
});
|
|
402
|
+
}, options.language, "Respond with ONLY the branch name, nothing else.");
|
|
487
403
|
const result = await generateText({
|
|
488
404
|
model,
|
|
489
405
|
prompt,
|
|
@@ -493,9 +409,9 @@ async function generateBranchName(description, options, context, template) {
|
|
|
493
409
|
}
|
|
494
410
|
async function generateBranchNameFromDiff(diff, options, template) {
|
|
495
411
|
const model = await getModel(options);
|
|
496
|
-
const prompt =
|
|
412
|
+
const prompt = buildPrompt(template, "checkout", {
|
|
497
413
|
diff: diff.slice(0, 8e3)
|
|
498
|
-
});
|
|
414
|
+
}, options.language, "Respond with ONLY the branch name, nothing else.");
|
|
499
415
|
const result = await generateText({
|
|
500
416
|
model,
|
|
501
417
|
prompt,
|
|
@@ -505,9 +421,9 @@ async function generateBranchNameFromDiff(diff, options, template) {
|
|
|
505
421
|
}
|
|
506
422
|
async function generateStashName(diff, options, template) {
|
|
507
423
|
const model = await getModel(options);
|
|
508
|
-
const prompt =
|
|
424
|
+
const prompt = buildPrompt(template, "stash", {
|
|
509
425
|
diff: diff.slice(0, 4e3)
|
|
510
|
-
});
|
|
426
|
+
}, options.language, "Respond with ONLY the stash name, nothing else.");
|
|
511
427
|
const result = await generateText({
|
|
512
428
|
model,
|
|
513
429
|
prompt,
|
|
@@ -537,13 +453,13 @@ async function generateWorkSummary(context, options, format = "custom", template
|
|
|
537
453
|
const commitList = context.commits.map((c) => `- ${c.hash.slice(0, 7)} ${c.message.split("\n")[0]} (${c.date.split("T")[0]})`).join("\n");
|
|
538
454
|
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}` : ""}.`;
|
|
539
455
|
const period = `${context.since}${context.until ? ` to ${context.until}` : " to now"}`;
|
|
540
|
-
const prompt =
|
|
456
|
+
const prompt = buildPrompt(template, "summary", {
|
|
541
457
|
author: context.author,
|
|
542
458
|
period,
|
|
543
459
|
format: formatHint,
|
|
544
460
|
commits: commitList,
|
|
545
461
|
diff: context.diff?.slice(0, 6e3)
|
|
546
|
-
});
|
|
462
|
+
}, options.language);
|
|
547
463
|
const result = await generateObject({
|
|
548
464
|
model,
|
|
549
465
|
schema: WorkSummarySchema,
|
|
@@ -559,12 +475,12 @@ async function generateWorkSummary(context, options, format = "custom", template
|
|
|
559
475
|
}
|
|
560
476
|
async function resolveConflict(conflictedContent, context, options, template) {
|
|
561
477
|
const model = await getModel(options);
|
|
562
|
-
const prompt =
|
|
478
|
+
const prompt = buildPrompt(template, "merge", {
|
|
563
479
|
filename: context.filename,
|
|
564
480
|
oursRef: context.oursRef,
|
|
565
481
|
theirsRef: context.theirsRef,
|
|
566
482
|
content: conflictedContent
|
|
567
|
-
});
|
|
483
|
+
}, options.language);
|
|
568
484
|
const result = await generateObject({
|
|
569
485
|
model,
|
|
570
486
|
schema: ConflictResolutionSchema,
|
|
@@ -574,11 +490,11 @@ async function resolveConflict(conflictedContent, context, options, template) {
|
|
|
574
490
|
}
|
|
575
491
|
async function generateGitignore(context, options, template) {
|
|
576
492
|
const model = await getModel(options);
|
|
577
|
-
const prompt =
|
|
493
|
+
const prompt = buildPrompt(template, "gitignore", {
|
|
578
494
|
files: context.files,
|
|
579
495
|
configFiles: context.configFiles,
|
|
580
496
|
existingGitignore: context.existingGitignore
|
|
581
|
-
});
|
|
497
|
+
}, options.language, "Respond with ONLY the .gitignore content, nothing else. No explanations or markdown code blocks.");
|
|
582
498
|
const result = await generateText({
|
|
583
499
|
model,
|
|
584
500
|
prompt,
|
|
@@ -586,6 +502,102 @@ async function generateGitignore(context, options, template) {
|
|
|
586
502
|
});
|
|
587
503
|
return result.text.trim();
|
|
588
504
|
}
|
|
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
|
+
}
|
|
589
601
|
export {
|
|
590
602
|
deleteApiKey,
|
|
591
603
|
findTemplate,
|