@rotorsoft/gent 1.21.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 +192 -251
- 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;
|
|
@@ -2878,19 +2845,24 @@ function truncate(text, max) {
|
|
|
2878
2845
|
if (text.length <= max) return text;
|
|
2879
2846
|
return text.slice(0, max - 1) + "\u2026";
|
|
2880
2847
|
}
|
|
2881
|
-
function
|
|
2848
|
+
function extractDescriptionLines(body, maxLen, maxLines = 3) {
|
|
2849
|
+
const result = [];
|
|
2882
2850
|
const lines = body.split("\n");
|
|
2883
2851
|
for (const line of lines) {
|
|
2852
|
+
if (result.length >= maxLines) break;
|
|
2884
2853
|
const trimmed = line.trim();
|
|
2885
2854
|
if (!trimmed) continue;
|
|
2886
2855
|
if (trimmed.startsWith("#")) continue;
|
|
2887
2856
|
if (trimmed.startsWith("---")) continue;
|
|
2888
2857
|
if (trimmed.startsWith("META:")) continue;
|
|
2889
2858
|
if (trimmed.startsWith("**Type:**")) continue;
|
|
2859
|
+
if (trimmed.startsWith("**Category:**")) continue;
|
|
2860
|
+
if (trimmed.startsWith("**Priority:**")) continue;
|
|
2861
|
+
if (trimmed.startsWith("**Risk:**")) continue;
|
|
2890
2862
|
const clean = trimmed.replace(/\*\*/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
2891
|
-
|
|
2863
|
+
result.push(truncate(clean, maxLen));
|
|
2892
2864
|
}
|
|
2893
|
-
return
|
|
2865
|
+
return result;
|
|
2894
2866
|
}
|
|
2895
2867
|
function topRow(title, w) {
|
|
2896
2868
|
const label = ` ${title} `;
|
|
@@ -3050,8 +3022,10 @@ function buildDashboardLines(state, actions, hint, refreshing, versionCheck) {
|
|
|
3050
3022
|
w
|
|
3051
3023
|
)
|
|
3052
3024
|
);
|
|
3053
|
-
const
|
|
3054
|
-
|
|
3025
|
+
const descLines = extractDescriptionLines(state.issue.body, descMax);
|
|
3026
|
+
for (const desc of descLines) {
|
|
3027
|
+
out(row(" " + chalk3.dim(desc), w));
|
|
3028
|
+
}
|
|
3055
3029
|
const tags = [];
|
|
3056
3030
|
if (state.workflowStatus !== "none")
|
|
3057
3031
|
tags.push(workflowBadge(state.workflowStatus));
|
|
@@ -3133,22 +3107,16 @@ function buildDashboardLines(state, actions, hint, refreshing, versionCheck) {
|
|
|
3133
3107
|
}
|
|
3134
3108
|
if (!state.hasConfig) {
|
|
3135
3109
|
section("Setup");
|
|
3136
|
-
out(row(chalk3.yellow(
|
|
3137
|
-
out(row(chalk3.dim("Press [i] to
|
|
3138
|
-
} 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) {
|
|
3113
|
+
section("Setup");
|
|
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) {
|
|
3139
3117
|
section("Setup");
|
|
3140
|
-
out(row(chalk3.yellow(
|
|
3118
|
+
out(row(chalk3.yellow("Step 3: Create workflow labels"), w));
|
|
3141
3119
|
out(row(chalk3.dim("Press [b] to set up labels"), w));
|
|
3142
|
-
} else if (!state.hasValidRemote) {
|
|
3143
|
-
section("Hint");
|
|
3144
|
-
out(
|
|
3145
|
-
row(
|
|
3146
|
-
chalk3.yellow(
|
|
3147
|
-
"Press [g] to create a GitHub repo and push"
|
|
3148
|
-
),
|
|
3149
|
-
w
|
|
3150
|
-
)
|
|
3151
|
-
);
|
|
3152
3120
|
} else if (hint) {
|
|
3153
3121
|
section("Hint");
|
|
3154
3122
|
out(row(chalk3.yellow(hint), w));
|
|
@@ -3662,38 +3630,6 @@ function showStatus(title, message, dashboardLines) {
|
|
|
3662
3630
|
renderOverlay(dashboardLines, lines, w);
|
|
3663
3631
|
}
|
|
3664
3632
|
|
|
3665
|
-
// src/commands/github-remote.ts
|
|
3666
|
-
import { execa as execa4 } from "execa";
|
|
3667
|
-
async function githubRemoteCommand() {
|
|
3668
|
-
const isAuthenticated = await checkGhAuth();
|
|
3669
|
-
if (!isAuthenticated) {
|
|
3670
|
-
logger.error("GitHub CLI is not authenticated. Run: gh auth login");
|
|
3671
|
-
return false;
|
|
3672
|
-
}
|
|
3673
|
-
try {
|
|
3674
|
-
try {
|
|
3675
|
-
const { stdout } = await execa4("git", [
|
|
3676
|
-
"config",
|
|
3677
|
-
"--get",
|
|
3678
|
-
"remote.origin.url"
|
|
3679
|
-
]);
|
|
3680
|
-
if (stdout.trim()) {
|
|
3681
|
-
logger.error("Remote origin already exists: " + stdout.trim());
|
|
3682
|
-
return false;
|
|
3683
|
-
}
|
|
3684
|
-
} catch {
|
|
3685
|
-
}
|
|
3686
|
-
logger.info("Creating GitHub repository...");
|
|
3687
|
-
await execa4("gh", ["repo", "create", "--source=.", "--push", "--private"]);
|
|
3688
|
-
const branch = await getCurrentBranch();
|
|
3689
|
-
logger.success(`Repository created and ${branch} pushed to GitHub`);
|
|
3690
|
-
return true;
|
|
3691
|
-
} catch (error) {
|
|
3692
|
-
logger.error(`Failed to create remote: ${error}`);
|
|
3693
|
-
return false;
|
|
3694
|
-
}
|
|
3695
|
-
}
|
|
3696
|
-
|
|
3697
3633
|
// src/commands/tui.ts
|
|
3698
3634
|
var CANCEL = /* @__PURE__ */ Symbol("cancel");
|
|
3699
3635
|
async function waitForKey(validKeys) {
|
|
@@ -3805,19 +3741,19 @@ async function executeAction(actionId, state, dashboardLines) {
|
|
|
3805
3741
|
}
|
|
3806
3742
|
async function handleCommit(state, dashboardLines) {
|
|
3807
3743
|
try {
|
|
3808
|
-
const { stdout: status } = await
|
|
3744
|
+
const { stdout: status } = await execa4("git", ["status", "--short"]);
|
|
3809
3745
|
if (!status.trim()) {
|
|
3810
3746
|
showStatus("Commit", "No changes to commit", dashboardLines);
|
|
3811
3747
|
await new Promise((r) => setTimeout(r, 1500));
|
|
3812
3748
|
return false;
|
|
3813
3749
|
}
|
|
3814
|
-
await
|
|
3815
|
-
const { stdout: diffStat } = await
|
|
3750
|
+
await execa4("git", ["add", "-A"]);
|
|
3751
|
+
const { stdout: diffStat } = await execa4("git", [
|
|
3816
3752
|
"diff",
|
|
3817
3753
|
"--cached",
|
|
3818
3754
|
"--stat"
|
|
3819
3755
|
]);
|
|
3820
|
-
const { stdout: diffPatch } = await
|
|
3756
|
+
const { stdout: diffPatch } = await execa4("git", ["diff", "--cached"]);
|
|
3821
3757
|
const diffContent = (diffStat + "\n\n" + diffPatch).slice(0, 4e3);
|
|
3822
3758
|
const issueNumber = state.issue?.number ?? null;
|
|
3823
3759
|
const issueTitle = state.issue?.title ?? null;
|
|
@@ -3832,7 +3768,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3832
3768
|
dashboardLines
|
|
3833
3769
|
});
|
|
3834
3770
|
if (!mode) {
|
|
3835
|
-
await
|
|
3771
|
+
await execa4("git", ["reset", "HEAD"]);
|
|
3836
3772
|
return false;
|
|
3837
3773
|
}
|
|
3838
3774
|
let message;
|
|
@@ -3854,7 +3790,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3854
3790
|
);
|
|
3855
3791
|
}
|
|
3856
3792
|
if (message === CANCEL) {
|
|
3857
|
-
await
|
|
3793
|
+
await execa4("git", ["reset", "HEAD"]);
|
|
3858
3794
|
return false;
|
|
3859
3795
|
}
|
|
3860
3796
|
const confirmed = await showConfirm({
|
|
@@ -3863,7 +3799,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3863
3799
|
dashboardLines
|
|
3864
3800
|
});
|
|
3865
3801
|
if (!confirmed) {
|
|
3866
|
-
await
|
|
3802
|
+
await execa4("git", ["reset", "HEAD"]);
|
|
3867
3803
|
return false;
|
|
3868
3804
|
}
|
|
3869
3805
|
const providerEmail = getProviderEmail(provider);
|
|
@@ -3871,7 +3807,7 @@ async function handleCommit(state, dashboardLines) {
|
|
|
3871
3807
|
|
|
3872
3808
|
Co-Authored-By: ${providerName} <${providerEmail}>`;
|
|
3873
3809
|
showStatus("Committing", "Committing changes...", dashboardLines);
|
|
3874
|
-
await
|
|
3810
|
+
await execa4("git", ["commit", "-m", fullMessage]);
|
|
3875
3811
|
return true;
|
|
3876
3812
|
} catch (error) {
|
|
3877
3813
|
logger.error(`Commit failed: ${error}`);
|
|
@@ -3951,10 +3887,10 @@ ${feedbackLines}`);
|
|
|
3951
3887
|
}
|
|
3952
3888
|
async function handlePush(dashboardLines) {
|
|
3953
3889
|
try {
|
|
3954
|
-
const { stdout: branch } = await
|
|
3890
|
+
const { stdout: branch } = await execa4("git", ["branch", "--show-current"]);
|
|
3955
3891
|
const branchName = branch.trim();
|
|
3956
3892
|
showStatus("Pushing", `Pushing ${branchName} to remote...`, dashboardLines);
|
|
3957
|
-
await
|
|
3893
|
+
await execa4("git", ["push", "-u", "origin", branchName]);
|
|
3958
3894
|
} catch (error) {
|
|
3959
3895
|
logger.error(`Push failed: ${error}`);
|
|
3960
3896
|
}
|
|
@@ -4216,6 +4152,11 @@ async function checkPrerequisites() {
|
|
|
4216
4152
|
logger.warning('Repository not initialized. Run "gent init" to set up this repository.');
|
|
4217
4153
|
return false;
|
|
4218
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
|
+
}
|
|
4219
4160
|
const labelsOk = await checkLabelsExist();
|
|
4220
4161
|
if (!labelsOk) {
|
|
4221
4162
|
logger.warning('GitHub labels not found. Run "gent setup-labels" to create required labels.');
|