@rotorsoft/gent 1.22.0 → 1.23.0
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/dist/{chunk-75KVN7ZC.js → chunk-HCJWQPMW.js} +214 -307
- package/dist/chunk-HCJWQPMW.js.map +1 -0
- package/dist/chunk-ZDOEOZDC.js +43 -0
- package/dist/chunk-ZDOEOZDC.js.map +1 -0
- package/dist/chunk-ZQDWILU5.js +283 -0
- package/dist/chunk-ZQDWILU5.js.map +1 -0
- package/dist/github-remote-4TLUL2HX.js +9 -0
- package/dist/index.js +180 -246
- package/dist/index.js.map +1 -1
- package/dist/setup-labels-2SNO3QQL.js +9 -0
- package/dist/setup-labels-2SNO3QQL.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-75KVN7ZC.js.map +0 -1
- package/dist/setup-labels-STWAFV2E.js +0 -8
- /package/dist/{setup-labels-STWAFV2E.js.map → github-remote-4TLUL2HX.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
githubRemoteCommand
|
|
4
|
+
} from "./chunk-ZDOEOZDC.js";
|
|
5
|
+
import {
|
|
6
|
+
aiSpinnerText,
|
|
7
|
+
buildIssueLabels,
|
|
8
|
+
createSpinner,
|
|
9
|
+
extractTypeFromLabels,
|
|
10
|
+
getWorkflowLabels,
|
|
11
|
+
setupLabelsCommand,
|
|
12
|
+
sortByPriority,
|
|
13
|
+
withSpinner
|
|
14
|
+
} from "./chunk-ZQDWILU5.js";
|
|
2
15
|
import {
|
|
3
16
|
addIssueComment,
|
|
4
17
|
addPrComment,
|
|
5
|
-
aiSpinnerText,
|
|
6
18
|
assignIssue,
|
|
7
|
-
|
|
19
|
+
branchExists,
|
|
8
20
|
checkAIProvider,
|
|
9
21
|
checkClaudeCli,
|
|
10
22
|
checkGeminiCli,
|
|
@@ -12,35 +24,47 @@ import {
|
|
|
12
24
|
checkGitRepo,
|
|
13
25
|
checkInitialized,
|
|
14
26
|
checkLabelsExist,
|
|
27
|
+
checkoutBranch,
|
|
15
28
|
colors,
|
|
16
29
|
configExists,
|
|
30
|
+
createBranch,
|
|
17
31
|
createIssue,
|
|
18
32
|
createPullRequest,
|
|
19
|
-
|
|
20
|
-
extractTypeFromLabels,
|
|
33
|
+
fetchAndCheckout,
|
|
21
34
|
generateDefaultConfig,
|
|
35
|
+
getAuthorInitials,
|
|
36
|
+
getCommitsSinceBase,
|
|
22
37
|
getConfigPath,
|
|
38
|
+
getCurrentBranch,
|
|
39
|
+
getCurrentCommitSha,
|
|
23
40
|
getCurrentUser,
|
|
41
|
+
getDefaultBranch,
|
|
42
|
+
getDiffSummary,
|
|
24
43
|
getIssue,
|
|
44
|
+
getLastCommitTimestamp,
|
|
25
45
|
getPrForBranch,
|
|
26
46
|
getPrReviewData,
|
|
27
47
|
getPrStatus,
|
|
28
|
-
|
|
48
|
+
getRepoInfo,
|
|
49
|
+
getUnpushedCommits,
|
|
50
|
+
hasNewCommits,
|
|
51
|
+
hasUncommittedChanges,
|
|
52
|
+
isOnMainBranch,
|
|
29
53
|
isValidIssueNumber,
|
|
30
54
|
listIssues,
|
|
55
|
+
listLocalBranches,
|
|
31
56
|
listOpenPrs,
|
|
32
57
|
loadAgentInstructions,
|
|
33
58
|
loadConfig,
|
|
34
59
|
logger,
|
|
60
|
+
pushBranch,
|
|
61
|
+
remoteBranchExists,
|
|
35
62
|
replyToReviewComment,
|
|
36
63
|
resolveProvider,
|
|
37
64
|
sanitizeSlug,
|
|
38
65
|
setRuntimeProvider,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
updateIssueLabels,
|
|
42
|
-
withSpinner
|
|
43
|
-
} from "./chunk-75KVN7ZC.js";
|
|
66
|
+
updateIssueLabels
|
|
67
|
+
} from "./chunk-HCJWQPMW.js";
|
|
44
68
|
|
|
45
69
|
// src/index.ts
|
|
46
70
|
import { Command } from "commander";
|
|
@@ -48,6 +72,7 @@ import { Command } from "commander";
|
|
|
48
72
|
// src/commands/init.ts
|
|
49
73
|
import { writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
50
74
|
import { join as join2 } from "path";
|
|
75
|
+
import { execa } from "execa";
|
|
51
76
|
import inquirer from "inquirer";
|
|
52
77
|
|
|
53
78
|
// src/lib/progress.ts
|
|
@@ -86,6 +111,47 @@ Each entry documents: date, feature, decisions, files changed, tests, and concer
|
|
|
86
111
|
}
|
|
87
112
|
|
|
88
113
|
// src/commands/init.ts
|
|
114
|
+
var DEFAULT_GITIGNORE = `# Dependencies
|
|
115
|
+
node_modules/
|
|
116
|
+
|
|
117
|
+
# Build output
|
|
118
|
+
dist/
|
|
119
|
+
|
|
120
|
+
# Test coverage
|
|
121
|
+
coverage/
|
|
122
|
+
|
|
123
|
+
# IDE
|
|
124
|
+
.idea/
|
|
125
|
+
.vscode/
|
|
126
|
+
*.swp
|
|
127
|
+
*.swo
|
|
128
|
+
|
|
129
|
+
# OS
|
|
130
|
+
.DS_Store
|
|
131
|
+
Thumbs.db
|
|
132
|
+
|
|
133
|
+
# Logs
|
|
134
|
+
*.log
|
|
135
|
+
npm-debug.log*
|
|
136
|
+
|
|
137
|
+
# Environment
|
|
138
|
+
.env
|
|
139
|
+
.env.local
|
|
140
|
+
.env.*.local
|
|
141
|
+
|
|
142
|
+
# Temporary files
|
|
143
|
+
*.tmp
|
|
144
|
+
*.temp
|
|
145
|
+
.cache/
|
|
146
|
+
`;
|
|
147
|
+
async function hasCommits() {
|
|
148
|
+
try {
|
|
149
|
+
await execa("git", ["rev-parse", "HEAD"]);
|
|
150
|
+
return true;
|
|
151
|
+
} catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
89
155
|
var DEFAULT_AGENT_MD = `# AI Agent Instructions
|
|
90
156
|
|
|
91
157
|
This file contains instructions for the AI when working on this repository.
|
|
@@ -147,7 +213,7 @@ async function initCommand(options) {
|
|
|
147
213
|
const isGitRepo = await checkGitRepo();
|
|
148
214
|
if (!isGitRepo) {
|
|
149
215
|
logger.error("Not a git repository. Please run 'git init' first.");
|
|
150
|
-
|
|
216
|
+
return;
|
|
151
217
|
}
|
|
152
218
|
const cwd = process.cwd();
|
|
153
219
|
if (configExists(cwd) && !options.force) {
|
|
@@ -173,6 +239,13 @@ async function initCommand(options) {
|
|
|
173
239
|
default: "claude"
|
|
174
240
|
}
|
|
175
241
|
]);
|
|
242
|
+
const gitignorePath = join2(cwd, ".gitignore");
|
|
243
|
+
if (!existsSync2(gitignorePath)) {
|
|
244
|
+
writeFileSync2(gitignorePath, DEFAULT_GITIGNORE, "utf-8");
|
|
245
|
+
logger.success(`Created ${colors.file(".gitignore")}`);
|
|
246
|
+
} else {
|
|
247
|
+
logger.info(`${colors.file(".gitignore")} already exists, skipping`);
|
|
248
|
+
}
|
|
176
249
|
const configPath = getConfigPath(cwd);
|
|
177
250
|
writeFileSync2(configPath, generateDefaultConfig(provider), "utf-8");
|
|
178
251
|
logger.success(`Created ${colors.file(".gent.yml")}`);
|
|
@@ -186,26 +259,72 @@ async function initCommand(options) {
|
|
|
186
259
|
const config = loadConfig(cwd);
|
|
187
260
|
initializeProgress(config, cwd);
|
|
188
261
|
logger.success(`Created ${colors.file(config.progress.file)}`);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
"
|
|
192
|
-
|
|
262
|
+
if (!await hasCommits()) {
|
|
263
|
+
logger.newline();
|
|
264
|
+
logger.info("No commits found. Creating initial commit with gent config...");
|
|
265
|
+
await execa("git", ["add", ".gitignore", ".gent.yml", "AGENT.md", config.progress.file]);
|
|
266
|
+
await execa("git", ["commit", "-m", "chore: initialize gent workflow"]);
|
|
267
|
+
logger.success("Created initial commit");
|
|
268
|
+
}
|
|
269
|
+
const repoInfo = await getRepoInfo();
|
|
270
|
+
if (!repoInfo) {
|
|
271
|
+
logger.newline();
|
|
272
|
+
logger.box(
|
|
273
|
+
"Setup Complete",
|
|
274
|
+
`Next steps:
|
|
275
|
+
1. Edit ${colors.file("AGENT.md")} with your project-specific instructions
|
|
276
|
+
2. Edit ${colors.file(".gent.yml")} to customize settings
|
|
277
|
+
3. Create a GitHub remote: ${colors.command("gent github-remote")}
|
|
278
|
+
4. Run ${colors.command("gent setup-labels")} to create GitHub labels`
|
|
279
|
+
);
|
|
280
|
+
const { createRemote } = await inquirer.prompt([
|
|
281
|
+
{
|
|
282
|
+
type: "confirm",
|
|
283
|
+
name: "createRemote",
|
|
284
|
+
message: "No GitHub remote found. Would you like to create one now?",
|
|
285
|
+
default: true
|
|
286
|
+
}
|
|
287
|
+
]);
|
|
288
|
+
if (createRemote) {
|
|
289
|
+
const { githubRemoteCommand: githubRemoteCommand2 } = await import("./github-remote-4TLUL2HX.js");
|
|
290
|
+
const success = await githubRemoteCommand2();
|
|
291
|
+
if (success) {
|
|
292
|
+
const { setupLabels } = await inquirer.prompt([
|
|
293
|
+
{
|
|
294
|
+
type: "confirm",
|
|
295
|
+
name: "setupLabels",
|
|
296
|
+
message: "Would you like to setup GitHub labels now?",
|
|
297
|
+
default: true
|
|
298
|
+
}
|
|
299
|
+
]);
|
|
300
|
+
if (setupLabels) {
|
|
301
|
+
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-2SNO3QQL.js");
|
|
302
|
+
await setupLabelsCommand2();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
logger.newline();
|
|
308
|
+
logger.box(
|
|
309
|
+
"Setup Complete",
|
|
310
|
+
`Next steps:
|
|
193
311
|
1. Edit ${colors.file("AGENT.md")} with your project-specific instructions
|
|
194
312
|
2. Edit ${colors.file(".gent.yml")} to customize settings
|
|
195
313
|
3. Run ${colors.command("gent setup-labels")} to create GitHub labels
|
|
196
314
|
4. Run ${colors.command("gent create <description>")} to create your first ticket`
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
315
|
+
);
|
|
316
|
+
const { setupLabels } = await inquirer.prompt([
|
|
317
|
+
{
|
|
318
|
+
type: "confirm",
|
|
319
|
+
name: "setupLabels",
|
|
320
|
+
message: "Would you like to setup GitHub labels now?",
|
|
321
|
+
default: true
|
|
322
|
+
}
|
|
323
|
+
]);
|
|
324
|
+
if (setupLabels) {
|
|
325
|
+
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-2SNO3QQL.js");
|
|
326
|
+
await setupLabelsCommand2();
|
|
204
327
|
}
|
|
205
|
-
]);
|
|
206
|
-
if (setupLabels) {
|
|
207
|
-
const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-STWAFV2E.js");
|
|
208
|
-
await setupLabelsCommand2();
|
|
209
328
|
}
|
|
210
329
|
}
|
|
211
330
|
|
|
@@ -215,7 +334,7 @@ import chalk from "chalk";
|
|
|
215
334
|
|
|
216
335
|
// src/lib/ai-provider.ts
|
|
217
336
|
import { spawn } from "child_process";
|
|
218
|
-
import { execa } from "execa";
|
|
337
|
+
import { execa as execa2 } from "execa";
|
|
219
338
|
async function getOtherAvailableProviders(currentProvider) {
|
|
220
339
|
const allProviders = ["claude", "gemini", "codex"];
|
|
221
340
|
const others = allProviders.filter((p) => p !== currentProvider);
|
|
@@ -266,20 +385,20 @@ async function invokeAIInteractive(prompt, config, providerOverride) {
|
|
|
266
385
|
case "claude": {
|
|
267
386
|
const args = ["--permission-mode", config.claude.permission_mode, prompt];
|
|
268
387
|
return {
|
|
269
|
-
result:
|
|
388
|
+
result: execa2("claude", args, { stdio: "inherit" }),
|
|
270
389
|
provider
|
|
271
390
|
};
|
|
272
391
|
}
|
|
273
392
|
case "gemini": {
|
|
274
393
|
return {
|
|
275
|
-
result:
|
|
394
|
+
result: execa2("gemini", ["-i", prompt], { stdio: "inherit" }),
|
|
276
395
|
provider
|
|
277
396
|
};
|
|
278
397
|
}
|
|
279
398
|
case "codex": {
|
|
280
399
|
const args = prompt ? [prompt] : [];
|
|
281
400
|
return {
|
|
282
|
-
result:
|
|
401
|
+
result: execa2("codex", args, { stdio: "inherit" }),
|
|
283
402
|
provider
|
|
284
403
|
};
|
|
285
404
|
}
|
|
@@ -325,7 +444,7 @@ async function invokeClaudeInternal(options) {
|
|
|
325
444
|
}
|
|
326
445
|
args.push(options.prompt);
|
|
327
446
|
if (options.printOutput) {
|
|
328
|
-
const subprocess =
|
|
447
|
+
const subprocess = execa2("claude", args, {
|
|
329
448
|
stdio: "inherit"
|
|
330
449
|
});
|
|
331
450
|
await subprocess;
|
|
@@ -361,7 +480,7 @@ async function invokeClaudeInternal(options) {
|
|
|
361
480
|
child.on("error", reject);
|
|
362
481
|
});
|
|
363
482
|
} else {
|
|
364
|
-
const { stdout } = await
|
|
483
|
+
const { stdout } = await execa2("claude", args);
|
|
365
484
|
return stdout;
|
|
366
485
|
}
|
|
367
486
|
}
|
|
@@ -369,7 +488,7 @@ async function invokeGeminiInternal(options) {
|
|
|
369
488
|
const args = [];
|
|
370
489
|
args.push(options.prompt);
|
|
371
490
|
if (options.printOutput) {
|
|
372
|
-
const subprocess =
|
|
491
|
+
const subprocess = execa2("gemini", args, {
|
|
373
492
|
stdio: "inherit"
|
|
374
493
|
});
|
|
375
494
|
await subprocess;
|
|
@@ -405,14 +524,14 @@ async function invokeGeminiInternal(options) {
|
|
|
405
524
|
child.on("error", reject);
|
|
406
525
|
});
|
|
407
526
|
} else {
|
|
408
|
-
const { stdout } = await
|
|
527
|
+
const { stdout } = await execa2("gemini", args);
|
|
409
528
|
return stdout;
|
|
410
529
|
}
|
|
411
530
|
}
|
|
412
531
|
async function invokeCodexInternal(options) {
|
|
413
532
|
const args = ["exec", options.prompt];
|
|
414
533
|
if (options.printOutput) {
|
|
415
|
-
const subprocess =
|
|
534
|
+
const subprocess = execa2("codex", args, {
|
|
416
535
|
stdio: "inherit"
|
|
417
536
|
});
|
|
418
537
|
await subprocess;
|
|
@@ -448,7 +567,7 @@ async function invokeCodexInternal(options) {
|
|
|
448
567
|
child.on("error", reject);
|
|
449
568
|
});
|
|
450
569
|
} else {
|
|
451
|
-
const { stdout } = await
|
|
570
|
+
const { stdout } = await execa2("codex", args);
|
|
452
571
|
return stdout;
|
|
453
572
|
}
|
|
454
573
|
}
|
|
@@ -842,158 +961,6 @@ Next steps:
|
|
|
842
961
|
import chalk2 from "chalk";
|
|
843
962
|
import inquirer3 from "inquirer";
|
|
844
963
|
|
|
845
|
-
// src/lib/git.ts
|
|
846
|
-
import { execa as execa2 } from "execa";
|
|
847
|
-
async function getCurrentBranch() {
|
|
848
|
-
const { stdout } = await execa2("git", ["branch", "--show-current"]);
|
|
849
|
-
return stdout.trim();
|
|
850
|
-
}
|
|
851
|
-
async function isOnMainBranch() {
|
|
852
|
-
const branch = await getCurrentBranch();
|
|
853
|
-
return branch === "main" || branch === "master";
|
|
854
|
-
}
|
|
855
|
-
async function getDefaultBranch() {
|
|
856
|
-
try {
|
|
857
|
-
const { stdout } = await execa2("git", [
|
|
858
|
-
"symbolic-ref",
|
|
859
|
-
"refs/remotes/origin/HEAD"
|
|
860
|
-
]);
|
|
861
|
-
return stdout.trim().replace("refs/remotes/origin/", "");
|
|
862
|
-
} catch {
|
|
863
|
-
try {
|
|
864
|
-
await execa2("git", ["rev-parse", "--verify", "main"]);
|
|
865
|
-
return "main";
|
|
866
|
-
} catch {
|
|
867
|
-
return "master";
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
async function branchExists(name) {
|
|
872
|
-
try {
|
|
873
|
-
await execa2("git", ["rev-parse", "--verify", name]);
|
|
874
|
-
return true;
|
|
875
|
-
} catch {
|
|
876
|
-
return false;
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
async function createBranch(name, from) {
|
|
880
|
-
if (from) {
|
|
881
|
-
await execa2("git", ["checkout", "-b", name, from]);
|
|
882
|
-
} else {
|
|
883
|
-
await execa2("git", ["checkout", "-b", name]);
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
async function checkoutBranch(name) {
|
|
887
|
-
await execa2("git", ["checkout", name]);
|
|
888
|
-
}
|
|
889
|
-
async function hasUncommittedChanges() {
|
|
890
|
-
const { stdout } = await execa2("git", ["status", "--porcelain"]);
|
|
891
|
-
return stdout.trim().length > 0;
|
|
892
|
-
}
|
|
893
|
-
async function getUnpushedCommits() {
|
|
894
|
-
try {
|
|
895
|
-
const { stdout } = await execa2("git", ["log", "@{u}..HEAD", "--oneline"]);
|
|
896
|
-
return stdout.trim().length > 0;
|
|
897
|
-
} catch {
|
|
898
|
-
return true;
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
async function pushBranch(branch) {
|
|
902
|
-
const branchName = branch || await getCurrentBranch();
|
|
903
|
-
await execa2("git", ["push", "-u", "origin", branchName]);
|
|
904
|
-
}
|
|
905
|
-
async function getAuthorInitials() {
|
|
906
|
-
try {
|
|
907
|
-
const { stdout } = await execa2("git", ["config", "user.initials"]);
|
|
908
|
-
if (stdout.trim()) {
|
|
909
|
-
return stdout.trim();
|
|
910
|
-
}
|
|
911
|
-
} catch {
|
|
912
|
-
}
|
|
913
|
-
try {
|
|
914
|
-
const { stdout } = await execa2("git", ["config", "user.name"]);
|
|
915
|
-
const name = stdout.trim();
|
|
916
|
-
if (name) {
|
|
917
|
-
const parts = name.split(/\s+/);
|
|
918
|
-
return parts.map((p) => p[0]?.toLowerCase() || "").join("");
|
|
919
|
-
}
|
|
920
|
-
} catch {
|
|
921
|
-
}
|
|
922
|
-
return "dev";
|
|
923
|
-
}
|
|
924
|
-
async function getRepoInfo() {
|
|
925
|
-
try {
|
|
926
|
-
const { stdout } = await execa2("git", [
|
|
927
|
-
"config",
|
|
928
|
-
"--get",
|
|
929
|
-
"remote.origin.url"
|
|
930
|
-
]);
|
|
931
|
-
const url = stdout.trim();
|
|
932
|
-
const sshMatch = url.match(/git@github\.com:([^/]+)\/([^.]+)/);
|
|
933
|
-
if (sshMatch) {
|
|
934
|
-
return { owner: sshMatch[1], repo: sshMatch[2] };
|
|
935
|
-
}
|
|
936
|
-
const httpsMatch = url.match(/github\.com\/([^/]+)\/([^.]+)/);
|
|
937
|
-
if (httpsMatch) {
|
|
938
|
-
return { owner: httpsMatch[1], repo: httpsMatch[2] };
|
|
939
|
-
}
|
|
940
|
-
return null;
|
|
941
|
-
} catch {
|
|
942
|
-
return null;
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
async function getCommitsSinceBase(base = "main") {
|
|
946
|
-
try {
|
|
947
|
-
const { stdout } = await execa2("git", [
|
|
948
|
-
"log",
|
|
949
|
-
`${base}..HEAD`,
|
|
950
|
-
"--pretty=format:%s"
|
|
951
|
-
]);
|
|
952
|
-
return stdout.trim().split("\n").filter(Boolean);
|
|
953
|
-
} catch {
|
|
954
|
-
return [];
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
async function getDiffSummary(base = "main") {
|
|
958
|
-
try {
|
|
959
|
-
const { stdout } = await execa2("git", ["diff", `${base}...HEAD`, "--stat"]);
|
|
960
|
-
return stdout.trim();
|
|
961
|
-
} catch {
|
|
962
|
-
return "";
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
async function getCurrentCommitSha() {
|
|
966
|
-
const { stdout } = await execa2("git", ["rev-parse", "HEAD"]);
|
|
967
|
-
return stdout.trim();
|
|
968
|
-
}
|
|
969
|
-
async function hasNewCommits(beforeSha) {
|
|
970
|
-
const currentSha = await getCurrentCommitSha();
|
|
971
|
-
return currentSha !== beforeSha;
|
|
972
|
-
}
|
|
973
|
-
async function getLastCommitTimestamp() {
|
|
974
|
-
const { stdout } = await execa2("git", ["log", "-1", "--format=%cI"]);
|
|
975
|
-
return stdout.trim();
|
|
976
|
-
}
|
|
977
|
-
async function listLocalBranches() {
|
|
978
|
-
const { stdout } = await execa2("git", [
|
|
979
|
-
"branch",
|
|
980
|
-
"--format=%(refname:short)"
|
|
981
|
-
]);
|
|
982
|
-
return stdout.trim().split("\n").filter(Boolean);
|
|
983
|
-
}
|
|
984
|
-
async function remoteBranchExists(name) {
|
|
985
|
-
try {
|
|
986
|
-
await execa2("git", ["ls-remote", "--exit-code", "--heads", "origin", name]);
|
|
987
|
-
return true;
|
|
988
|
-
} catch {
|
|
989
|
-
return false;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
async function fetchAndCheckout(name) {
|
|
993
|
-
await execa2("git", ["fetch", "origin", `${name}:${name}`]);
|
|
994
|
-
await execa2("git", ["checkout", name]);
|
|
995
|
-
}
|
|
996
|
-
|
|
997
964
|
// src/lib/branch.ts
|
|
998
965
|
async function generateBranchName(config, issueNumber, issueTitle, type) {
|
|
999
966
|
const author = await resolveAuthor(config);
|
|
@@ -2240,7 +2207,7 @@ import { homedir } from "os";
|
|
|
2240
2207
|
// package.json
|
|
2241
2208
|
var package_default = {
|
|
2242
2209
|
name: "@rotorsoft/gent",
|
|
2243
|
-
version: "1.
|
|
2210
|
+
version: "1.23.0",
|
|
2244
2211
|
description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
|
|
2245
2212
|
keywords: [
|
|
2246
2213
|
"cli",
|
|
@@ -2662,7 +2629,7 @@ async function statusCommand() {
|
|
|
2662
2629
|
}
|
|
2663
2630
|
|
|
2664
2631
|
// src/commands/tui.ts
|
|
2665
|
-
import { execa as
|
|
2632
|
+
import { execa as execa4 } from "execa";
|
|
2666
2633
|
|
|
2667
2634
|
// src/tui/state.ts
|
|
2668
2635
|
var envCache = null;
|
|
@@ -3140,22 +3107,16 @@ function buildDashboardLines(state, actions, hint, refreshing, versionCheck) {
|
|
|
3140
3107
|
}
|
|
3141
3108
|
if (!state.hasConfig) {
|
|
3142
3109
|
section("Setup");
|
|
3143
|
-
out(row(chalk3.yellow(
|
|
3144
|
-
out(row(chalk3.dim("Press [i] to
|
|
3145
|
-
} else if (state.hasValidRemote
|
|
3110
|
+
out(row(chalk3.yellow("Step 1: Initialize gent configuration"), w));
|
|
3111
|
+
out(row(chalk3.dim("Press [i] to run gent init"), w));
|
|
3112
|
+
} else if (!state.hasValidRemote) {
|
|
3146
3113
|
section("Setup");
|
|
3147
|
-
out(row(chalk3.yellow(
|
|
3114
|
+
out(row(chalk3.yellow("Step 2: Create a GitHub repository"), w));
|
|
3115
|
+
out(row(chalk3.dim("Press [g] to create a GitHub repo and push"), w));
|
|
3116
|
+
} else if (!state.hasLabels) {
|
|
3117
|
+
section("Setup");
|
|
3118
|
+
out(row(chalk3.yellow("Step 3: Create workflow labels"), w));
|
|
3148
3119
|
out(row(chalk3.dim("Press [b] to set up labels"), w));
|
|
3149
|
-
} else if (!state.hasValidRemote) {
|
|
3150
|
-
section("Hint");
|
|
3151
|
-
out(
|
|
3152
|
-
row(
|
|
3153
|
-
chalk3.yellow(
|
|
3154
|
-
"Press [g] to create a GitHub repo and push"
|
|
3155
|
-
),
|
|
3156
|
-
w
|
|
3157
|
-
)
|
|
3158
|
-
);
|
|
3159
3120
|
} else if (hint) {
|
|
3160
3121
|
section("Hint");
|
|
3161
3122
|
out(row(chalk3.yellow(hint), w));
|
|
@@ -3669,38 +3630,6 @@ function showStatus(title, message, dashboardLines) {
|
|
|
3669
3630
|
renderOverlay(dashboardLines, lines, w);
|
|
3670
3631
|
}
|
|
3671
3632
|
|
|
3672
|
-
// src/commands/github-remote.ts
|
|
3673
|
-
import { execa as execa4 } from "execa";
|
|
3674
|
-
async function githubRemoteCommand() {
|
|
3675
|
-
const isAuthenticated = await checkGhAuth();
|
|
3676
|
-
if (!isAuthenticated) {
|
|
3677
|
-
logger.error("GitHub CLI is not authenticated. Run: gh auth login");
|
|
3678
|
-
return false;
|
|
3679
|
-
}
|
|
3680
|
-
try {
|
|
3681
|
-
try {
|
|
3682
|
-
const { stdout } = await execa4("git", [
|
|
3683
|
-
"config",
|
|
3684
|
-
"--get",
|
|
3685
|
-
"remote.origin.url"
|
|
3686
|
-
]);
|
|
3687
|
-
if (stdout.trim()) {
|
|
3688
|
-
logger.error("Remote origin already exists: " + stdout.trim());
|
|
3689
|
-
return false;
|
|
3690
|
-
}
|
|
3691
|
-
} catch {
|
|
3692
|
-
}
|
|
3693
|
-
logger.info("Creating GitHub repository...");
|
|
3694
|
-
await execa4("gh", ["repo", "create", "--source=.", "--push", "--private"]);
|
|
3695
|
-
const branch = await getCurrentBranch();
|
|
3696
|
-
logger.success(`Repository created and ${branch} pushed to GitHub`);
|
|
3697
|
-
return true;
|
|
3698
|
-
} catch (error) {
|
|
3699
|
-
logger.error(`Failed to create remote: ${error}`);
|
|
3700
|
-
return false;
|
|
3701
|
-
}
|
|
3702
|
-
}
|
|
3703
|
-
|
|
3704
3633
|
// src/commands/tui.ts
|
|
3705
3634
|
var CANCEL = /* @__PURE__ */ Symbol("cancel");
|
|
3706
3635
|
async function waitForKey(validKeys) {
|
|
@@ -3812,19 +3741,19 @@ async function executeAction(actionId, state, dashboardLines) {
|
|
|
3812
3741
|
}
|
|
3813
3742
|
async function handleCommit(state, dashboardLines) {
|
|
3814
3743
|
try {
|
|
3815
|
-
const { stdout: status } = await
|
|
3744
|
+
const { stdout: status } = await execa4("git", ["status", "--short"]);
|
|
3816
3745
|
if (!status.trim()) {
|
|
3817
3746
|
showStatus("Commit", "No changes to commit", dashboardLines);
|
|
3818
3747
|
await new Promise((r) => setTimeout(r, 1500));
|
|
3819
3748
|
return false;
|
|
3820
3749
|
}
|
|
3821
|
-
await
|
|
3822
|
-
const { stdout: diffStat } = await
|
|
3750
|
+
await execa4("git", ["add", "-A"]);
|
|
3751
|
+
const { stdout: diffStat } = await execa4("git", [
|
|
3823
3752
|
"diff",
|
|
3824
3753
|
"--cached",
|
|
3825
3754
|
"--stat"
|
|
3826
3755
|
]);
|
|
3827
|
-
const { stdout: diffPatch } = await
|
|
3756
|
+
const { stdout: diffPatch } = await execa4("git", ["diff", "--cached"]);
|
|
3828
3757
|
const diffContent = (diffStat + "\n\n" + diffPatch).slice(0, 4e3);
|
|
3829
3758
|
const issueNumber = state.issue?.number ?? null;
|
|
3830
3759
|
const issueTitle = state.issue?.title ?? null;
|
|
@@ -3839,7 +3768,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3839
3768
|
dashboardLines
|
|
3840
3769
|
});
|
|
3841
3770
|
if (!mode) {
|
|
3842
|
-
await
|
|
3771
|
+
await execa4("git", ["reset", "HEAD"]);
|
|
3843
3772
|
return false;
|
|
3844
3773
|
}
|
|
3845
3774
|
let message;
|
|
@@ -3861,7 +3790,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3861
3790
|
);
|
|
3862
3791
|
}
|
|
3863
3792
|
if (message === CANCEL) {
|
|
3864
|
-
await
|
|
3793
|
+
await execa4("git", ["reset", "HEAD"]);
|
|
3865
3794
|
return false;
|
|
3866
3795
|
}
|
|
3867
3796
|
const confirmed = await showConfirm({
|
|
@@ -3870,7 +3799,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3870
3799
|
dashboardLines
|
|
3871
3800
|
});
|
|
3872
3801
|
if (!confirmed) {
|
|
3873
|
-
await
|
|
3802
|
+
await execa4("git", ["reset", "HEAD"]);
|
|
3874
3803
|
return false;
|
|
3875
3804
|
}
|
|
3876
3805
|
const providerEmail = getProviderEmail(provider);
|
|
@@ -3878,7 +3807,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3878
3807
|
|
|
3879
3808
|
Co-Authored-By: ${providerName} <${providerEmail}>`;
|
|
3880
3809
|
showStatus("Committing", "Committing changes...", dashboardLines);
|
|
3881
|
-
await
|
|
3810
|
+
await execa4("git", ["commit", "-m", fullMessage]);
|
|
3882
3811
|
return true;
|
|
3883
3812
|
} catch (error) {
|
|
3884
3813
|
logger.error(`Commit failed: ${error}`);
|
|
@@ -3958,10 +3887,10 @@ ${feedbackLines}`);
|
|
|
3958
3887
|
}
|
|
3959
3888
|
async function handlePush(dashboardLines) {
|
|
3960
3889
|
try {
|
|
3961
|
-
const { stdout: branch } = await
|
|
3890
|
+
const { stdout: branch } = await execa4("git", ["branch", "--show-current"]);
|
|
3962
3891
|
const branchName = branch.trim();
|
|
3963
3892
|
showStatus("Pushing", `Pushing ${branchName} to remote...`, dashboardLines);
|
|
3964
|
-
await
|
|
3893
|
+
await execa4("git", ["push", "-u", "origin", branchName]);
|
|
3965
3894
|
} catch (error) {
|
|
3966
3895
|
logger.error(`Push failed: ${error}`);
|
|
3967
3896
|
}
|
|
@@ -4223,6 +4152,11 @@ async function checkPrerequisites() {
|
|
|
4223
4152
|
logger.warning('Repository not initialized. Run "gent init" to set up this repository.');
|
|
4224
4153
|
return false;
|
|
4225
4154
|
}
|
|
4155
|
+
const repoInfo = await getRepoInfo();
|
|
4156
|
+
if (!repoInfo) {
|
|
4157
|
+
logger.warning('No GitHub remote found. Run "gent github-remote" to create one.');
|
|
4158
|
+
return false;
|
|
4159
|
+
}
|
|
4226
4160
|
const labelsOk = await checkLabelsExist();
|
|
4227
4161
|
if (!labelsOk) {
|
|
4228
4162
|
logger.warning('GitHub labels not found. Run "gent setup-labels" to create required labels.');
|