claude-issue-solver 1.8.2 → 1.9.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.
@@ -17,7 +17,19 @@ async function selectCommand() {
17
17
  console.log(chalk_1.default.yellow('No open issues found.'));
18
18
  return;
19
19
  }
20
- const choices = issues.map((issue) => ({
20
+ // Filter out issues that already have open PRs
21
+ const issuesWithPRs = (0, github_1.getIssuesWithOpenPRs)();
22
+ const availableIssues = issues.filter((issue) => !issuesWithPRs.has(issue.number));
23
+ if (availableIssues.length === 0) {
24
+ console.log(chalk_1.default.yellow('All open issues already have PRs in progress.'));
25
+ console.log(chalk_1.default.dim('Use `claude-issue go` to navigate to existing worktrees.'));
26
+ return;
27
+ }
28
+ if (issuesWithPRs.size > 0) {
29
+ const skipped = issues.length - availableIssues.length;
30
+ console.log(chalk_1.default.dim(`(${skipped} issue${skipped > 1 ? 's' : ''} with open PRs hidden)\n`));
31
+ }
32
+ const choices = availableIssues.map((issue) => ({
21
33
  name: `#${issue.number}\t${issue.title}`,
22
34
  value: issue.number,
23
35
  }));
@@ -20,3 +20,8 @@ export interface PRStatus {
20
20
  }
21
21
  export declare function getIssueStatus(issueNumber: number): IssueStatus | null;
22
22
  export declare function getPRForBranch(branch: string): PRStatus | null;
23
+ /**
24
+ * Get all open PRs with their head branch names (single API call)
25
+ * Returns a Set of issue numbers that have open PRs from issue-{number}-* branches
26
+ */
27
+ export declare function getIssuesWithOpenPRs(): Set<number>;
@@ -5,6 +5,7 @@ exports.listIssues = listIssues;
5
5
  exports.createPullRequest = createPullRequest;
6
6
  exports.getIssueStatus = getIssueStatus;
7
7
  exports.getPRForBranch = getPRForBranch;
8
+ exports.getIssuesWithOpenPRs = getIssuesWithOpenPRs;
8
9
  const child_process_1 = require("child_process");
9
10
  function getIssue(issueNumber) {
10
11
  try {
@@ -61,3 +62,25 @@ function getPRForBranch(branch) {
61
62
  return null;
62
63
  }
63
64
  }
65
+ /**
66
+ * Get all open PRs with their head branch names (single API call)
67
+ * Returns a Set of issue numbers that have open PRs from issue-{number}-* branches
68
+ */
69
+ function getIssuesWithOpenPRs() {
70
+ try {
71
+ const output = (0, child_process_1.execSync)(`gh pr list --state open --json headRefName --limit 100`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
72
+ const data = JSON.parse(output);
73
+ const issueNumbers = new Set();
74
+ for (const pr of data) {
75
+ // Match branches like "issue-42-fix-bug"
76
+ const match = pr.headRefName.match(/^issue-(\d+)-/);
77
+ if (match) {
78
+ issueNumbers.add(parseInt(match[1], 10));
79
+ }
80
+ }
81
+ return issueNumbers;
82
+ }
83
+ catch {
84
+ return new Set();
85
+ }
86
+ }
@@ -136,12 +136,45 @@ function openInNewTerminal(script) {
136
136
  console.log(script);
137
137
  }
138
138
  }
139
+ /**
140
+ * Recursively find all .env* files in a directory
141
+ */
142
+ function findEnvFiles(dir, baseDir, results = []) {
143
+ const skipDirs = ['node_modules', '.git', 'dist', 'build', '.next', '.turbo'];
144
+ try {
145
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
146
+ for (const entry of entries) {
147
+ const fullPath = path.join(dir, entry.name);
148
+ if (entry.isDirectory()) {
149
+ if (!skipDirs.includes(entry.name)) {
150
+ findEnvFiles(fullPath, baseDir, results);
151
+ }
152
+ }
153
+ else if (entry.isFile() && entry.name.startsWith('.env')) {
154
+ // Store relative path from base directory
155
+ const relativePath = path.relative(baseDir, fullPath);
156
+ results.push(relativePath);
157
+ }
158
+ }
159
+ }
160
+ catch {
161
+ // Ignore permission errors or inaccessible directories
162
+ }
163
+ return results;
164
+ }
139
165
  function copyEnvFiles(from, to) {
140
- const envFiles = ['.env', '.env.local'];
141
- for (const file of envFiles) {
142
- const src = path.join(from, file);
143
- const dest = path.join(to, file);
144
- if (fs.existsSync(src) && !fs.existsSync(dest)) {
166
+ // Find all .env* files recursively
167
+ const envFiles = findEnvFiles(from, from);
168
+ for (const relativePath of envFiles) {
169
+ const src = path.join(from, relativePath);
170
+ const dest = path.join(to, relativePath);
171
+ // Create parent directory if it doesn't exist
172
+ const destDir = path.dirname(dest);
173
+ if (!fs.existsSync(destDir)) {
174
+ fs.mkdirSync(destDir, { recursive: true });
175
+ }
176
+ // Only copy if destination doesn't exist
177
+ if (!fs.existsSync(dest)) {
145
178
  fs.copyFileSync(src, dest);
146
179
  }
147
180
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-issue-solver",
3
- "version": "1.8.2",
3
+ "version": "1.9.0",
4
4
  "description": "Automatically solve GitHub issues using Claude Code",
5
5
  "main": "dist/index.js",
6
6
  "bin": {