@rotorsoft/gent 1.22.0 → 1.24.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/index.js CHANGED
@@ -1,46 +1,69 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ githubRemoteCommand
4
+ } from "./chunk-OIHSXI5X.js";
5
+ import {
6
+ aiSpinnerText,
7
+ buildIssueLabels,
8
+ createSpinner,
9
+ extractTypeFromLabels,
10
+ getWorkflowLabels,
11
+ setupLabelsCommand,
12
+ sortByPriority,
13
+ withSpinner
14
+ } from "./chunk-YS7HWP4W.js";
2
15
  import {
3
16
  addIssueComment,
4
17
  addPrComment,
5
- aiSpinnerText,
6
18
  assignIssue,
7
- buildIssueLabels,
19
+ branchExists,
8
20
  checkAIProvider,
9
21
  checkClaudeCli,
10
22
  checkGeminiCli,
11
23
  checkGhAuth,
12
24
  checkGitRepo,
13
- checkInitialized,
14
25
  checkLabelsExist,
26
+ checkoutBranch,
15
27
  colors,
16
28
  configExists,
29
+ createBranch,
17
30
  createIssue,
18
31
  createPullRequest,
19
- createSpinner,
20
- extractTypeFromLabels,
32
+ fetchAndCheckout,
21
33
  generateDefaultConfig,
34
+ getAuthorInitials,
35
+ getCommitsSinceBase,
22
36
  getConfigPath,
37
+ getCurrentBranch,
38
+ getCurrentCommitSha,
23
39
  getCurrentUser,
40
+ getDefaultBranch,
41
+ getDiffSummary,
24
42
  getIssue,
43
+ getLastCommitTimestamp,
25
44
  getPrForBranch,
26
45
  getPrReviewData,
27
46
  getPrStatus,
28
- getWorkflowLabels,
47
+ getRepoInfo,
48
+ getUnpushedCommits,
49
+ hasNewCommits,
50
+ hasUncommittedChanges,
51
+ isOnMainBranch,
29
52
  isValidIssueNumber,
30
53
  listIssues,
54
+ listLocalBranches,
31
55
  listOpenPrs,
32
56
  loadAgentInstructions,
33
57
  loadConfig,
34
58
  logger,
59
+ pushBranch,
60
+ remoteBranchExists,
35
61
  replyToReviewComment,
36
62
  resolveProvider,
37
63
  sanitizeSlug,
38
64
  setRuntimeProvider,
39
- setupLabelsCommand,
40
- sortByPriority,
41
- updateIssueLabels,
42
- withSpinner
43
- } from "./chunk-75KVN7ZC.js";
65
+ updateIssueLabels
66
+ } from "./chunk-P5MZOU4B.js";
44
67
 
45
68
  // src/index.ts
46
69
  import { Command } from "commander";
@@ -48,6 +71,7 @@ import { Command } from "commander";
48
71
  // src/commands/init.ts
49
72
  import { writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
50
73
  import { join as join2 } from "path";
74
+ import { execa } from "execa";
51
75
  import inquirer from "inquirer";
52
76
 
53
77
  // src/lib/progress.ts
@@ -86,6 +110,47 @@ Each entry documents: date, feature, decisions, files changed, tests, and concer
86
110
  }
87
111
 
88
112
  // src/commands/init.ts
113
+ var DEFAULT_GITIGNORE = `# Dependencies
114
+ node_modules/
115
+
116
+ # Build output
117
+ dist/
118
+
119
+ # Test coverage
120
+ coverage/
121
+
122
+ # IDE
123
+ .idea/
124
+ .vscode/
125
+ *.swp
126
+ *.swo
127
+
128
+ # OS
129
+ .DS_Store
130
+ Thumbs.db
131
+
132
+ # Logs
133
+ *.log
134
+ npm-debug.log*
135
+
136
+ # Environment
137
+ .env
138
+ .env.local
139
+ .env.*.local
140
+
141
+ # Temporary files
142
+ *.tmp
143
+ *.temp
144
+ .cache/
145
+ `;
146
+ async function hasCommits() {
147
+ try {
148
+ await execa("git", ["rev-parse", "HEAD"]);
149
+ return true;
150
+ } catch {
151
+ return false;
152
+ }
153
+ }
89
154
  var DEFAULT_AGENT_MD = `# AI Agent Instructions
90
155
 
91
156
  This file contains instructions for the AI when working on this repository.
@@ -142,12 +207,12 @@ All AI commits should include the Co-Authored-By trailer as specified in the tas
142
207
  - [E.g., "Always use async/await over callbacks"]
143
208
  `;
144
209
  async function initCommand(options) {
145
- logger.bold("Initializing gent workflow...");
210
+ logger.bold("Initializing gent workflow (optional \u2014 gent works with sensible defaults)...");
146
211
  logger.newline();
147
212
  const isGitRepo = await checkGitRepo();
148
213
  if (!isGitRepo) {
149
214
  logger.error("Not a git repository. Please run 'git init' first.");
150
- process.exit(1);
215
+ return;
151
216
  }
152
217
  const cwd = process.cwd();
153
218
  if (configExists(cwd) && !options.force) {
@@ -173,6 +238,13 @@ async function initCommand(options) {
173
238
  default: "claude"
174
239
  }
175
240
  ]);
241
+ const gitignorePath = join2(cwd, ".gitignore");
242
+ if (!existsSync2(gitignorePath)) {
243
+ writeFileSync2(gitignorePath, DEFAULT_GITIGNORE, "utf-8");
244
+ logger.success(`Created ${colors.file(".gitignore")}`);
245
+ } else {
246
+ logger.info(`${colors.file(".gitignore")} already exists, skipping`);
247
+ }
176
248
  const configPath = getConfigPath(cwd);
177
249
  writeFileSync2(configPath, generateDefaultConfig(provider), "utf-8");
178
250
  logger.success(`Created ${colors.file(".gent.yml")}`);
@@ -186,26 +258,72 @@ async function initCommand(options) {
186
258
  const config = loadConfig(cwd);
187
259
  initializeProgress(config, cwd);
188
260
  logger.success(`Created ${colors.file(config.progress.file)}`);
189
- logger.newline();
190
- logger.box(
191
- "Setup Complete",
192
- `Next steps:
261
+ if (!await hasCommits()) {
262
+ logger.newline();
263
+ logger.info("No commits found. Creating initial commit with gent config...");
264
+ await execa("git", ["add", ".gitignore", ".gent.yml", "AGENT.md", config.progress.file]);
265
+ await execa("git", ["commit", "-m", "chore: initialize gent workflow"]);
266
+ logger.success("Created initial commit");
267
+ }
268
+ const repoInfo = await getRepoInfo();
269
+ if (!repoInfo) {
270
+ logger.newline();
271
+ logger.box(
272
+ "Setup Complete",
273
+ `Next steps:
274
+ 1. Edit ${colors.file("AGENT.md")} with your project-specific instructions
275
+ 2. Edit ${colors.file(".gent.yml")} to customize settings
276
+ 3. Create a GitHub remote: ${colors.command("gent github-remote")}
277
+ 4. Run ${colors.command("gent setup-labels")} to create GitHub labels`
278
+ );
279
+ const { createRemote } = await inquirer.prompt([
280
+ {
281
+ type: "confirm",
282
+ name: "createRemote",
283
+ message: "No GitHub remote found. Would you like to create one now?",
284
+ default: true
285
+ }
286
+ ]);
287
+ if (createRemote) {
288
+ const { githubRemoteCommand: githubRemoteCommand2 } = await import("./github-remote-G6UKRDUB.js");
289
+ const success = await githubRemoteCommand2();
290
+ if (success) {
291
+ const { setupLabels } = await inquirer.prompt([
292
+ {
293
+ type: "confirm",
294
+ name: "setupLabels",
295
+ message: "Would you like to setup GitHub labels now?",
296
+ default: true
297
+ }
298
+ ]);
299
+ if (setupLabels) {
300
+ const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-FZEN5TKM.js");
301
+ await setupLabelsCommand2();
302
+ }
303
+ }
304
+ }
305
+ } else {
306
+ logger.newline();
307
+ logger.box(
308
+ "Setup Complete",
309
+ `Next steps:
193
310
  1. Edit ${colors.file("AGENT.md")} with your project-specific instructions
194
311
  2. Edit ${colors.file(".gent.yml")} to customize settings
195
312
  3. Run ${colors.command("gent setup-labels")} to create GitHub labels
196
313
  4. Run ${colors.command("gent create <description>")} to create your first ticket`
197
- );
198
- const { setupLabels } = await inquirer.prompt([
199
- {
200
- type: "confirm",
201
- name: "setupLabels",
202
- message: "Would you like to setup GitHub labels now?",
203
- default: true
314
+ );
315
+ const { setupLabels } = await inquirer.prompt([
316
+ {
317
+ type: "confirm",
318
+ name: "setupLabels",
319
+ message: "Would you like to setup GitHub labels now?",
320
+ default: true
321
+ }
322
+ ]);
323
+ if (setupLabels) {
324
+ const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-FZEN5TKM.js");
325
+ await setupLabelsCommand2();
204
326
  }
205
- ]);
206
- if (setupLabels) {
207
- const { setupLabelsCommand: setupLabelsCommand2 } = await import("./setup-labels-STWAFV2E.js");
208
- await setupLabelsCommand2();
209
327
  }
210
328
  }
211
329
 
@@ -215,7 +333,7 @@ import chalk from "chalk";
215
333
 
216
334
  // src/lib/ai-provider.ts
217
335
  import { spawn } from "child_process";
218
- import { execa } from "execa";
336
+ import { execa as execa2 } from "execa";
219
337
  async function getOtherAvailableProviders(currentProvider) {
220
338
  const allProviders = ["claude", "gemini", "codex"];
221
339
  const others = allProviders.filter((p) => p !== currentProvider);
@@ -266,20 +384,20 @@ async function invokeAIInteractive(prompt, config, providerOverride) {
266
384
  case "claude": {
267
385
  const args = ["--permission-mode", config.claude.permission_mode, prompt];
268
386
  return {
269
- result: execa("claude", args, { stdio: "inherit" }),
387
+ result: execa2("claude", args, { stdio: "inherit" }),
270
388
  provider
271
389
  };
272
390
  }
273
391
  case "gemini": {
274
392
  return {
275
- result: execa("gemini", ["-i", prompt], { stdio: "inherit" }),
393
+ result: execa2("gemini", ["-i", prompt], { stdio: "inherit" }),
276
394
  provider
277
395
  };
278
396
  }
279
397
  case "codex": {
280
398
  const args = prompt ? [prompt] : [];
281
399
  return {
282
- result: execa("codex", args, { stdio: "inherit" }),
400
+ result: execa2("codex", args, { stdio: "inherit" }),
283
401
  provider
284
402
  };
285
403
  }
@@ -325,7 +443,7 @@ async function invokeClaudeInternal(options) {
325
443
  }
326
444
  args.push(options.prompt);
327
445
  if (options.printOutput) {
328
- const subprocess = execa("claude", args, {
446
+ const subprocess = execa2("claude", args, {
329
447
  stdio: "inherit"
330
448
  });
331
449
  await subprocess;
@@ -361,7 +479,7 @@ async function invokeClaudeInternal(options) {
361
479
  child.on("error", reject);
362
480
  });
363
481
  } else {
364
- const { stdout } = await execa("claude", args);
482
+ const { stdout } = await execa2("claude", args);
365
483
  return stdout;
366
484
  }
367
485
  }
@@ -369,7 +487,7 @@ async function invokeGeminiInternal(options) {
369
487
  const args = [];
370
488
  args.push(options.prompt);
371
489
  if (options.printOutput) {
372
- const subprocess = execa("gemini", args, {
490
+ const subprocess = execa2("gemini", args, {
373
491
  stdio: "inherit"
374
492
  });
375
493
  await subprocess;
@@ -405,14 +523,14 @@ async function invokeGeminiInternal(options) {
405
523
  child.on("error", reject);
406
524
  });
407
525
  } else {
408
- const { stdout } = await execa("gemini", args);
526
+ const { stdout } = await execa2("gemini", args);
409
527
  return stdout;
410
528
  }
411
529
  }
412
530
  async function invokeCodexInternal(options) {
413
531
  const args = ["exec", options.prompt];
414
532
  if (options.printOutput) {
415
- const subprocess = execa("codex", args, {
533
+ const subprocess = execa2("codex", args, {
416
534
  stdio: "inherit"
417
535
  });
418
536
  await subprocess;
@@ -448,7 +566,7 @@ async function invokeCodexInternal(options) {
448
566
  child.on("error", reject);
449
567
  });
450
568
  } else {
451
- const { stdout } = await execa("codex", args);
569
+ const { stdout } = await execa2("codex", args);
452
570
  return stdout;
453
571
  }
454
572
  }
@@ -842,158 +960,6 @@ Next steps:
842
960
  import chalk2 from "chalk";
843
961
  import inquirer3 from "inquirer";
844
962
 
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
963
  // src/lib/branch.ts
998
964
  async function generateBranchName(config, issueNumber, issueTitle, type) {
999
965
  const author = await resolveAuthor(config);
@@ -2240,7 +2206,7 @@ import { homedir } from "os";
2240
2206
  // package.json
2241
2207
  var package_default = {
2242
2208
  name: "@rotorsoft/gent",
2243
- version: "1.22.0",
2209
+ version: "1.24.0",
2244
2210
  description: "AI-powered GitHub workflow CLI - leverage AI (Claude, Gemini, or Codex) to create tickets, implement features, and manage PRs",
2245
2211
  keywords: [
2246
2212
  "cli",
@@ -2662,7 +2628,7 @@ async function statusCommand() {
2662
2628
  }
2663
2629
 
2664
2630
  // src/commands/tui.ts
2665
- import { execa as execa5 } from "execa";
2631
+ import { execa as execa4 } from "execa";
2666
2632
 
2667
2633
  // src/tui/state.ts
2668
2634
  var envCache = null;
@@ -2730,7 +2696,7 @@ async function aggregateState() {
2730
2696
  getUnpushedCommits()
2731
2697
  ]);
2732
2698
  const hasRemote = repoInfo !== null;
2733
- if (hasConfig && hasRemote && envCache.hasLabels === null) {
2699
+ if (hasRemote && envCache.hasLabels === null) {
2734
2700
  envCache.hasLabels = await checkLabelsExist().catch(() => false);
2735
2701
  }
2736
2702
  const hasLabels = envCache.hasLabels ?? false;
@@ -2814,14 +2780,14 @@ function getAvailableActions(state) {
2814
2780
  actions.push({ id: "quit", label: "quit", shortcut: "q" });
2815
2781
  return actions;
2816
2782
  }
2817
- const needsInit = !state.hasConfig;
2818
- const needsLabels = state.hasConfig && state.hasValidRemote && !state.hasLabels;
2819
- if (needsInit) {
2820
- actions.push({ id: "init", label: "init", shortcut: "i" });
2821
- } else if (needsLabels) {
2783
+ const needsLabels = state.hasValidRemote && !state.hasLabels;
2784
+ if (needsLabels) {
2822
2785
  actions.push({ id: "setup-labels", label: "setup-labels", shortcut: "b" });
2823
2786
  }
2824
- const isSetUp = state.hasConfig && (!state.hasValidRemote || state.hasLabels);
2787
+ if (!state.hasConfig) {
2788
+ actions.push({ id: "init", label: "init", shortcut: "i" });
2789
+ }
2790
+ const isSetUp = !state.hasValidRemote || state.hasLabels;
2825
2791
  if (isSetUp && state.hasValidRemote) {
2826
2792
  actions.push({ id: "create", label: "new", shortcut: "n" });
2827
2793
  }
@@ -3138,24 +3104,17 @@ function buildDashboardLines(state, actions, hint, refreshing, versionCheck) {
3138
3104
  out(row(chalk3.dim(" No commits"), w));
3139
3105
  }
3140
3106
  }
3141
- if (!state.hasConfig) {
3107
+ if (!state.hasValidRemote) {
3142
3108
  section("Setup");
3143
- out(row(chalk3.yellow('Run "gent init" to set up this repository'), w));
3144
- out(row(chalk3.dim("Press [i] to initialize"), w));
3145
- } else if (state.hasValidRemote && !state.hasLabels) {
3109
+ out(row(chalk3.yellow("Step 1: Create a GitHub repository"), w));
3110
+ out(row(chalk3.dim("Press [g] to create a GitHub repo and push"), w));
3111
+ } else if (!state.hasLabels) {
3146
3112
  section("Setup");
3147
- out(row(chalk3.yellow('Run "gent setup-labels" to create required GitHub labels'), w));
3113
+ out(row(chalk3.yellow("Step 2: Create workflow labels"), w));
3148
3114
  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
- );
3115
+ } else if (!state.hasConfig) {
3116
+ section("Tip");
3117
+ out(row(chalk3.dim("Press [i] to customize configuration (optional)"), w));
3159
3118
  } else if (hint) {
3160
3119
  section("Hint");
3161
3120
  out(row(chalk3.yellow(hint), w));
@@ -3669,38 +3628,6 @@ function showStatus(title, message, dashboardLines) {
3669
3628
  renderOverlay(dashboardLines, lines, w);
3670
3629
  }
3671
3630
 
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
3631
  // src/commands/tui.ts
3705
3632
  var CANCEL = /* @__PURE__ */ Symbol("cancel");
3706
3633
  async function waitForKey(validKeys) {
@@ -3812,19 +3739,19 @@ async function executeAction(actionId, state, dashboardLines) {
3812
3739
  }
3813
3740
  async function handleCommit(state, dashboardLines) {
3814
3741
  try {
3815
- const { stdout: status } = await execa5("git", ["status", "--short"]);
3742
+ const { stdout: status } = await execa4("git", ["status", "--short"]);
3816
3743
  if (!status.trim()) {
3817
3744
  showStatus("Commit", "No changes to commit", dashboardLines);
3818
3745
  await new Promise((r) => setTimeout(r, 1500));
3819
3746
  return false;
3820
3747
  }
3821
- await execa5("git", ["add", "-A"]);
3822
- const { stdout: diffStat } = await execa5("git", [
3748
+ await execa4("git", ["add", "-A"]);
3749
+ const { stdout: diffStat } = await execa4("git", [
3823
3750
  "diff",
3824
3751
  "--cached",
3825
3752
  "--stat"
3826
3753
  ]);
3827
- const { stdout: diffPatch } = await execa5("git", ["diff", "--cached"]);
3754
+ const { stdout: diffPatch } = await execa4("git", ["diff", "--cached"]);
3828
3755
  const diffContent = (diffStat + "\n\n" + diffPatch).slice(0, 4e3);
3829
3756
  const issueNumber = state.issue?.number ?? null;
3830
3757
  const issueTitle = state.issue?.title ?? null;
@@ -3839,7 +3766,7 @@ async function handleCommit(state, dashboardLines) {
3839
3766
  dashboardLines
3840
3767
  });
3841
3768
  if (!mode) {
3842
- await execa5("git", ["reset", "HEAD"]);
3769
+ await execa4("git", ["reset", "HEAD"]);
3843
3770
  return false;
3844
3771
  }
3845
3772
  let message;
@@ -3861,7 +3788,7 @@ async function handleCommit(state, dashboardLines) {
3861
3788
  );
3862
3789
  }
3863
3790
  if (message === CANCEL) {
3864
- await execa5("git", ["reset", "HEAD"]);
3791
+ await execa4("git", ["reset", "HEAD"]);
3865
3792
  return false;
3866
3793
  }
3867
3794
  const confirmed = await showConfirm({
@@ -3870,7 +3797,7 @@ async function handleCommit(state, dashboardLines) {
3870
3797
  dashboardLines
3871
3798
  });
3872
3799
  if (!confirmed) {
3873
- await execa5("git", ["reset", "HEAD"]);
3800
+ await execa4("git", ["reset", "HEAD"]);
3874
3801
  return false;
3875
3802
  }
3876
3803
  const providerEmail = getProviderEmail(provider);
@@ -3878,7 +3805,7 @@ async function handleCommit(state, dashboardLines) {
3878
3805
 
3879
3806
  Co-Authored-By: ${providerName} <${providerEmail}>`;
3880
3807
  showStatus("Committing", "Committing changes...", dashboardLines);
3881
- await execa5("git", ["commit", "-m", fullMessage]);
3808
+ await execa4("git", ["commit", "-m", fullMessage]);
3882
3809
  return true;
3883
3810
  } catch (error) {
3884
3811
  logger.error(`Commit failed: ${error}`);
@@ -3958,10 +3885,10 @@ ${feedbackLines}`);
3958
3885
  }
3959
3886
  async function handlePush(dashboardLines) {
3960
3887
  try {
3961
- const { stdout: branch } = await execa5("git", ["branch", "--show-current"]);
3888
+ const { stdout: branch } = await execa4("git", ["branch", "--show-current"]);
3962
3889
  const branchName = branch.trim();
3963
3890
  showStatus("Pushing", `Pushing ${branchName} to remote...`, dashboardLines);
3964
- await execa5("git", ["push", "-u", "origin", branchName]);
3891
+ await execa4("git", ["push", "-u", "origin", branchName]);
3965
3892
  } catch (error) {
3966
3893
  logger.error(`Push failed: ${error}`);
3967
3894
  }
@@ -4219,8 +4146,9 @@ function startVersionCheck() {
4219
4146
  });
4220
4147
  }
4221
4148
  async function checkPrerequisites() {
4222
- if (!checkInitialized()) {
4223
- logger.warning('Repository not initialized. Run "gent init" to set up this repository.');
4149
+ const repoInfo = await getRepoInfo();
4150
+ if (!repoInfo) {
4151
+ logger.warning('No GitHub remote found. Run "gent github-remote" to create one.');
4224
4152
  return false;
4225
4153
  }
4226
4154
  const labelsOk = await checkLabelsExist();
@@ -4238,7 +4166,7 @@ program.name("gent").description(
4238
4166
  startVersionCheck();
4239
4167
  }
4240
4168
  });
4241
- program.command("init").description("Initialize gent workflow in current repository").option("-f, --force", "Overwrite existing configuration").action(async (options) => {
4169
+ program.command("init").description("Initialize gent workflow in current repository (optional \u2014 for customization)").option("-f, --force", "Overwrite existing configuration").action(async (options) => {
4242
4170
  await initCommand(options);
4243
4171
  });
4244
4172
  program.command("setup-labels").description("Setup GitHub labels for AI workflow").action(async () => {