@g-abhishek/gitx 0.1.2 → 0.1.5
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 +386 -3
- package/dist/ai/claudeAi.d.ts +35 -0
- package/dist/ai/claudeAi.d.ts.map +1 -0
- package/dist/ai/claudeAi.js +396 -0
- package/dist/ai/claudeAi.js.map +1 -0
- package/dist/ai/claudeCliAi.d.ts +27 -0
- package/dist/ai/claudeCliAi.d.ts.map +1 -0
- package/dist/ai/claudeCliAi.js +312 -0
- package/dist/ai/claudeCliAi.js.map +1 -0
- package/dist/ai/localClaudeAi.d.ts +2 -0
- package/dist/ai/localClaudeAi.d.ts.map +1 -0
- package/dist/ai/localClaudeAi.js +4 -0
- package/dist/ai/localClaudeAi.js.map +1 -0
- package/dist/ai/mockAi.d.ts +8 -1
- package/dist/ai/mockAi.d.ts.map +1 -1
- package/dist/ai/mockAi.js +57 -0
- package/dist/ai/mockAi.js.map +1 -1
- package/dist/ai/openAiAi.d.ts +33 -0
- package/dist/ai/openAiAi.d.ts.map +1 -0
- package/dist/ai/openAiAi.js +388 -0
- package/dist/ai/openAiAi.js.map +1 -0
- package/dist/ai/reviewHelpers.d.ts +66 -0
- package/dist/ai/reviewHelpers.d.ts.map +1 -0
- package/dist/ai/reviewHelpers.js +574 -0
- package/dist/ai/reviewHelpers.js.map +1 -0
- package/dist/ai/types.d.ts +247 -0
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/ai/types.js.map +1 -1
- package/dist/cli/commands/ask.d.ts +27 -0
- package/dist/cli/commands/ask.d.ts.map +1 -0
- package/dist/cli/commands/ask.js +230 -0
- package/dist/cli/commands/ask.js.map +1 -0
- package/dist/cli/commands/commit.d.ts +16 -0
- package/dist/cli/commands/commit.d.ts.map +1 -0
- package/dist/cli/commands/commit.js +163 -0
- package/dist/cli/commands/commit.js.map +1 -0
- package/dist/cli/commands/config.d.ts +4 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +666 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/implement.d.ts.map +1 -1
- package/dist/cli/commands/implement.js +149 -31
- package/dist/cli/commands/implement.js.map +1 -1
- package/dist/cli/commands/init.d.ts +4 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +7 -69
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/port.d.ts +32 -0
- package/dist/cli/commands/port.d.ts.map +1 -0
- package/dist/cli/commands/port.js +554 -0
- package/dist/cli/commands/port.js.map +1 -0
- package/dist/cli/commands/pr/close.d.ts +15 -0
- package/dist/cli/commands/pr/close.d.ts.map +1 -0
- package/dist/cli/commands/pr/close.js +71 -0
- package/dist/cli/commands/pr/close.js.map +1 -0
- package/dist/cli/commands/pr/create.d.ts +17 -0
- package/dist/cli/commands/pr/create.d.ts.map +1 -1
- package/dist/cli/commands/pr/create.js +208 -7
- package/dist/cli/commands/pr/create.js.map +1 -1
- package/dist/cli/commands/pr/fixComments.d.ts +5 -2
- package/dist/cli/commands/pr/fixComments.d.ts.map +1 -1
- package/dist/cli/commands/pr/fixComments.js +5 -13
- package/dist/cli/commands/pr/fixComments.js.map +1 -1
- package/dist/cli/commands/pr/index.d.ts.map +1 -1
- package/dist/cli/commands/pr/index.js +6 -2
- package/dist/cli/commands/pr/index.js.map +1 -1
- package/dist/cli/commands/pr/list.d.ts.map +1 -1
- package/dist/cli/commands/pr/list.js +24 -4
- package/dist/cli/commands/pr/list.js.map +1 -1
- package/dist/cli/commands/pr/merge.d.ts +23 -0
- package/dist/cli/commands/pr/merge.d.ts.map +1 -0
- package/dist/cli/commands/pr/merge.js +191 -0
- package/dist/cli/commands/pr/merge.js.map +1 -0
- package/dist/cli/commands/pr/resolve.d.ts +3 -0
- package/dist/cli/commands/pr/resolve.d.ts.map +1 -0
- package/dist/cli/commands/pr/resolve.js +92 -0
- package/dist/cli/commands/pr/resolve.js.map +1 -0
- package/dist/cli/commands/pr/review.d.ts.map +1 -1
- package/dist/cli/commands/pr/review.js +121 -6
- package/dist/cli/commands/pr/review.js.map +1 -1
- package/dist/cli/commands/push.d.ts +16 -0
- package/dist/cli/commands/push.d.ts.map +1 -0
- package/dist/cli/commands/push.js +166 -0
- package/dist/cli/commands/push.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +24 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +414 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +34 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/config/config.d.ts +20 -3
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +98 -45
- package/dist/config/config.js.map +1 -1
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +61 -6
- package/dist/config/schema.js.map +1 -1
- package/dist/core/context.d.ts +6 -0
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js.map +1 -1
- package/dist/core/gitx.d.ts +43 -0
- package/dist/core/gitx.d.ts.map +1 -1
- package/dist/core/gitx.js +187 -20
- package/dist/core/gitx.js.map +1 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/azure.d.ts +26 -0
- package/dist/providers/azure.d.ts.map +1 -0
- package/dist/providers/azure.js +256 -0
- package/dist/providers/azure.js.map +1 -0
- package/dist/providers/base.d.ts +104 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +5 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/factory.d.ts +8 -0
- package/dist/providers/factory.d.ts.map +1 -0
- package/dist/providers/factory.js +25 -0
- package/dist/providers/factory.js.map +1 -0
- package/dist/providers/github.d.ts +19 -0
- package/dist/providers/github.d.ts.map +1 -0
- package/dist/providers/github.js +291 -0
- package/dist/providers/github.js.map +1 -0
- package/dist/providers/gitlab.d.ts +19 -0
- package/dist/providers/gitlab.d.ts.map +1 -0
- package/dist/providers/gitlab.js +186 -0
- package/dist/providers/gitlab.js.map +1 -0
- package/dist/types/config.d.ts +50 -7
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/dist/utils/azureAuth.d.ts +51 -0
- package/dist/utils/azureAuth.d.ts.map +1 -0
- package/dist/utils/azureAuth.js +172 -0
- package/dist/utils/azureAuth.js.map +1 -0
- package/dist/utils/git.d.ts +19 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +45 -8
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/gitOps.d.ts +125 -0
- package/dist/utils/gitOps.d.ts.map +1 -0
- package/dist/utils/gitOps.js +396 -0
- package/dist/utils/gitOps.js.map +1 -0
- package/dist/utils/lockFile.d.ts +13 -0
- package/dist/utils/lockFile.d.ts.map +1 -0
- package/dist/utils/lockFile.js +54 -0
- package/dist/utils/lockFile.js.map +1 -0
- package/dist/utils/retry.d.ts +10 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +31 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/workflows/implement.d.ts +41 -0
- package/dist/workflows/implement.d.ts.map +1 -0
- package/dist/workflows/implement.js +219 -0
- package/dist/workflows/implement.js.map +1 -0
- package/dist/workflows/pr.d.ts +41 -0
- package/dist/workflows/pr.d.ts.map +1 -0
- package/dist/workflows/pr.js +291 -0
- package/dist/workflows/pr.js.map +1 -0
- package/dist/workflows/prAddress.d.ts +55 -0
- package/dist/workflows/prAddress.d.ts.map +1 -0
- package/dist/workflows/prAddress.js +349 -0
- package/dist/workflows/prAddress.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gitx ask "<question>"` — Smart support assistant for your repo.
|
|
3
|
+
*
|
|
4
|
+
* Acts as a support agent that can answer three types of questions:
|
|
5
|
+
*
|
|
6
|
+
* 1. SETUP / DIAGNOSTIC — "is my AI provider set up?", "why isn't gitx working?",
|
|
7
|
+
* "do I have a GitHub token configured?"
|
|
8
|
+
* → Reads your live gitx config and reports real status (no fabrication).
|
|
9
|
+
*
|
|
10
|
+
* 2. REPO STATE — "what did I last commit?", "do I have unstaged changes?",
|
|
11
|
+
* "show me all open PRs"
|
|
12
|
+
* → Reads live git state (branch, commits, status, stashes).
|
|
13
|
+
*
|
|
14
|
+
* 3. HOW-TO — "how do I sync with main?", "how do I undo my last commit?",
|
|
15
|
+
* "what command creates a PR?"
|
|
16
|
+
* → Uses built-in gitx command reference embedded in every prompt.
|
|
17
|
+
*
|
|
18
|
+
* Examples:
|
|
19
|
+
* gitx ask "is my AI provider set up?"
|
|
20
|
+
* gitx ask "what did I last commit?"
|
|
21
|
+
* gitx ask "how do I sync my branch with main?"
|
|
22
|
+
* gitx ask "do I have any open PRs?" --pr
|
|
23
|
+
* gitx ask "why is gitx not working?"
|
|
24
|
+
*/
|
|
25
|
+
import ora from "ora";
|
|
26
|
+
import chalk from "chalk";
|
|
27
|
+
import { Gitx } from "../../core/gitx.js";
|
|
28
|
+
import { logger } from "../../logger/logger.js";
|
|
29
|
+
import { getCurrentBranch, getGitStatus, getRecentCommits, getStashList, } from "../../utils/gitOps.js";
|
|
30
|
+
import { isInsideGitRepo } from "../../utils/git.js";
|
|
31
|
+
import { createProvider } from "../../providers/factory.js";
|
|
32
|
+
// ─── Config diagnostic helpers ────────────────────────────────────────────────
|
|
33
|
+
/**
|
|
34
|
+
* Inspect the loaded config + environment variables and return a plain-English
|
|
35
|
+
* snapshot of which AI provider is active — WITHOUT exposing actual key values.
|
|
36
|
+
*/
|
|
37
|
+
function buildAiSetupStatus(config) {
|
|
38
|
+
// 1. ANTHROPIC_API_KEY env var (highest priority)
|
|
39
|
+
if (process.env["ANTHROPIC_API_KEY"]) {
|
|
40
|
+
const model = process.env["GITX_AI_MODEL"] ?? config.aiProviders?.claude?.model ?? "claude-3-5-haiku-20241022";
|
|
41
|
+
return {
|
|
42
|
+
provider: "claude (Anthropic API)",
|
|
43
|
+
model,
|
|
44
|
+
keySource: "ANTHROPIC_API_KEY env var",
|
|
45
|
+
isConfigured: true,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// 2. OPENAI_API_KEY env var
|
|
49
|
+
if (process.env["OPENAI_API_KEY"]) {
|
|
50
|
+
const model = process.env["GITX_AI_MODEL"] ?? config.aiProviders?.openai?.model ?? "gpt-4o";
|
|
51
|
+
return {
|
|
52
|
+
provider: "openai",
|
|
53
|
+
model,
|
|
54
|
+
keySource: "OPENAI_API_KEY env var",
|
|
55
|
+
isConfigured: true,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// 3. defaultAiProvider in config
|
|
59
|
+
const defaultProv = config.defaultAiProvider;
|
|
60
|
+
if (defaultProv) {
|
|
61
|
+
const entry = config.aiProviders?.[defaultProv];
|
|
62
|
+
if (defaultProv === "claude-cli") {
|
|
63
|
+
return {
|
|
64
|
+
provider: "claude-cli (local)",
|
|
65
|
+
keySource: "local CLI",
|
|
66
|
+
isConfigured: true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (entry?.apiKey) {
|
|
70
|
+
const model = process.env["GITX_AI_MODEL"] ?? entry.model ?? (defaultProv === "claude" ? "claude-3-5-haiku-20241022" : "gpt-4o");
|
|
71
|
+
return {
|
|
72
|
+
provider: defaultProv === "claude" ? "claude (Anthropic API)" : defaultProv,
|
|
73
|
+
model,
|
|
74
|
+
keySource: "config file",
|
|
75
|
+
isConfigured: true,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// defaultAiProvider set but key is missing
|
|
79
|
+
return {
|
|
80
|
+
provider: `${defaultProv} (key missing)`,
|
|
81
|
+
keySource: "config file (key missing — needs to be set)",
|
|
82
|
+
isConfigured: false,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// 4. Scan all aiProviders entries
|
|
86
|
+
const entries = Object.entries(config.aiProviders ?? {});
|
|
87
|
+
for (const [kind, entry] of entries) {
|
|
88
|
+
if (kind === "claude-cli") {
|
|
89
|
+
return {
|
|
90
|
+
provider: "claude-cli (local)",
|
|
91
|
+
keySource: "config file",
|
|
92
|
+
isConfigured: true,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (entry?.apiKey) {
|
|
96
|
+
const model = process.env["GITX_AI_MODEL"] ?? entry.model;
|
|
97
|
+
return {
|
|
98
|
+
provider: kind === "claude" ? "claude (Anthropic API)" : kind,
|
|
99
|
+
model,
|
|
100
|
+
keySource: "config file",
|
|
101
|
+
isConfigured: true,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// 5. No provider found
|
|
106
|
+
return {
|
|
107
|
+
provider: "none (not configured)",
|
|
108
|
+
keySource: "none",
|
|
109
|
+
isConfigured: false,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Return the list of configured git hosting providers with a simple
|
|
114
|
+
* "has auth / missing auth" status. Never exposes actual token values.
|
|
115
|
+
* Recognises both PAT tokens and GCM (OAuth) as "configured".
|
|
116
|
+
*/
|
|
117
|
+
function buildGitProviderStatus(config) {
|
|
118
|
+
const providers = config.providers ?? {};
|
|
119
|
+
const result = [];
|
|
120
|
+
for (const [name, entry] of Object.entries(providers)) {
|
|
121
|
+
const hasToken = entry?.authMethod === "gcm" ||
|
|
122
|
+
(!!entry?.token && entry.token.length > 0);
|
|
123
|
+
result.push({ name, hasToken });
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
// ─── Command ──────────────────────────────────────────────────────────────────
|
|
128
|
+
export function registerAskCommand(program) {
|
|
129
|
+
program
|
|
130
|
+
.command("ask")
|
|
131
|
+
.description("💬 Ask a question about your repo or gitx setup using AI")
|
|
132
|
+
.argument("<question>", "Your question (wrap in quotes for multi-word questions)")
|
|
133
|
+
.option("--pr", "Include open pull requests in the context (requires a provider token)")
|
|
134
|
+
.option("--no-color", "Disable colored output")
|
|
135
|
+
.action(async (question, options) => {
|
|
136
|
+
const cwd = process.cwd();
|
|
137
|
+
const gitx = await Gitx.fromCwd(cwd);
|
|
138
|
+
// ── 1. Build gitx setup diagnostics ───────────────────────────────────
|
|
139
|
+
const aiSetup = buildAiSetupStatus(gitx.config);
|
|
140
|
+
const gitProviders = buildGitProviderStatus(gitx.config);
|
|
141
|
+
const defaultBranch = gitx.config.defaultBranch;
|
|
142
|
+
// ── 2. Gather live git context ─────────────────────────────────────────
|
|
143
|
+
const contextSpinner = ora("🔍 Gathering context…").start();
|
|
144
|
+
let inRepo = false;
|
|
145
|
+
let currentBranch = "unknown";
|
|
146
|
+
let gitStatus = "";
|
|
147
|
+
let recentCommits = [];
|
|
148
|
+
let stashes = [];
|
|
149
|
+
try {
|
|
150
|
+
inRepo = await isInsideGitRepo(cwd);
|
|
151
|
+
if (inRepo) {
|
|
152
|
+
[currentBranch, gitStatus, recentCommits, stashes] = await Promise.all([
|
|
153
|
+
getCurrentBranch(cwd),
|
|
154
|
+
getGitStatus(cwd),
|
|
155
|
+
getRecentCommits(cwd, 10),
|
|
156
|
+
getStashList(cwd),
|
|
157
|
+
]);
|
|
158
|
+
}
|
|
159
|
+
contextSpinner.succeed("Context gathered.");
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
contextSpinner.warn("Could not read full git context — proceeding with partial info.");
|
|
163
|
+
}
|
|
164
|
+
const context = {
|
|
165
|
+
isInsideGitRepo: inRepo,
|
|
166
|
+
currentBranch,
|
|
167
|
+
recentCommits,
|
|
168
|
+
gitStatus,
|
|
169
|
+
stashes: stashes.length > 0 ? stashes : undefined,
|
|
170
|
+
aiSetup,
|
|
171
|
+
gitProviders,
|
|
172
|
+
defaultBranch,
|
|
173
|
+
};
|
|
174
|
+
// ── 3. Optionally fetch open PRs ───────────────────────────────────────
|
|
175
|
+
const mentionsPR = /\bpr\b|pull request/i.test(question);
|
|
176
|
+
if (options.pr || mentionsPR) {
|
|
177
|
+
const prSpinner = ora("📋 Fetching open PRs…").start();
|
|
178
|
+
try {
|
|
179
|
+
const repoCtx = await gitx.getRepoContext();
|
|
180
|
+
const provider = createProvider(repoCtx);
|
|
181
|
+
const allPRs = await provider.listPRs(repoCtx.repoSlug);
|
|
182
|
+
const prs = allPRs.filter((pr) => pr.state === "open");
|
|
183
|
+
context.openPRs = prs.map((pr) => ({
|
|
184
|
+
number: pr.number,
|
|
185
|
+
title: pr.title,
|
|
186
|
+
state: pr.state,
|
|
187
|
+
branch: pr.head,
|
|
188
|
+
}));
|
|
189
|
+
prSpinner.succeed(`Found ${prs.length} open PR${prs.length !== 1 ? "s" : ""}.`);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
prSpinner.warn("Could not fetch PRs — provider token may not be configured.");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// ── 4. Ask AI ──────────────────────────────────────────────────────────
|
|
196
|
+
const aiSpinner = ora("🤖 Thinking…").start();
|
|
197
|
+
let answer = "";
|
|
198
|
+
let suggestedCommands = [];
|
|
199
|
+
try {
|
|
200
|
+
const result = await gitx.ai.ask(question, context);
|
|
201
|
+
answer = result.answer;
|
|
202
|
+
suggestedCommands = result.suggestedCommands ?? [];
|
|
203
|
+
aiSpinner.succeed("Answer ready.");
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
aiSpinner.fail("AI query failed.");
|
|
207
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
208
|
+
process.exitCode = 1;
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// ── 5. Display answer ──────────────────────────────────────────────────
|
|
212
|
+
console.log("");
|
|
213
|
+
console.log(chalk.bold.cyan("╭─ gitx ask ─────────────────────────────────────────"));
|
|
214
|
+
console.log(chalk.gray("│ Q: ") + chalk.white(question));
|
|
215
|
+
console.log(chalk.bold.cyan("│"));
|
|
216
|
+
answer.split("\n").forEach((line) => {
|
|
217
|
+
console.log(chalk.bold.cyan("│") + " " + line);
|
|
218
|
+
});
|
|
219
|
+
if (suggestedCommands.length > 0) {
|
|
220
|
+
console.log(chalk.bold.cyan("│"));
|
|
221
|
+
console.log(chalk.bold.cyan("│") + " " + chalk.yellow.bold("💡 Suggested commands:"));
|
|
222
|
+
suggestedCommands.forEach((cmd) => {
|
|
223
|
+
console.log(chalk.bold.cyan("│") + " " + chalk.green(`$ ${cmd}`));
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
console.log(chalk.bold.cyan("╰────────────────────────────────────────────────────"));
|
|
227
|
+
console.log("");
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=ask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.js","sourceRoot":"","sources":["../../../src/cli/commands/ask.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `gitx ask \"<question>\"` — Smart support assistant for your repo.\n *\n * Acts as a support agent that can answer three types of questions:\n *\n * 1. SETUP / DIAGNOSTIC — \"is my AI provider set up?\", \"why isn't gitx working?\",\n * \"do I have a GitHub token configured?\"\n * → Reads your live gitx config and reports real status (no fabrication).\n *\n * 2. REPO STATE — \"what did I last commit?\", \"do I have unstaged changes?\",\n * \"show me all open PRs\"\n * → Reads live git state (branch, commits, status, stashes).\n *\n * 3. HOW-TO — \"how do I sync with main?\", \"how do I undo my last commit?\",\n * \"what command creates a PR?\"\n * → Uses built-in gitx command reference embedded in every prompt.\n *\n * Examples:\n * gitx ask \"is my AI provider set up?\"\n * gitx ask \"what did I last commit?\"\n * gitx ask \"how do I sync my branch with main?\"\n * gitx ask \"do I have any open PRs?\" --pr\n * gitx ask \"why is gitx not working?\"\n */\n\nimport type { Command } from \"commander\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { Gitx } from \"../../core/gitx.js\";\nimport { logger } from \"../../logger/logger.js\";\nimport {\n getCurrentBranch,\n getGitStatus,\n getRecentCommits,\n getStashList,\n} from \"../../utils/gitOps.js\";\nimport { isInsideGitRepo } from \"../../utils/git.js\";\nimport { createProvider } from \"../../providers/factory.js\";\nimport type { AiAskContext, AiSetupStatus, GitProviderStatus } from \"../../ai/types.js\";\nimport type { GitxConfig } from \"../../types/config.js\";\n\n// ─── Config diagnostic helpers ────────────────────────────────────────────────\n\n/**\n * Inspect the loaded config + environment variables and return a plain-English\n * snapshot of which AI provider is active — WITHOUT exposing actual key values.\n */\nfunction buildAiSetupStatus(config: GitxConfig): AiSetupStatus {\n // 1. ANTHROPIC_API_KEY env var (highest priority)\n if (process.env[\"ANTHROPIC_API_KEY\"]) {\n const model = process.env[\"GITX_AI_MODEL\"] ?? config.aiProviders?.claude?.model ?? \"claude-3-5-haiku-20241022\";\n return {\n provider: \"claude (Anthropic API)\",\n model,\n keySource: \"ANTHROPIC_API_KEY env var\",\n isConfigured: true,\n };\n }\n\n // 2. OPENAI_API_KEY env var\n if (process.env[\"OPENAI_API_KEY\"]) {\n const model = process.env[\"GITX_AI_MODEL\"] ?? config.aiProviders?.openai?.model ?? \"gpt-4o\";\n return {\n provider: \"openai\",\n model,\n keySource: \"OPENAI_API_KEY env var\",\n isConfigured: true,\n };\n }\n\n // 3. defaultAiProvider in config\n const defaultProv = config.defaultAiProvider;\n if (defaultProv) {\n const entry = config.aiProviders?.[defaultProv];\n if (defaultProv === \"claude-cli\") {\n return {\n provider: \"claude-cli (local)\",\n keySource: \"local CLI\",\n isConfigured: true,\n };\n }\n if (entry?.apiKey) {\n const model = process.env[\"GITX_AI_MODEL\"] ?? entry.model ?? (defaultProv === \"claude\" ? \"claude-3-5-haiku-20241022\" : \"gpt-4o\");\n return {\n provider: defaultProv === \"claude\" ? \"claude (Anthropic API)\" : defaultProv,\n model,\n keySource: \"config file\",\n isConfigured: true,\n };\n }\n // defaultAiProvider set but key is missing\n return {\n provider: `${defaultProv} (key missing)`,\n keySource: \"config file (key missing — needs to be set)\",\n isConfigured: false,\n };\n }\n\n // 4. Scan all aiProviders entries\n const entries = Object.entries(config.aiProviders ?? {}) as Array<[string, { apiKey?: string; model?: string }]>;\n for (const [kind, entry] of entries) {\n if (kind === \"claude-cli\") {\n return {\n provider: \"claude-cli (local)\",\n keySource: \"config file\",\n isConfigured: true,\n };\n }\n if (entry?.apiKey) {\n const model = process.env[\"GITX_AI_MODEL\"] ?? entry.model;\n return {\n provider: kind === \"claude\" ? \"claude (Anthropic API)\" : kind,\n model,\n keySource: \"config file\",\n isConfigured: true,\n };\n }\n }\n\n // 5. No provider found\n return {\n provider: \"none (not configured)\",\n keySource: \"none\",\n isConfigured: false,\n };\n}\n\n/**\n * Return the list of configured git hosting providers with a simple\n * \"has auth / missing auth\" status. Never exposes actual token values.\n * Recognises both PAT tokens and GCM (OAuth) as \"configured\".\n */\nfunction buildGitProviderStatus(config: GitxConfig): GitProviderStatus[] {\n const providers = config.providers ?? {};\n const result: GitProviderStatus[] = [];\n for (const [name, entry] of Object.entries(providers)) {\n const hasToken =\n entry?.authMethod === \"gcm\" ||\n (!!entry?.token && entry.token.length > 0);\n result.push({ name, hasToken });\n }\n return result;\n}\n\n// ─── Command ──────────────────────────────────────────────────────────────────\n\nexport function registerAskCommand(program: Command): void {\n program\n .command(\"ask\")\n .description(\"💬 Ask a question about your repo or gitx setup using AI\")\n .argument(\"<question>\", \"Your question (wrap in quotes for multi-word questions)\")\n .option(\"--pr\", \"Include open pull requests in the context (requires a provider token)\")\n .option(\"--no-color\", \"Disable colored output\")\n .action(async (question: string, options: { pr?: boolean }) => {\n const cwd = process.cwd();\n const gitx = await Gitx.fromCwd(cwd);\n\n // ── 1. Build gitx setup diagnostics ───────────────────────────────────\n const aiSetup = buildAiSetupStatus(gitx.config);\n const gitProviders = buildGitProviderStatus(gitx.config);\n const defaultBranch = gitx.config.defaultBranch;\n\n // ── 2. Gather live git context ─────────────────────────────────────────\n const contextSpinner = ora(\"🔍 Gathering context…\").start();\n\n let inRepo = false;\n let currentBranch = \"unknown\";\n let gitStatus = \"\";\n let recentCommits: string[] = [];\n let stashes: string[] = [];\n\n try {\n inRepo = await isInsideGitRepo(cwd);\n\n if (inRepo) {\n [currentBranch, gitStatus, recentCommits, stashes] = await Promise.all([\n getCurrentBranch(cwd),\n getGitStatus(cwd),\n getRecentCommits(cwd, 10),\n getStashList(cwd),\n ]);\n }\n contextSpinner.succeed(\"Context gathered.\");\n } catch {\n contextSpinner.warn(\"Could not read full git context — proceeding with partial info.\");\n }\n\n const context: AiAskContext = {\n isInsideGitRepo: inRepo,\n currentBranch,\n recentCommits,\n gitStatus,\n stashes: stashes.length > 0 ? stashes : undefined,\n aiSetup,\n gitProviders,\n defaultBranch,\n };\n\n // ── 3. Optionally fetch open PRs ───────────────────────────────────────\n const mentionsPR = /\\bpr\\b|pull request/i.test(question);\n if (options.pr || mentionsPR) {\n const prSpinner = ora(\"📋 Fetching open PRs…\").start();\n try {\n const repoCtx = await gitx.getRepoContext();\n const provider = createProvider(repoCtx);\n const allPRs = await provider.listPRs(repoCtx.repoSlug);\n const prs = allPRs.filter((pr) => pr.state === \"open\");\n context.openPRs = prs.map((pr) => ({\n number: pr.number,\n title: pr.title,\n state: pr.state,\n branch: pr.head,\n }));\n prSpinner.succeed(`Found ${prs.length} open PR${prs.length !== 1 ? \"s\" : \"\"}.`);\n } catch {\n prSpinner.warn(\"Could not fetch PRs — provider token may not be configured.\");\n }\n }\n\n // ── 4. Ask AI ──────────────────────────────────────────────────────────\n const aiSpinner = ora(\"🤖 Thinking…\").start();\n let answer = \"\";\n let suggestedCommands: string[] = [];\n\n try {\n const result = await gitx.ai.ask(question, context);\n answer = result.answer;\n suggestedCommands = result.suggestedCommands ?? [];\n aiSpinner.succeed(\"Answer ready.\");\n } catch (err) {\n aiSpinner.fail(\"AI query failed.\");\n logger.error(err instanceof Error ? err.message : String(err));\n process.exitCode = 1;\n return;\n }\n\n // ── 5. Display answer ──────────────────────────────────────────────────\n console.log(\"\");\n console.log(chalk.bold.cyan(\"╭─ gitx ask ─────────────────────────────────────────\"));\n console.log(chalk.gray(\"│ Q: \") + chalk.white(question));\n console.log(chalk.bold.cyan(\"│\"));\n answer.split(\"\\n\").forEach((line) => {\n console.log(chalk.bold.cyan(\"│\") + \" \" + line);\n });\n\n if (suggestedCommands.length > 0) {\n console.log(chalk.bold.cyan(\"│\"));\n console.log(chalk.bold.cyan(\"│\") + \" \" + chalk.yellow.bold(\"💡 Suggested commands:\"));\n suggestedCommands.forEach((cmd) => {\n console.log(chalk.bold.cyan(\"│\") + \" \" + chalk.green(`$ ${cmd}`));\n });\n }\n\n console.log(chalk.bold.cyan(\"╰────────────────────────────────────────────────────\"));\n console.log(\"\");\n });\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gitx commit
|
|
3
|
+
*
|
|
4
|
+
* AI-powered commit: detects what changed, generates a meaningful
|
|
5
|
+
* conventional-commit message, commits, and optionally pushes.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* gitx commit # AI generates message, prompts to confirm
|
|
9
|
+
* gitx commit -m "fix: typo" # use custom message, skip AI
|
|
10
|
+
* gitx commit --push # commit + push in one step
|
|
11
|
+
* gitx commit --no-push # commit only (default)
|
|
12
|
+
* gitx commit --dry-run # preview message, do not commit
|
|
13
|
+
*/
|
|
14
|
+
import type { Command } from "commander";
|
|
15
|
+
export declare function registerCommitCommand(program: Command): void;
|
|
16
|
+
//# sourceMappingURL=commit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/commit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmBzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+J5D"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gitx commit
|
|
3
|
+
*
|
|
4
|
+
* AI-powered commit: detects what changed, generates a meaningful
|
|
5
|
+
* conventional-commit message, commits, and optionally pushes.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* gitx commit # AI generates message, prompts to confirm
|
|
9
|
+
* gitx commit -m "fix: typo" # use custom message, skip AI
|
|
10
|
+
* gitx commit --push # commit + push in one step
|
|
11
|
+
* gitx commit --no-push # commit only (default)
|
|
12
|
+
* gitx commit --dry-run # preview message, do not commit
|
|
13
|
+
*/
|
|
14
|
+
import inquirer from "inquirer";
|
|
15
|
+
import ora from "ora";
|
|
16
|
+
import { logger } from "../../logger/logger.js";
|
|
17
|
+
import { Gitx } from "../../core/gitx.js";
|
|
18
|
+
import { stageAll, hasStagedChanges, isWorkingTreeDirty, getWorkingDiff, getWorkingDiffStat, commitChanges, pushBranch, getCurrentBranch, } from "../../utils/gitOps.js";
|
|
19
|
+
import { isInsideGitRepo } from "../../utils/git.js";
|
|
20
|
+
import { GitxError } from "../../utils/errors.js";
|
|
21
|
+
import { withLockRetry } from "../../utils/lockFile.js";
|
|
22
|
+
export function registerCommitCommand(program) {
|
|
23
|
+
program
|
|
24
|
+
.command("commit")
|
|
25
|
+
.description("🤖 Stage, AI-generate a commit message, commit, and optionally push")
|
|
26
|
+
.option("-m, --message <msg>", "Use a custom commit message (skips AI generation)")
|
|
27
|
+
.option("--push", "Push to remote after committing")
|
|
28
|
+
.option("--no-push", "Commit only, do not push (default)")
|
|
29
|
+
.option("--dry-run", "Preview the commit message without committing")
|
|
30
|
+
.option("--all", "Stage all changes before committing (default: true)", true)
|
|
31
|
+
.action(async (opts) => {
|
|
32
|
+
const cwd = process.cwd();
|
|
33
|
+
// ── Guards ──────────────────────────────────────────────────────────
|
|
34
|
+
if (!(await isInsideGitRepo(cwd))) {
|
|
35
|
+
throw new GitxError("Not inside a git repository. cd into your project folder first.", { exitCode: 2 });
|
|
36
|
+
}
|
|
37
|
+
const dirty = await isWorkingTreeDirty(cwd);
|
|
38
|
+
if (!dirty) {
|
|
39
|
+
logger.info("✨ Nothing to commit — working tree is clean.");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// ── Stage changes ────────────────────────────────────────────────────
|
|
43
|
+
if (opts.all) {
|
|
44
|
+
const stageSpinner = ora("Staging all changes…").start();
|
|
45
|
+
await withLockRetry(() => stageAll(cwd), cwd);
|
|
46
|
+
stageSpinner.succeed("All changes staged.");
|
|
47
|
+
}
|
|
48
|
+
const staged = await hasStagedChanges(cwd);
|
|
49
|
+
if (!staged) {
|
|
50
|
+
logger.warn("No staged changes found. Use `git add` to stage files, or run without --no-all.");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// ── Get diff for AI ──────────────────────────────────────────────────
|
|
54
|
+
// stat = compact file list (always complete, never truncated)
|
|
55
|
+
// diff = full patch (may be large — we truncate later in the AI call)
|
|
56
|
+
const [stat, diff] = await Promise.all([
|
|
57
|
+
getWorkingDiffStat(cwd),
|
|
58
|
+
getWorkingDiff(cwd),
|
|
59
|
+
]);
|
|
60
|
+
// ── Generate or use custom message ───────────────────────────────────
|
|
61
|
+
let commitMsg;
|
|
62
|
+
if (opts.message) {
|
|
63
|
+
commitMsg = opts.message;
|
|
64
|
+
logger.info(`📝 Using provided message: ${commitMsg}`);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const gitx = await Gitx.fromCwd(cwd);
|
|
68
|
+
if (!await Gitx.isAiAvailable(gitx.config)) {
|
|
69
|
+
logger.warn("⚠️ No AI provider configured. Run `gitx config` to set one up.");
|
|
70
|
+
logger.warn(" Falling back to manual commit message entry.\n");
|
|
71
|
+
const { manualMsg } = await inquirer.prompt([
|
|
72
|
+
{
|
|
73
|
+
type: "input",
|
|
74
|
+
name: "manualMsg",
|
|
75
|
+
message: "Commit message:",
|
|
76
|
+
validate: (v) => v.trim().length > 0 || "Message cannot be empty",
|
|
77
|
+
},
|
|
78
|
+
]);
|
|
79
|
+
commitMsg = manualMsg.trim();
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const aiSpinner = ora("🤖 Generating commit message…").start();
|
|
83
|
+
try {
|
|
84
|
+
// Build AI input: complete file summary first, then detailed patch.
|
|
85
|
+
// The stat ensures the AI sees every changed file even when the
|
|
86
|
+
// full diff is truncated by the 12 000 char safety limit.
|
|
87
|
+
const aiInput = stat
|
|
88
|
+
? `=== Changed files (complete list) ===\n${stat}\n\n=== Detailed diff ===\n${diff}`
|
|
89
|
+
: diff;
|
|
90
|
+
const result = await gitx.ai.generateCommitMessage(aiInput);
|
|
91
|
+
// Use the AI-generated conventional commit subject + optional body as-is
|
|
92
|
+
commitMsg = result.body
|
|
93
|
+
? `${result.subject}\n\n${result.body}`
|
|
94
|
+
: result.subject;
|
|
95
|
+
aiSpinner.succeed("Commit message generated.");
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
aiSpinner.fail("AI generation failed.");
|
|
99
|
+
logger.warn(` ${err instanceof Error ? err.message : String(err)}`);
|
|
100
|
+
logger.warn(" Falling back to manual entry.\n");
|
|
101
|
+
const { manualMsg } = await inquirer.prompt([
|
|
102
|
+
{
|
|
103
|
+
type: "input",
|
|
104
|
+
name: "manualMsg",
|
|
105
|
+
message: "Commit message:",
|
|
106
|
+
validate: (v) => v.trim().length > 0 || "Message cannot be empty",
|
|
107
|
+
},
|
|
108
|
+
]);
|
|
109
|
+
commitMsg = manualMsg.trim();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// ── Show preview and confirm ─────────────────────────────────────────
|
|
114
|
+
logger.info("\n📋 Commit message preview:\n");
|
|
115
|
+
logger.info("─".repeat(60));
|
|
116
|
+
logger.info(commitMsg);
|
|
117
|
+
logger.info("─".repeat(60));
|
|
118
|
+
if (opts.dryRun) {
|
|
119
|
+
logger.info("\n🔍 Dry run — nothing committed.");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const { confirmed } = await inquirer.prompt([
|
|
123
|
+
{
|
|
124
|
+
type: "confirm",
|
|
125
|
+
name: "confirmed",
|
|
126
|
+
message: "Commit with this message?",
|
|
127
|
+
default: true,
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
if (!confirmed) {
|
|
131
|
+
// Let user edit the message manually
|
|
132
|
+
const { editedMsg } = await inquirer.prompt([
|
|
133
|
+
{
|
|
134
|
+
type: "editor",
|
|
135
|
+
name: "editedMsg",
|
|
136
|
+
message: "Edit commit message:",
|
|
137
|
+
default: commitMsg,
|
|
138
|
+
},
|
|
139
|
+
]);
|
|
140
|
+
commitMsg = editedMsg.trim();
|
|
141
|
+
if (!commitMsg) {
|
|
142
|
+
logger.warn("Empty message — commit aborted.");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// ── Commit ───────────────────────────────────────────────────────────
|
|
147
|
+
const commitSpinner = ora("Committing…").start();
|
|
148
|
+
await withLockRetry(() => commitChanges(commitMsg, cwd), cwd);
|
|
149
|
+
commitSpinner.succeed("Committed ✓");
|
|
150
|
+
// ── Push ─────────────────────────────────────────────────────────────
|
|
151
|
+
if (opts.push) {
|
|
152
|
+
const branch = await getCurrentBranch(cwd);
|
|
153
|
+
const pushSpinner = ora(`Pushing ${branch} to origin…`).start();
|
|
154
|
+
await pushBranch(branch, cwd);
|
|
155
|
+
pushSpinner.succeed(`Pushed to origin/${branch} ✓`);
|
|
156
|
+
logger.success(`\n✅ Done! Changes committed and pushed.`);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
logger.success(`\n✅ Done! Run \`gitx commit --push\` or \`git push\` to push.`);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=commit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit.js","sourceRoot":"","sources":["../../../src/cli/commands/commit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gitx commit\n *\n * AI-powered commit: detects what changed, generates a meaningful\n * conventional-commit message, commits, and optionally pushes.\n *\n * Usage:\n * gitx commit # AI generates message, prompts to confirm\n * gitx commit -m \"fix: typo\" # use custom message, skip AI\n * gitx commit --push # commit + push in one step\n * gitx commit --no-push # commit only (default)\n * gitx commit --dry-run # preview message, do not commit\n */\n\nimport type { Command } from \"commander\";\nimport inquirer from \"inquirer\";\nimport ora from \"ora\";\nimport { logger } from \"../../logger/logger.js\";\nimport { Gitx } from \"../../core/gitx.js\";\nimport {\n stageAll,\n hasStagedChanges,\n isWorkingTreeDirty,\n getWorkingDiff,\n getWorkingDiffStat,\n commitChanges,\n pushBranch,\n getCurrentBranch,\n} from \"../../utils/gitOps.js\";\nimport { isInsideGitRepo } from \"../../utils/git.js\";\nimport { GitxError } from \"../../utils/errors.js\";\nimport { withLockRetry } from \"../../utils/lockFile.js\";\n\nexport function registerCommitCommand(program: Command): void {\n program\n .command(\"commit\")\n .description(\"🤖 Stage, AI-generate a commit message, commit, and optionally push\")\n .option(\"-m, --message <msg>\", \"Use a custom commit message (skips AI generation)\")\n .option(\"--push\", \"Push to remote after committing\")\n .option(\"--no-push\", \"Commit only, do not push (default)\")\n .option(\"--dry-run\", \"Preview the commit message without committing\")\n .option(\"--all\", \"Stage all changes before committing (default: true)\", true)\n .action(async (opts: {\n message?: string;\n push: boolean;\n dryRun?: boolean;\n all: boolean;\n }) => {\n const cwd = process.cwd();\n\n // ── Guards ──────────────────────────────────────────────────────────\n if (!(await isInsideGitRepo(cwd))) {\n throw new GitxError(\"Not inside a git repository. cd into your project folder first.\", { exitCode: 2 });\n }\n\n const dirty = await isWorkingTreeDirty(cwd);\n if (!dirty) {\n logger.info(\"✨ Nothing to commit — working tree is clean.\");\n return;\n }\n\n // ── Stage changes ────────────────────────────────────────────────────\n if (opts.all) {\n const stageSpinner = ora(\"Staging all changes…\").start();\n await withLockRetry(() => stageAll(cwd), cwd);\n stageSpinner.succeed(\"All changes staged.\");\n }\n\n const staged = await hasStagedChanges(cwd);\n if (!staged) {\n logger.warn(\"No staged changes found. Use `git add` to stage files, or run without --no-all.\");\n return;\n }\n\n // ── Get diff for AI ──────────────────────────────────────────────────\n // stat = compact file list (always complete, never truncated)\n // diff = full patch (may be large — we truncate later in the AI call)\n const [stat, diff] = await Promise.all([\n getWorkingDiffStat(cwd),\n getWorkingDiff(cwd),\n ]);\n\n // ── Generate or use custom message ───────────────────────────────────\n let commitMsg: string;\n\n if (opts.message) {\n commitMsg = opts.message;\n logger.info(`📝 Using provided message: ${commitMsg}`);\n } else {\n const gitx = await Gitx.fromCwd(cwd);\n\n if (!await Gitx.isAiAvailable(gitx.config)) {\n logger.warn(\"⚠️ No AI provider configured. Run `gitx config` to set one up.\");\n logger.warn(\" Falling back to manual commit message entry.\\n\");\n\n const { manualMsg } = await inquirer.prompt<{ manualMsg: string }>([\n {\n type: \"input\",\n name: \"manualMsg\",\n message: \"Commit message:\",\n validate: (v: string) => v.trim().length > 0 || \"Message cannot be empty\",\n },\n ]);\n commitMsg = manualMsg.trim();\n } else {\n const aiSpinner = ora(\"🤖 Generating commit message…\").start();\n try {\n // Build AI input: complete file summary first, then detailed patch.\n // The stat ensures the AI sees every changed file even when the\n // full diff is truncated by the 12 000 char safety limit.\n const aiInput = stat\n ? `=== Changed files (complete list) ===\\n${stat}\\n\\n=== Detailed diff ===\\n${diff}`\n : diff;\n const result = await gitx.ai.generateCommitMessage(aiInput);\n\n // Use the AI-generated conventional commit subject + optional body as-is\n commitMsg = result.body\n ? `${result.subject}\\n\\n${result.body}`\n : result.subject;\n\n aiSpinner.succeed(\"Commit message generated.\");\n } catch (err) {\n aiSpinner.fail(\"AI generation failed.\");\n logger.warn(` ${err instanceof Error ? err.message : String(err)}`);\n logger.warn(\" Falling back to manual entry.\\n\");\n\n const { manualMsg } = await inquirer.prompt<{ manualMsg: string }>([\n {\n type: \"input\",\n name: \"manualMsg\",\n message: \"Commit message:\",\n validate: (v: string) => v.trim().length > 0 || \"Message cannot be empty\",\n },\n ]);\n commitMsg = manualMsg.trim();\n }\n }\n }\n\n // ── Show preview and confirm ─────────────────────────────────────────\n logger.info(\"\\n📋 Commit message preview:\\n\");\n logger.info(\"─\".repeat(60));\n logger.info(commitMsg);\n logger.info(\"─\".repeat(60));\n\n if (opts.dryRun) {\n logger.info(\"\\n🔍 Dry run — nothing committed.\");\n return;\n }\n\n const { confirmed } = await inquirer.prompt<{ confirmed: boolean }>([\n {\n type: \"confirm\",\n name: \"confirmed\",\n message: \"Commit with this message?\",\n default: true,\n },\n ]);\n\n if (!confirmed) {\n // Let user edit the message manually\n const { editedMsg } = await inquirer.prompt<{ editedMsg: string }>([\n {\n type: \"editor\",\n name: \"editedMsg\",\n message: \"Edit commit message:\",\n default: commitMsg,\n },\n ]);\n commitMsg = editedMsg.trim();\n if (!commitMsg) {\n logger.warn(\"Empty message — commit aborted.\");\n return;\n }\n }\n\n // ── Commit ───────────────────────────────────────────────────────────\n const commitSpinner = ora(\"Committing…\").start();\n await withLockRetry(() => commitChanges(commitMsg, cwd), cwd);\n commitSpinner.succeed(\"Committed ✓\");\n\n // ── Push ─────────────────────────────────────────────────────────────\n if (opts.push) {\n const branch = await getCurrentBranch(cwd);\n const pushSpinner = ora(`Pushing ${branch} to origin…`).start();\n await pushBranch(branch, cwd);\n pushSpinner.succeed(`Pushed to origin/${branch} ✓`);\n logger.success(`\\n✅ Done! Changes committed and pushed.`);\n } else {\n logger.success(`\\n✅ Done! Run \\`gitx commit --push\\` or \\`git push\\` to push.`);\n }\n });\n}\n\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8DzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8G5D;AA6SD,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAsQ9C"}
|