gut-cli 0.1.19 → 0.1.20
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 -7
- 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 -13
- package/.gut/review.md +1 -7
- package/.gut/stash.md +1 -7
- package/.gut/summary.md +1 -19
- package/dist/index.js +179 -185
- package/dist/index.js.map +1 -1
- package/dist/lib/index.d.ts +7 -6
- package/dist/lib/index.js +152 -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,20 @@ 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) {
|
|
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
|
+
return contextXml + "<instructions>\n" + template + langInstruction + "\n</instructions>";
|
|
235
140
|
}
|
|
236
141
|
var DEFAULT_MODELS = {
|
|
237
142
|
gemini: "gemini-2.0-flash",
|
|
@@ -279,9 +184,9 @@ async function getModel(options) {
|
|
|
279
184
|
}
|
|
280
185
|
async function generateCommitMessage(diff, options, template) {
|
|
281
186
|
const model = await getModel(options);
|
|
282
|
-
const prompt =
|
|
187
|
+
const prompt = buildPrompt(template, "commit", {
|
|
283
188
|
diff: diff.slice(0, 8e3)
|
|
284
|
-
});
|
|
189
|
+
}, options.language);
|
|
285
190
|
const result = await generateText({
|
|
286
191
|
model,
|
|
287
192
|
prompt,
|
|
@@ -291,12 +196,12 @@ async function generateCommitMessage(diff, options, template) {
|
|
|
291
196
|
}
|
|
292
197
|
async function generatePRDescription(context, options, template) {
|
|
293
198
|
const model = await getModel(options);
|
|
294
|
-
const prompt =
|
|
199
|
+
const prompt = buildPrompt(template, "pr", {
|
|
295
200
|
baseBranch: context.baseBranch,
|
|
296
201
|
currentBranch: context.currentBranch,
|
|
297
202
|
commits: context.commits.map((c) => `- ${c}`).join("\n"),
|
|
298
203
|
diff: context.diff.slice(0, 6e3)
|
|
299
|
-
});
|
|
204
|
+
}, options.language);
|
|
300
205
|
const result = await generateText({
|
|
301
206
|
model,
|
|
302
207
|
prompt,
|
|
@@ -327,9 +232,9 @@ var CodeReviewSchema = z.object({
|
|
|
327
232
|
});
|
|
328
233
|
async function generateCodeReview(diff, options, template) {
|
|
329
234
|
const model = await getModel(options);
|
|
330
|
-
const prompt =
|
|
235
|
+
const prompt = buildPrompt(template, "review", {
|
|
331
236
|
diff: diff.slice(0, 1e4)
|
|
332
|
-
});
|
|
237
|
+
}, options.language);
|
|
333
238
|
const result = await generateObject({
|
|
334
239
|
model,
|
|
335
240
|
schema: CodeReviewSchema,
|
|
@@ -351,13 +256,13 @@ var ChangelogSchema = z.object({
|
|
|
351
256
|
async function generateChangelog(context, options, template) {
|
|
352
257
|
const model = await getModel(options);
|
|
353
258
|
const commitList = context.commits.map((c) => `- ${c.hash.slice(0, 7)} ${c.message} (${c.author})`).join("\n");
|
|
354
|
-
const prompt =
|
|
259
|
+
const prompt = buildPrompt(template, "changelog", {
|
|
355
260
|
fromRef: context.fromRef,
|
|
356
261
|
toRef: context.toRef,
|
|
357
262
|
commits: commitList,
|
|
358
263
|
diff: context.diff.slice(0, 8e3),
|
|
359
264
|
todayDate: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
360
|
-
});
|
|
265
|
+
}, options.language);
|
|
361
266
|
const result = await generateObject({
|
|
362
267
|
model,
|
|
363
268
|
schema: ChangelogSchema,
|
|
@@ -385,10 +290,10 @@ var ExplanationSchema = z.object({
|
|
|
385
290
|
async function generateExplanation(context, options, template) {
|
|
386
291
|
const model = await getModel(options);
|
|
387
292
|
if (context.type === "file-content") {
|
|
388
|
-
const prompt2 =
|
|
293
|
+
const prompt2 = buildPrompt(template, "explain-file", {
|
|
389
294
|
filePath: context.metadata.filePath || "",
|
|
390
295
|
content: context.content?.slice(0, 15e3) || ""
|
|
391
|
-
});
|
|
296
|
+
}, options.language);
|
|
392
297
|
const result2 = await generateObject({
|
|
393
298
|
model,
|
|
394
299
|
schema: ExplanationSchema,
|
|
@@ -422,11 +327,11 @@ Author: ${context.metadata.author}
|
|
|
422
327
|
Date: ${context.metadata.date}`;
|
|
423
328
|
targetType = "commit";
|
|
424
329
|
}
|
|
425
|
-
const prompt =
|
|
330
|
+
const prompt = buildPrompt(template, "explain", {
|
|
426
331
|
targetType,
|
|
427
|
-
|
|
332
|
+
contextInfo,
|
|
428
333
|
diff: context.diff?.slice(0, 12e3) || ""
|
|
429
|
-
});
|
|
334
|
+
}, options.language);
|
|
430
335
|
const result = await generateObject({
|
|
431
336
|
model,
|
|
432
337
|
schema: ExplanationSchema,
|
|
@@ -446,11 +351,11 @@ var CommitSearchSchema = z.object({
|
|
|
446
351
|
async function searchCommits(query, commits, options, maxResults = 5, template) {
|
|
447
352
|
const model = await getModel(options);
|
|
448
353
|
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 =
|
|
354
|
+
const prompt = buildPrompt(template, "find", {
|
|
450
355
|
query,
|
|
451
356
|
commits: commitList,
|
|
452
357
|
maxResults: String(maxResults)
|
|
453
|
-
});
|
|
358
|
+
}, options.language);
|
|
454
359
|
const result = await generateObject({
|
|
455
360
|
model,
|
|
456
361
|
schema: CommitSearchSchema,
|
|
@@ -479,11 +384,11 @@ async function searchCommits(query, commits, options, maxResults = 5, template)
|
|
|
479
384
|
}
|
|
480
385
|
async function generateBranchName(description, options, context, template) {
|
|
481
386
|
const model = await getModel(options);
|
|
482
|
-
const prompt =
|
|
387
|
+
const prompt = buildPrompt(template, "branch", {
|
|
483
388
|
description,
|
|
484
389
|
type: context?.type,
|
|
485
390
|
issue: context?.issue
|
|
486
|
-
});
|
|
391
|
+
}, options.language);
|
|
487
392
|
const result = await generateText({
|
|
488
393
|
model,
|
|
489
394
|
prompt,
|
|
@@ -493,9 +398,9 @@ async function generateBranchName(description, options, context, template) {
|
|
|
493
398
|
}
|
|
494
399
|
async function generateBranchNameFromDiff(diff, options, template) {
|
|
495
400
|
const model = await getModel(options);
|
|
496
|
-
const prompt =
|
|
401
|
+
const prompt = buildPrompt(template, "checkout", {
|
|
497
402
|
diff: diff.slice(0, 8e3)
|
|
498
|
-
});
|
|
403
|
+
}, options.language);
|
|
499
404
|
const result = await generateText({
|
|
500
405
|
model,
|
|
501
406
|
prompt,
|
|
@@ -505,9 +410,9 @@ async function generateBranchNameFromDiff(diff, options, template) {
|
|
|
505
410
|
}
|
|
506
411
|
async function generateStashName(diff, options, template) {
|
|
507
412
|
const model = await getModel(options);
|
|
508
|
-
const prompt =
|
|
413
|
+
const prompt = buildPrompt(template, "stash", {
|
|
509
414
|
diff: diff.slice(0, 4e3)
|
|
510
|
-
});
|
|
415
|
+
}, options.language);
|
|
511
416
|
const result = await generateText({
|
|
512
417
|
model,
|
|
513
418
|
prompt,
|
|
@@ -537,13 +442,13 @@ async function generateWorkSummary(context, options, format = "custom", template
|
|
|
537
442
|
const commitList = context.commits.map((c) => `- ${c.hash.slice(0, 7)} ${c.message.split("\n")[0]} (${c.date.split("T")[0]})`).join("\n");
|
|
538
443
|
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
444
|
const period = `${context.since}${context.until ? ` to ${context.until}` : " to now"}`;
|
|
540
|
-
const prompt =
|
|
445
|
+
const prompt = buildPrompt(template, "summary", {
|
|
541
446
|
author: context.author,
|
|
542
447
|
period,
|
|
543
448
|
format: formatHint,
|
|
544
449
|
commits: commitList,
|
|
545
450
|
diff: context.diff?.slice(0, 6e3)
|
|
546
|
-
});
|
|
451
|
+
}, options.language);
|
|
547
452
|
const result = await generateObject({
|
|
548
453
|
model,
|
|
549
454
|
schema: WorkSummarySchema,
|
|
@@ -559,12 +464,12 @@ async function generateWorkSummary(context, options, format = "custom", template
|
|
|
559
464
|
}
|
|
560
465
|
async function resolveConflict(conflictedContent, context, options, template) {
|
|
561
466
|
const model = await getModel(options);
|
|
562
|
-
const prompt =
|
|
467
|
+
const prompt = buildPrompt(template, "merge", {
|
|
563
468
|
filename: context.filename,
|
|
564
469
|
oursRef: context.oursRef,
|
|
565
470
|
theirsRef: context.theirsRef,
|
|
566
471
|
content: conflictedContent
|
|
567
|
-
});
|
|
472
|
+
}, options.language);
|
|
568
473
|
const result = await generateObject({
|
|
569
474
|
model,
|
|
570
475
|
schema: ConflictResolutionSchema,
|
|
@@ -574,11 +479,11 @@ async function resolveConflict(conflictedContent, context, options, template) {
|
|
|
574
479
|
}
|
|
575
480
|
async function generateGitignore(context, options, template) {
|
|
576
481
|
const model = await getModel(options);
|
|
577
|
-
const prompt =
|
|
482
|
+
const prompt = buildPrompt(template, "gitignore", {
|
|
578
483
|
files: context.files,
|
|
579
484
|
configFiles: context.configFiles,
|
|
580
485
|
existingGitignore: context.existingGitignore
|
|
581
|
-
});
|
|
486
|
+
}, options.language);
|
|
582
487
|
const result = await generateText({
|
|
583
488
|
model,
|
|
584
489
|
prompt,
|
|
@@ -586,6 +491,102 @@ async function generateGitignore(context, options, template) {
|
|
|
586
491
|
});
|
|
587
492
|
return result.text.trim();
|
|
588
493
|
}
|
|
494
|
+
|
|
495
|
+
// src/lib/config.ts
|
|
496
|
+
import { homedir as homedir2 } from "os";
|
|
497
|
+
import { join as join2 } from "path";
|
|
498
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
499
|
+
import { execSync } from "child_process";
|
|
500
|
+
var DEFAULT_CONFIG = {
|
|
501
|
+
lang: "en"
|
|
502
|
+
};
|
|
503
|
+
function getGlobalConfigPath() {
|
|
504
|
+
const configDir = join2(homedir2(), ".config", "gut");
|
|
505
|
+
return join2(configDir, "config.json");
|
|
506
|
+
}
|
|
507
|
+
function getRepoRoot() {
|
|
508
|
+
try {
|
|
509
|
+
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
510
|
+
} catch {
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
function getLocalConfigPath() {
|
|
515
|
+
const repoRoot = getRepoRoot();
|
|
516
|
+
if (!repoRoot) return null;
|
|
517
|
+
return join2(repoRoot, ".gut", "config.json");
|
|
518
|
+
}
|
|
519
|
+
function ensureGlobalConfigDir() {
|
|
520
|
+
const configDir = join2(homedir2(), ".config", "gut");
|
|
521
|
+
if (!existsSync2(configDir)) {
|
|
522
|
+
mkdirSync(configDir, { recursive: true });
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function ensureLocalConfigDir() {
|
|
526
|
+
const repoRoot = getRepoRoot();
|
|
527
|
+
if (!repoRoot) return;
|
|
528
|
+
const gutDir = join2(repoRoot, ".gut");
|
|
529
|
+
if (!existsSync2(gutDir)) {
|
|
530
|
+
mkdirSync(gutDir, { recursive: true });
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function readConfigFile(path) {
|
|
534
|
+
if (!existsSync2(path)) return {};
|
|
535
|
+
try {
|
|
536
|
+
return JSON.parse(readFileSync2(path, "utf-8"));
|
|
537
|
+
} catch {
|
|
538
|
+
return {};
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function getGlobalConfig() {
|
|
542
|
+
const globalPath = getGlobalConfigPath();
|
|
543
|
+
return { ...DEFAULT_CONFIG, ...readConfigFile(globalPath) };
|
|
544
|
+
}
|
|
545
|
+
function getLocalConfig() {
|
|
546
|
+
const localPath = getLocalConfigPath();
|
|
547
|
+
if (!localPath) return {};
|
|
548
|
+
return readConfigFile(localPath);
|
|
549
|
+
}
|
|
550
|
+
function getConfig() {
|
|
551
|
+
const globalConfig = getGlobalConfig();
|
|
552
|
+
const localConfig = getLocalConfig();
|
|
553
|
+
return { ...globalConfig, ...localConfig };
|
|
554
|
+
}
|
|
555
|
+
function setGlobalConfig(key, value) {
|
|
556
|
+
ensureGlobalConfigDir();
|
|
557
|
+
const config = getGlobalConfig();
|
|
558
|
+
config[key] = value;
|
|
559
|
+
writeFileSync(getGlobalConfigPath(), JSON.stringify(config, null, 2));
|
|
560
|
+
}
|
|
561
|
+
function setLocalConfig(key, value) {
|
|
562
|
+
const localPath = getLocalConfigPath();
|
|
563
|
+
if (!localPath) {
|
|
564
|
+
throw new Error("Not in a git repository");
|
|
565
|
+
}
|
|
566
|
+
ensureLocalConfigDir();
|
|
567
|
+
const config = getLocalConfig();
|
|
568
|
+
config[key] = value;
|
|
569
|
+
writeFileSync(localPath, JSON.stringify(config, null, 2));
|
|
570
|
+
}
|
|
571
|
+
function getLanguage() {
|
|
572
|
+
return getConfig().lang;
|
|
573
|
+
}
|
|
574
|
+
function setLanguage(lang, local = false) {
|
|
575
|
+
if (local) {
|
|
576
|
+
setLocalConfig("lang", lang);
|
|
577
|
+
} else {
|
|
578
|
+
setGlobalConfig("lang", lang);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
function getLanguageInstruction(lang) {
|
|
582
|
+
switch (lang) {
|
|
583
|
+
case "ja":
|
|
584
|
+
return "\n\nIMPORTANT: Respond in Japanese (\u65E5\u672C\u8A9E\u3067\u56DE\u7B54\u3057\u3066\u304F\u3060\u3055\u3044).";
|
|
585
|
+
case "en":
|
|
586
|
+
default:
|
|
587
|
+
return "";
|
|
588
|
+
}
|
|
589
|
+
}
|
|
589
590
|
export {
|
|
590
591
|
deleteApiKey,
|
|
591
592
|
findTemplate,
|