claude-teammate 0.1.113 → 0.1.115
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 +1 -1
- package/src/commands/worker.js +61 -19
- package/src/jira.js +1 -0
package/package.json
CHANGED
package/src/commands/worker.js
CHANGED
|
@@ -68,6 +68,10 @@ function markClaudeUsageLimit() {
|
|
|
68
68
|
function isClaudeUsageLimited() {
|
|
69
69
|
return Date.now() < claudeUsageLimitUntil;
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
function isSubtask(issueType) {
|
|
73
|
+
return ["subtask", "sub-task"].includes(issueType.toLowerCase());
|
|
74
|
+
}
|
|
71
75
|
const REPO_REQUEST_COMMENT = "Please provide repo url";
|
|
72
76
|
const EPIC_MEMORY_MAX_CHARS = 3_500;
|
|
73
77
|
const ISSUE_SETTLE_TIME_MS = 2 * 60 * 1000;
|
|
@@ -642,6 +646,18 @@ async function processJiraIssue({ issue, jira, forgeRegistry, botUser, config, p
|
|
|
642
646
|
|
|
643
647
|
const issueMemoryRecord = await loadIssueMemory(projectRoot, config.JIRA_BASE_URL, detail);
|
|
644
648
|
|
|
649
|
+
if (!detail.epicId && !detail.epicKey) {
|
|
650
|
+
if (isSubtask(detail.issueType) && detail.parentKey) {
|
|
651
|
+
await logger.info("Subtask has no direct epic, checking parent for epic", { issue: detail.key, parent: detail.parentKey });
|
|
652
|
+
const parentDetail = await jira.fetchIssueDetails(detail.parentKey);
|
|
653
|
+
if (parentDetail.epicId || parentDetail.epicKey) {
|
|
654
|
+
detail.epicId = parentDetail.epicId;
|
|
655
|
+
detail.epicKey = parentDetail.epicKey;
|
|
656
|
+
detail.epicUrl = parentDetail.epicUrl;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
645
661
|
if (!detail.epicId && !detail.epicKey) {
|
|
646
662
|
await logger.info("Issue has no epic, notifying user", { issue: detail.key });
|
|
647
663
|
await ensureJiraComment(detail, jira, botUser, "An epic is missing from this ticket, please add one and tell me when you are done.");
|
|
@@ -661,6 +677,11 @@ async function processJiraIssue({ issue, jira, forgeRegistry, botUser, config, p
|
|
|
661
677
|
);
|
|
662
678
|
};
|
|
663
679
|
|
|
680
|
+
const TERMINAL_WORKFLOW_STATES = new Set(["completed", "failed", "skipped_no_epic"]);
|
|
681
|
+
const wasReopened =
|
|
682
|
+
TERMINAL_WORKFLOW_STATES.has(issueMemory.workflow_state) &&
|
|
683
|
+
(isToDoStatus(detail.status) || isInProgressStatus(detail.status));
|
|
684
|
+
|
|
664
685
|
try {
|
|
665
686
|
issueMemory.last_error = "";
|
|
666
687
|
|
|
@@ -674,38 +695,59 @@ async function processJiraIssue({ issue, jira, forgeRegistry, botUser, config, p
|
|
|
674
695
|
|
|
675
696
|
if (transition.transitioned) {
|
|
676
697
|
detail.status = "In Progress";
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
// Jira transition failed previously; just retry without re-running the task.
|
|
680
|
-
issueMemory.workflow_state = issueMemory.no_code_result === "completed" ? "completed" : "failed";
|
|
681
|
-
} else {
|
|
682
|
-
// Fresh pickup or re-run: clear previous-run fields so task executes from scratch.
|
|
698
|
+
if (wasReopened) {
|
|
699
|
+
// Issue was re-opened from a terminal state; clear all task run state and restart from scratch.
|
|
683
700
|
issueMemory.workflow_state = "in_progress";
|
|
684
701
|
issueMemory.code_change_verdict = null;
|
|
685
702
|
issueMemory.no_code_result = null;
|
|
686
703
|
issueMemory.no_code_summary = null;
|
|
687
704
|
issueMemory.progress_comment_id = null;
|
|
688
|
-
await
|
|
705
|
+
issueMemory = await saveIssueMemory(issueMemoryRecord.filePath, detail, issueMemory);
|
|
706
|
+
await ensureJiraComment(detail, jira, botUser, "Issue re-opened. Clearing previous run state and restarting from scratch.");
|
|
707
|
+
await saveProgress("Task restarted from scratch. Setting up workspace...");
|
|
708
|
+
} else {
|
|
709
|
+
const taskAlreadyProducedResult = issueMemory.no_code_result === "completed" || issueMemory.no_code_result === "failed";
|
|
710
|
+
if (taskAlreadyProducedResult) {
|
|
711
|
+
// Jira transition failed previously; just retry without re-running the task.
|
|
712
|
+
issueMemory.workflow_state = issueMemory.no_code_result === "completed" ? "completed" : "failed";
|
|
713
|
+
} else {
|
|
714
|
+
// Fresh pickup or re-run: clear previous-run fields so task executes from scratch.
|
|
715
|
+
issueMemory.workflow_state = "in_progress";
|
|
716
|
+
issueMemory.code_change_verdict = null;
|
|
717
|
+
issueMemory.no_code_result = null;
|
|
718
|
+
issueMemory.no_code_summary = null;
|
|
719
|
+
issueMemory.progress_comment_id = null;
|
|
720
|
+
await saveProgress("Task picked up. Setting up workspace...");
|
|
721
|
+
}
|
|
722
|
+
issueMemory = await saveIssueMemory(issueMemoryRecord.filePath, detail, issueMemory);
|
|
689
723
|
}
|
|
690
|
-
issueMemory = await saveIssueMemory(issueMemoryRecord.filePath, detail, issueMemory);
|
|
691
724
|
}
|
|
692
725
|
}
|
|
693
726
|
|
|
694
|
-
const TERMINAL_WORKFLOW_STATES = new Set(["completed", "failed", "skipped_no_epic"]);
|
|
695
727
|
if (isInProgressStatus(detail.status) && TERMINAL_WORKFLOW_STATES.has(issueMemory.workflow_state)) {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
// the transition on this poll.
|
|
699
|
-
const taskAlreadyProducedResult = issueMemory.no_code_result === "completed" || issueMemory.no_code_result === "failed";
|
|
700
|
-
if (!taskAlreadyProducedResult) {
|
|
728
|
+
if (wasReopened) {
|
|
729
|
+
// Issue was re-opened from terminal state directly to In Progress; resume from prior context.
|
|
701
730
|
issueMemory.workflow_state = "in_progress";
|
|
702
|
-
// Clear previous-run fields so clarification and code-change decision re-run with the new user comment.
|
|
703
|
-
issueMemory.code_change_verdict = null;
|
|
704
731
|
issueMemory.no_code_result = null;
|
|
705
|
-
issueMemory.
|
|
732
|
+
issueMemory.code_change_verdict = null;
|
|
706
733
|
issueMemory.progress_comment_id = null;
|
|
707
734
|
issueMemory = await saveIssueMemory(issueMemoryRecord.filePath, detail, issueMemory);
|
|
708
|
-
await
|
|
735
|
+
await ensureJiraComment(detail, jira, botUser, "Issue re-opened. Resuming previous session.");
|
|
736
|
+
} else {
|
|
737
|
+
// If the task already produced a successful result, the Jira transition to "In Review" likely
|
|
738
|
+
// failed (no transition available). Don't re-run the task; just let the no_code path retry
|
|
739
|
+
// the transition on this poll.
|
|
740
|
+
const taskAlreadyProducedResult = issueMemory.no_code_result === "completed" || issueMemory.no_code_result === "failed";
|
|
741
|
+
if (!taskAlreadyProducedResult) {
|
|
742
|
+
issueMemory.workflow_state = "in_progress";
|
|
743
|
+
// Clear previous-run fields so clarification and code-change decision re-run with the new user comment.
|
|
744
|
+
issueMemory.code_change_verdict = null;
|
|
745
|
+
issueMemory.no_code_result = null;
|
|
746
|
+
issueMemory.no_code_summary = null;
|
|
747
|
+
issueMemory.progress_comment_id = null;
|
|
748
|
+
issueMemory = await saveIssueMemory(issueMemoryRecord.filePath, detail, issueMemory);
|
|
749
|
+
await saveProgress("Task restarted. Setting up workspace...");
|
|
750
|
+
}
|
|
709
751
|
}
|
|
710
752
|
}
|
|
711
753
|
|
|
@@ -1020,7 +1062,7 @@ async function processJiraIssue({ issue, jira, forgeRegistry, botUser, config, p
|
|
|
1020
1062
|
? `Completed: ${issueMemory.no_code_summary}`
|
|
1021
1063
|
: `Failed: ${issueMemory.no_code_summary}`;
|
|
1022
1064
|
await ensureJiraComment(detail, jira, botUser, resultComment);
|
|
1023
|
-
if (issueMemory.no_code_result === "completed") {
|
|
1065
|
+
if (issueMemory.no_code_result === "completed" && !wasReopened) {
|
|
1024
1066
|
let transition = await jira.transitionIssueToStatus(detail.key, "In Review");
|
|
1025
1067
|
if (!transition.transitioned) {
|
|
1026
1068
|
transition = await jira.transitionIssueToStatus(detail.key, "Done");
|
package/src/jira.js
CHANGED
|
@@ -204,6 +204,7 @@ function mapIssue(issue, baseUrl) {
|
|
|
204
204
|
epicId: epic?.id ?? null,
|
|
205
205
|
epicKey: epic?.key ?? null,
|
|
206
206
|
epicUrl: epic?.url ?? null,
|
|
207
|
+
parentKey: issue.fields?.parent?.key ?? null,
|
|
207
208
|
created: issue.fields?.created ?? null,
|
|
208
209
|
updated: issue.fields?.updated ?? null
|
|
209
210
|
};
|