newpr 1.0.19 → 1.0.20
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
package/src/cli/args.ts
CHANGED
|
@@ -57,7 +57,7 @@ Options (review mode):
|
|
|
57
57
|
-v, --version Show version
|
|
58
58
|
|
|
59
59
|
Environment Variables:
|
|
60
|
-
OPENROUTER_API_KEY
|
|
60
|
+
OPENROUTER_API_KEY Optional. Used when configured; otherwise local agent fallback is used.
|
|
61
61
|
GITHUB_TOKEN Optional. Falls back to gh CLI token.
|
|
62
62
|
NEWPR_MODEL Default model override.
|
|
63
63
|
NEWPR_MAX_FILES Max files to analyze (default: 100).
|
package/src/config/index.ts
CHANGED
|
@@ -61,12 +61,7 @@ export async function loadConfig(
|
|
|
61
61
|
): Promise<NewprConfig> {
|
|
62
62
|
const stored = await (_readStore ?? readStoredConfig)();
|
|
63
63
|
|
|
64
|
-
const apiKey = process.env.OPENROUTER_API_KEY || stored.openrouter_api_key;
|
|
65
|
-
if (!apiKey) {
|
|
66
|
-
throw new Error(
|
|
67
|
-
"OPENROUTER_API_KEY is not set. Run `newpr auth` to configure, or set the environment variable.",
|
|
68
|
-
);
|
|
69
|
-
}
|
|
64
|
+
const apiKey = process.env.OPENROUTER_API_KEY || stored.openrouter_api_key || "";
|
|
70
65
|
|
|
71
66
|
const agentVal = stored.agent as NewprConfig["agent"];
|
|
72
67
|
const rawLang = process.env.NEWPR_LANGUAGE || stored.language || DEFAULT_CONFIG.language;
|
package/src/stack/feasibility.ts
CHANGED
|
@@ -30,7 +30,7 @@ export function checkFeasibility(input: FeasibilityInput): FeasibilityResult {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const deduped = deduplicateEdges(edges);
|
|
33
|
-
const result = topologicalSort(Array.from(allGroups), deduped, deltas);
|
|
33
|
+
const result = topologicalSort(Array.from(allGroups), deduped, deltas, ownership);
|
|
34
34
|
|
|
35
35
|
return result;
|
|
36
36
|
}
|
|
@@ -137,6 +137,7 @@ function topologicalSort(
|
|
|
137
137
|
groups: string[],
|
|
138
138
|
edges: ConstraintEdge[],
|
|
139
139
|
deltas: DeltaEntry[],
|
|
140
|
+
ownership?: Map<string, string>,
|
|
140
141
|
): FeasibilityResult {
|
|
141
142
|
const inDegree = new Map<string, number>();
|
|
142
143
|
const adjacency = new Map<string, string[]>();
|
|
@@ -156,7 +157,7 @@ function topologicalSort(
|
|
|
156
157
|
edgeMap.set(`${edge.from}→${edge.to}`, edge);
|
|
157
158
|
}
|
|
158
159
|
|
|
159
|
-
const firstCommitDate = buildFirstCommitDateMap(groups, deltas);
|
|
160
|
+
const firstCommitDate = buildFirstCommitDateMap(groups, deltas, ownership);
|
|
160
161
|
|
|
161
162
|
const queue: string[] = [];
|
|
162
163
|
for (const [g, deg] of inDegree) {
|
|
@@ -200,6 +201,7 @@ function topologicalSort(
|
|
|
200
201
|
function buildFirstCommitDateMap(
|
|
201
202
|
groups: string[],
|
|
202
203
|
deltas: DeltaEntry[],
|
|
204
|
+
ownership?: Map<string, string>,
|
|
203
205
|
): Map<string, string> {
|
|
204
206
|
const result = new Map<string, string>();
|
|
205
207
|
for (const g of groups) {
|
|
@@ -207,12 +209,11 @@ function buildFirstCommitDateMap(
|
|
|
207
209
|
}
|
|
208
210
|
for (const delta of deltas) {
|
|
209
211
|
for (const change of delta.changes) {
|
|
210
|
-
const
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
212
|
+
const groupId = ownership?.get(change.path);
|
|
213
|
+
if (!groupId) continue;
|
|
214
|
+
const current = result.get(groupId);
|
|
215
|
+
if (current && delta.date < current) {
|
|
216
|
+
result.set(groupId, delta.date);
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
219
|
}
|
package/src/web/server/routes.ts
CHANGED
|
@@ -1658,7 +1658,8 @@ Before posting an inline comment, ALWAYS call \`get_file_diff\` first to find th
|
|
|
1658
1658
|
},
|
|
1659
1659
|
|
|
1660
1660
|
"POST /api/slides": async (req: Request) => {
|
|
1661
|
-
|
|
1661
|
+
const apiKey = config.openrouter_api_key;
|
|
1662
|
+
if (!apiKey) return json({ error: "OpenRouter API key required" }, 400);
|
|
1662
1663
|
|
|
1663
1664
|
const body = await req.json() as { sessionId?: string; language?: string; resume?: boolean };
|
|
1664
1665
|
const sessionId = body.sessionId;
|
|
@@ -1678,7 +1679,7 @@ Before posting an inline comment, ALWAYS call \`get_file_diff\` first to find th
|
|
|
1678
1679
|
(async () => {
|
|
1679
1680
|
try {
|
|
1680
1681
|
const deck = await generateSlides(
|
|
1681
|
-
|
|
1682
|
+
apiKey,
|
|
1682
1683
|
data,
|
|
1683
1684
|
config.model,
|
|
1684
1685
|
body.language ?? config.language,
|
|
@@ -1749,7 +1750,8 @@ Before posting an inline comment, ALWAYS call \`get_file_diff\` first to find th
|
|
|
1749
1750
|
const body = await req.json() as { sessionId?: string; resume?: boolean };
|
|
1750
1751
|
const sessionId = body.sessionId;
|
|
1751
1752
|
if (!sessionId) return json({ error: "Missing sessionId" }, 400);
|
|
1752
|
-
|
|
1753
|
+
const apiKey = config.openrouter_api_key;
|
|
1754
|
+
if (!apiKey) return json({ error: "API key required" }, 400);
|
|
1753
1755
|
|
|
1754
1756
|
const plugin = getPlugin(pluginId);
|
|
1755
1757
|
if (!plugin) return json({ error: `Unknown plugin: ${pluginId}` }, 404);
|
|
@@ -1770,7 +1772,7 @@ Before posting an inline comment, ALWAYS call \`get_file_diff\` first to find th
|
|
|
1770
1772
|
(async () => {
|
|
1771
1773
|
try {
|
|
1772
1774
|
const result = await plugin.generate(
|
|
1773
|
-
{ apiKey
|
|
1775
|
+
{ apiKey, sessionId, data, language: config.language },
|
|
1774
1776
|
(event) => { job.message = event.message; job.current = event.current; job.total = event.total; },
|
|
1775
1777
|
existingData,
|
|
1776
1778
|
);
|
|
@@ -442,22 +442,22 @@ async function runStackPipeline(
|
|
|
442
442
|
|
|
443
443
|
buildReattributionWarnings(partition, analysisSet, allStructuredWarnings);
|
|
444
444
|
|
|
445
|
-
const
|
|
446
|
-
if (
|
|
445
|
+
const backfillGroup = groupOrder[0] ?? groupOrder[groupOrder.length - 1];
|
|
446
|
+
if (backfillGroup) {
|
|
447
447
|
const backfilled: string[] = [];
|
|
448
448
|
for (const path of deltaFilePaths) {
|
|
449
449
|
if (!mergedOwnership.has(path)) {
|
|
450
|
-
mergedOwnership.set(path,
|
|
450
|
+
mergedOwnership.set(path, backfillGroup);
|
|
451
451
|
backfilled.push(path);
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
if (backfilled.length > 0) {
|
|
455
|
-
allWarnings.push(`Files still unassigned after AI classification, fallback to "${
|
|
455
|
+
allWarnings.push(`Files still unassigned after AI classification, fallback to "${backfillGroup}": ${backfilled.join(", ")}`);
|
|
456
456
|
allStructuredWarnings.push({
|
|
457
457
|
category: "assignment",
|
|
458
458
|
severity: "warn",
|
|
459
|
-
title: `${backfilled.length} file(s) fell back to
|
|
460
|
-
message: `AI could not classify these files — assigned to "${
|
|
459
|
+
title: `${backfilled.length} file(s) fell back to first group`,
|
|
460
|
+
message: `AI could not classify these files — assigned to "${backfillGroup}" as fallback`,
|
|
461
461
|
details: backfilled,
|
|
462
462
|
});
|
|
463
463
|
}
|
|
@@ -476,6 +476,13 @@ async function runStackPipeline(
|
|
|
476
476
|
for (const [path, groupId] of balanced.ownership) {
|
|
477
477
|
mergedOwnership.set(path, groupId);
|
|
478
478
|
}
|
|
479
|
+
const balancedGroupFiles = new Map<string, string[]>();
|
|
480
|
+
for (const [path, groupId] of mergedOwnership) {
|
|
481
|
+
const files = balancedGroupFiles.get(groupId) ?? [];
|
|
482
|
+
files.push(path);
|
|
483
|
+
balancedGroupFiles.set(groupId, files);
|
|
484
|
+
}
|
|
485
|
+
currentGroups = currentGroups.map((g) => ({ ...g, files: (balancedGroupFiles.get(g.name) ?? g.files).sort() }));
|
|
479
486
|
allStructuredWarnings.push(...balanced.warnings);
|
|
480
487
|
|
|
481
488
|
if (session.maxGroups && session.maxGroups > 0 && currentGroups.length > session.maxGroups) {
|