claude-teammate 0.1.13 → 0.1.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-teammate",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "CLI bootstrapper for Claude Teammate.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -32,6 +32,7 @@ import {
32
32
  listMissingRepoPaths,
33
33
  parseGitHubRepoUrl,
34
34
  prepareRepoForBranch,
35
+ RepoCheckoutNotReadyError,
35
36
  validateOrEnsureLocalRepo
36
37
  } from "../repo.js";
37
38
  import {
@@ -206,7 +207,18 @@ export async function runWorkerCommand({ projectRoot }) {
206
207
  let trackedIssueCount = 0;
207
208
 
208
209
  for (const repo of repos) {
209
- repo.local_path = await validateOrEnsureLocalRepo(repo.url, runtimePaths.reposDir, repo.local_path);
210
+ try {
211
+ repo.local_path = await validateOrEnsureLocalRepo(repo.url, runtimePaths.reposDir, repo.local_path);
212
+ } catch (error) {
213
+ if (isRepoCheckoutNotReadyError(error)) {
214
+ await logger.info("Skipping GitHub repo until checkout is ready", {
215
+ repo: repo.url,
216
+ localPath: error.localPath || repo.local_path
217
+ });
218
+ continue;
219
+ }
220
+ throw error;
221
+ }
210
222
  const issues = await github.listIssues(repo.url);
211
223
  const botIssues = issues.filter((issue) => isGitHubBotAuthor(issue.author, githubBotUser));
212
224
  trackedIssueCount += botIssues.length;
@@ -258,7 +270,18 @@ export async function runWorkerCommand({ projectRoot }) {
258
270
  let trackedPrCount = 0;
259
271
 
260
272
  for (const repo of repos) {
261
- repo.local_path = await validateOrEnsureLocalRepo(repo.url, runtimePaths.reposDir, repo.local_path);
273
+ try {
274
+ repo.local_path = await validateOrEnsureLocalRepo(repo.url, runtimePaths.reposDir, repo.local_path);
275
+ } catch (error) {
276
+ if (isRepoCheckoutNotReadyError(error)) {
277
+ await logger.info("Skipping draft PR repo until checkout is ready", {
278
+ repo: repo.url,
279
+ localPath: error.localPath || repo.local_path
280
+ });
281
+ continue;
282
+ }
283
+ throw error;
284
+ }
262
285
 
263
286
  const pullRequests = await github.listPullRequests(repo.url);
264
287
  const botPullRequests = pullRequests.filter(
@@ -403,7 +426,22 @@ async function processJiraIssue({ issue, jira, github, botUser, config, projectR
403
426
 
404
427
  let repoCheckoutUpdated = false;
405
428
  for (const repo of epicMemory.repos) {
406
- const validatedLocalPath = await validateOrEnsureLocalRepo(repo.url, runtimePaths.reposDir, repo.local_path);
429
+ let validatedLocalPath;
430
+ try {
431
+ validatedLocalPath = await validateOrEnsureLocalRepo(repo.url, runtimePaths.reposDir, repo.local_path);
432
+ } catch (error) {
433
+ if (isRepoCheckoutNotReadyError(error)) {
434
+ issueMemory.last_error = `Repository checkout is not ready yet for ${repo.url}. The worker will retry automatically.`;
435
+ issueMemory = await saveIssueMemory(issueMemoryRecord.filePath, detail, issueMemory);
436
+ await logger.info("Skipping Jira issue until checkout is ready", {
437
+ issue: detail.key,
438
+ repo: repo.url,
439
+ localPath: error.localPath || repo.local_path
440
+ });
441
+ return buildIssueState(detail, epicMemory, issueMemory, null, issueMemory.last_error);
442
+ }
443
+ throw error;
444
+ }
407
445
  if (validatedLocalPath !== repo.local_path) {
408
446
  repo.local_path = validatedLocalPath;
409
447
  repoCheckoutUpdated = true;
@@ -2234,6 +2272,17 @@ function normalizeRepoAlias(value) {
2234
2272
  return String(value || "").trim().toLowerCase();
2235
2273
  }
2236
2274
 
2275
+ function isRepoCheckoutNotReadyError(error) {
2276
+ return Boolean(
2277
+ error &&
2278
+ typeof error === "object" &&
2279
+ (
2280
+ error instanceof RepoCheckoutNotReadyError ||
2281
+ error.code === "REPO_CHECKOUT_NOT_READY"
2282
+ )
2283
+ );
2284
+ }
2285
+
2237
2286
  function normalizeRepoRelativePath(value) {
2238
2287
  const normalized = String(value || "").trim().replace(/^\/+/u, "").replace(/\/+$/u, "");
2239
2288
  if (!normalized || normalized.startsWith("http://") || normalized.startsWith("https://")) {
package/src/repo.js CHANGED
@@ -1,4 +1,4 @@
1
- import { access, mkdir } from "node:fs/promises";
1
+ import { access, mkdir, readdir } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { execFile } from "node:child_process";
4
4
  import process from "node:process";
@@ -6,6 +6,16 @@ import { promisify } from "node:util";
6
6
 
7
7
  const execFileAsync = promisify(execFile);
8
8
 
9
+ export class RepoCheckoutNotReadyError extends Error {
10
+ constructor(message, details = {}) {
11
+ super(message);
12
+ this.name = "RepoCheckoutNotReadyError";
13
+ this.code = "REPO_CHECKOUT_NOT_READY";
14
+ this.repoUrl = details.repoUrl || "";
15
+ this.localPath = details.localPath || "";
16
+ }
17
+ }
18
+
9
19
  export async function ensureLocalRepo(repoUrl, reposDir) {
10
20
  const repo = parseGitHubRepoUrl(repoUrl);
11
21
  const checkoutPath = path.join(reposDir, repo.owner, repo.name);
@@ -28,6 +38,19 @@ export async function validateOrEnsureLocalRepo(repoUrl, reposDir, localPath = "
28
38
  return candidatePath;
29
39
  }
30
40
 
41
+ if (candidatePath && await pathExists(candidatePath)) {
42
+ const entries = await safeReadDir(candidatePath);
43
+ if (entries.length > 0) {
44
+ throw new RepoCheckoutNotReadyError(
45
+ `Repository checkout is not ready yet at ${candidatePath}.`,
46
+ {
47
+ repoUrl,
48
+ localPath: candidatePath
49
+ }
50
+ );
51
+ }
52
+ }
53
+
31
54
  return ensureLocalRepo(repoUrl, reposDir);
32
55
  }
33
56
 
@@ -114,6 +137,14 @@ async function pathExists(targetPath) {
114
137
  }
115
138
  }
116
139
 
140
+ async function safeReadDir(targetPath) {
141
+ try {
142
+ return await readdir(targetPath);
143
+ } catch {
144
+ return [];
145
+ }
146
+ }
147
+
117
148
  async function isUsableGitCheckout(repoPath) {
118
149
  if (!repoPath) {
119
150
  return false;