@nyxa/nyx-agent 0.8.1 → 0.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.
- package/README.md +3 -3
- package/dist/runtime/runPipeline.js +14 -8
- package/dist/runtime/selectionConfirmation.js +57 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,9 +7,9 @@ GitHub issue at a time, each phase with fresh context.
|
|
|
7
7
|
|
|
8
8
|
For every run NyxAgent:
|
|
9
9
|
|
|
10
|
-
1. **Selects** executable open GitHub issues to work on (read-only),
|
|
11
|
-
GitHub sub-issues
|
|
12
|
-
|
|
10
|
+
1. **Selects** executable open GitHub issues to work on (read-only), presenting
|
|
11
|
+
GitHub sub-issues in parent PRD/plan sections, then asks the user to confirm
|
|
12
|
+
the proposed checklist with the same section order.
|
|
13
13
|
2. For each selected issue, in an isolated git **worktree**:
|
|
14
14
|
- **implements** it (the agent — the only customizable prompt),
|
|
15
15
|
- optionally **reviews** it in bounded discovery rounds, then revises only
|
|
@@ -11,7 +11,7 @@ import { createRunReporter } from "./reporter.js";
|
|
|
11
11
|
import { runAgentPhase, } from "./runPhase.js";
|
|
12
12
|
import { REVIEW_CHALLENGE_SCHEMA, REVIEW_DISCOVERY_SCHEMA, GLOBAL_REVIEW_SCHEMA, REVIEW_VALIDATION_SCHEMA, SELECTION_SCHEMA, } from "./schemas.js";
|
|
13
13
|
import { commitAll, commitsAhead, createPullRequest, pushBranch, rangeDiff, stageAllAndDiff, } from "./scm.js";
|
|
14
|
-
import { confirmWorkItemSelection, } from "./selectionConfirmation.js";
|
|
14
|
+
import { buildSelectionSections, confirmWorkItemSelection, } from "./selectionConfirmation.js";
|
|
15
15
|
import { createRunId } from "./time.js";
|
|
16
16
|
import { filterAvailable, listGitHubWorkItemInventory, resolveSelectedQueue, } from "./workItems.js";
|
|
17
17
|
const MAX_CANDIDATES = 50;
|
|
@@ -285,13 +285,9 @@ async function runSelection(input) {
|
|
|
285
285
|
["Max work items this run", input.config.max_iterations],
|
|
286
286
|
[
|
|
287
287
|
"Available candidates",
|
|
288
|
-
input.candidates.map((
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
title: candidate.title,
|
|
292
|
-
labels: candidate.labels,
|
|
293
|
-
parent: candidate.parent,
|
|
294
|
-
excerpt: candidate.excerpt,
|
|
288
|
+
buildSelectionSections(input.candidates).map((section) => ({
|
|
289
|
+
section: section.label ?? "Ungrouped issues",
|
|
290
|
+
candidates: section.candidates.map(selectionCandidateSummary),
|
|
295
291
|
})),
|
|
296
292
|
],
|
|
297
293
|
]);
|
|
@@ -880,6 +876,16 @@ function workItemSummary(item) {
|
|
|
880
876
|
parent: item.parent,
|
|
881
877
|
};
|
|
882
878
|
}
|
|
879
|
+
function selectionCandidateSummary(candidate) {
|
|
880
|
+
return {
|
|
881
|
+
key: candidate.key,
|
|
882
|
+
number: candidate.number,
|
|
883
|
+
title: candidate.title,
|
|
884
|
+
labels: candidate.labels,
|
|
885
|
+
parent: candidate.parent,
|
|
886
|
+
excerpt: candidate.excerpt,
|
|
887
|
+
};
|
|
888
|
+
}
|
|
883
889
|
function buildCommitMessage(item) {
|
|
884
890
|
return `${item.title}\n\nWork item: ${item.source.locator}`;
|
|
885
891
|
}
|
|
@@ -7,10 +7,11 @@ export async function confirmWorkItemSelection(input) {
|
|
|
7
7
|
if (!isInteractive) {
|
|
8
8
|
throw new Error('Interactive work item selection requires a TTY. Re-run with "nyxagent run --yes" to accept the agent selection.');
|
|
9
9
|
}
|
|
10
|
+
const choiceItems = buildSelectionChoiceItems(input);
|
|
10
11
|
const selectedKeys = await checkbox({
|
|
11
12
|
message: `Select work items to run (max ${input.maxItems})`,
|
|
12
|
-
choices:
|
|
13
|
-
pageSize: Math.min(Math.max(
|
|
13
|
+
choices: choiceItems.map(toInquirerChoice),
|
|
14
|
+
pageSize: Math.min(Math.max(choiceItems.length, 7), 20),
|
|
14
15
|
required: false,
|
|
15
16
|
validate: (selected) => selected.length <= input.maxItems ||
|
|
16
17
|
`Select at most ${input.maxItems} work item(s).`,
|
|
@@ -20,28 +21,58 @@ export async function confirmWorkItemSelection(input) {
|
|
|
20
21
|
},
|
|
21
22
|
});
|
|
22
23
|
const selected = new Set(selectedKeys);
|
|
23
|
-
|
|
24
|
+
const candidatesByKey = new Map(input.candidates.map((candidate) => [candidate.key, candidate]));
|
|
25
|
+
return choiceItems.flatMap((item) => {
|
|
26
|
+
if (item.type !== "choice" || !selected.has(item.value)) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
const candidate = candidatesByKey.get(item.value);
|
|
30
|
+
return candidate ? [candidate] : [];
|
|
31
|
+
});
|
|
24
32
|
}
|
|
25
33
|
export function buildSelectionChoiceItems(input) {
|
|
26
34
|
const proposedKeys = new Set(input.proposed.map((item) => item.key));
|
|
27
35
|
const items = [];
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
for (const section of buildSelectionSections(input.candidates)) {
|
|
37
|
+
if (section.label) {
|
|
38
|
+
items.push({ type: "separator", label: section.label });
|
|
39
|
+
}
|
|
40
|
+
for (const candidate of section.candidates) {
|
|
41
|
+
const proposed = proposedKeys.has(candidate.key);
|
|
42
|
+
items.push({
|
|
43
|
+
type: "choice",
|
|
44
|
+
value: candidate.key,
|
|
45
|
+
name: `#${candidate.number} ${candidate.title}${proposed ? " (agent)" : ""}`,
|
|
46
|
+
checked: proposed,
|
|
47
|
+
});
|
|
33
48
|
}
|
|
34
|
-
currentGroup = group;
|
|
35
|
-
const proposed = proposedKeys.has(candidate.key);
|
|
36
|
-
items.push({
|
|
37
|
-
type: "choice",
|
|
38
|
-
value: candidate.key,
|
|
39
|
-
name: `#${candidate.number} ${candidate.title}${proposed ? " (agent)" : ""}`,
|
|
40
|
-
checked: proposed,
|
|
41
|
-
});
|
|
42
49
|
}
|
|
43
50
|
return items;
|
|
44
51
|
}
|
|
52
|
+
export function buildSelectionSections(candidates) {
|
|
53
|
+
const sections = [];
|
|
54
|
+
const groupedSections = new Map();
|
|
55
|
+
let ungroupedSection;
|
|
56
|
+
for (const candidate of candidates) {
|
|
57
|
+
const group = detectSelectionGroup(candidate);
|
|
58
|
+
if (!group) {
|
|
59
|
+
if (!ungroupedSection) {
|
|
60
|
+
ungroupedSection = { candidates: [] };
|
|
61
|
+
sections.push(ungroupedSection);
|
|
62
|
+
}
|
|
63
|
+
ungroupedSection.candidates.push(candidate);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
let section = groupedSections.get(group.key);
|
|
67
|
+
if (!section) {
|
|
68
|
+
section = { label: group.label, candidates: [] };
|
|
69
|
+
groupedSections.set(group.key, section);
|
|
70
|
+
sections.push(section);
|
|
71
|
+
}
|
|
72
|
+
section.candidates.push(candidate);
|
|
73
|
+
}
|
|
74
|
+
return sections;
|
|
75
|
+
}
|
|
45
76
|
function toInquirerChoice(item) {
|
|
46
77
|
if (item.type === "separator") {
|
|
47
78
|
return new Separator(item.label);
|
|
@@ -52,17 +83,23 @@ function toInquirerChoice(item) {
|
|
|
52
83
|
checked: item.checked,
|
|
53
84
|
};
|
|
54
85
|
}
|
|
55
|
-
function
|
|
86
|
+
function detectSelectionGroup(candidate) {
|
|
56
87
|
if (candidate.parent) {
|
|
57
|
-
return
|
|
88
|
+
return {
|
|
89
|
+
key: `parent:${candidate.parent.key}`,
|
|
90
|
+
label: formatParentGroup(candidate.parent),
|
|
91
|
+
};
|
|
58
92
|
}
|
|
59
93
|
for (const label of candidate.labels ?? []) {
|
|
60
94
|
const group = parseGroupLabel(label);
|
|
61
95
|
if (group) {
|
|
62
|
-
return group;
|
|
96
|
+
return { key: `display:${group.toLowerCase()}`, label: group };
|
|
63
97
|
}
|
|
64
98
|
}
|
|
65
|
-
|
|
99
|
+
const titleGroup = parseBracketedTitleGroup(candidate.title);
|
|
100
|
+
return titleGroup
|
|
101
|
+
? { key: `display:${titleGroup.toLowerCase()}`, label: titleGroup }
|
|
102
|
+
: undefined;
|
|
66
103
|
}
|
|
67
104
|
function formatParentGroup(parent) {
|
|
68
105
|
if (!parent) {
|