nexus-agents 3.6.13 → 3.6.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/dist/{chunk-G45DZK5W.js → chunk-4DU2MJLV.js} +8 -11
- package/dist/chunk-4DU2MJLV.js.map +1 -0
- package/dist/{chunk-EOIQRW7A.js → chunk-G3D7AJ3C.js} +9 -2
- package/dist/chunk-G3D7AJ3C.js.map +1 -0
- package/dist/{chunk-MNQYHPWP.js → chunk-JNHHJZT3.js} +5 -2
- package/dist/chunk-JNHHJZT3.js.map +1 -0
- package/dist/chunk-KYVWCL7O.js +10 -0
- package/dist/chunk-KYVWCL7O.js.map +1 -0
- package/dist/{chunk-SQOTLTGX.js → chunk-KZJXRKAE.js} +3 -3
- package/dist/{chunk-SYR23V25.js → chunk-YSYSJICI.js} +2 -2
- package/dist/cli.js +21 -11
- package/dist/cli.js.map +1 -1
- package/dist/{consensus-vote-2HY2OC6W.js → consensus-vote-JGES3MKP.js} +3 -2
- package/dist/index.js +12 -5
- package/dist/index.js.map +1 -1
- package/dist/{pr-reviewer-helpers-L3JGYIAM.js → pr-reviewer-helpers-SBWJ4WP6.js} +3 -2
- package/dist/{setup-command-4JDXXE7X.js → setup-command-LRV5CMBR.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-EOIQRW7A.js.map +0 -1
- package/dist/chunk-G45DZK5W.js.map +0 -1
- package/dist/chunk-MNQYHPWP.js.map +0 -1
- /package/dist/{chunk-SQOTLTGX.js.map → chunk-KZJXRKAE.js.map} +0 -0
- /package/dist/{chunk-SYR23V25.js.map → chunk-YSYSJICI.js.map} +0 -0
- /package/dist/{consensus-vote-2HY2OC6W.js.map → consensus-vote-JGES3MKP.js.map} +0 -0
- /package/dist/{pr-reviewer-helpers-L3JGYIAM.js.map → pr-reviewer-helpers-SBWJ4WP6.js.map} +0 -0
- /package/dist/{setup-command-4JDXXE7X.js.map → setup-command-LRV5CMBR.js.map} +0 -0
|
@@ -4,6 +4,9 @@ import {
|
|
|
4
4
|
SEVERITY_EMOJI,
|
|
5
5
|
SEVERITY_ORDER2 as SEVERITY_ORDER
|
|
6
6
|
} from "./chunk-CQ77TF4Y.js";
|
|
7
|
+
import {
|
|
8
|
+
allOf
|
|
9
|
+
} from "./chunk-KYVWCL7O.js";
|
|
7
10
|
import {
|
|
8
11
|
assessReputation,
|
|
9
12
|
gateWithReputation,
|
|
@@ -148,7 +151,7 @@ function determineApproval(findings) {
|
|
|
148
151
|
function determineDecision(reviews, findings) {
|
|
149
152
|
const hasCritical = findings.some((f) => f.severity === "critical");
|
|
150
153
|
const hasHigh = findings.some((f) => f.severity === "high");
|
|
151
|
-
const allApproved = reviews
|
|
154
|
+
const allApproved = allOf(reviews, (r) => r.approved, false);
|
|
152
155
|
if (hasCritical) return "request_changes";
|
|
153
156
|
if (hasHigh && !allApproved) return "request_changes";
|
|
154
157
|
if (findings.length > 0) return "comment";
|
|
@@ -288,4 +291,4 @@ export {
|
|
|
288
291
|
formatReviewComment,
|
|
289
292
|
createFailedReview
|
|
290
293
|
};
|
|
291
|
-
//# sourceMappingURL=chunk-
|
|
294
|
+
//# sourceMappingURL=chunk-JNHHJZT3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dogfooding/pr-reviewer-helpers.ts"],"sourcesContent":["/**\n * nexus-agents/dogfooding - PR Reviewer Helpers\n *\n * Helper functions for PR review formatting and aggregation.\n *\n * @module dogfooding/pr-reviewer-helpers\n * (Source: Issue #161, Alignment Roadmap Phase 3)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { Result } from '../core/index.js';\nimport { getTimeProvider, createLogger } from '../core/index.js';\nimport type { ScmUserMetadata } from '../scm/types.js';\nimport type {\n PRMetadata,\n PRReviewDraft,\n PRTrustAssessment,\n ExpertReviewResult,\n ReviewFinding,\n ReviewCategory,\n ReviewSeverity,\n ReviewDecision,\n} from './pr-review-types.js';\nimport {\n SEVERITY_ORDER,\n CATEGORY_DISPLAY_NAMES,\n SEVERITY_EMOJI,\n DECISION_EMOJI,\n} from './pr-review-types.js';\nimport { sanitizeInput } from '../security/input-sanitizer.js';\nimport { allOf } from '../utils/verdict-aggregation.js';\nimport {\n assessReputation,\n gateWithReputation,\n resolveReputationGatingMode,\n} from '../security/reputation-model.js';\nimport type {\n ReputationCache,\n ReputationGateDecision,\n ReputationAssessment,\n GitHubUserMetadata,\n} from '../security/reputation-model.js';\nimport type { ClassifyResult } from '../security/trust-classifier.js';\n\nconst repLogger = createLogger({ component: 'PRReviewer.reputation' });\n\n// =============================================================================\n// Reputation Gating Helpers (#3123, epic #3118 Phase 5)\n// =============================================================================\n\n/**\n * Best-effort account-age lookup for a PR author (#3133, Phase-3 equivalent for\n * the PR path). Returns the author's real account age in days via the provider's\n * `fetchUserMetadata`, or `undefined` on any failure (err Result, unparseable\n * `createdAt`, or an unexpected rejection) — never fabricated, never throws, so\n * the review is never blocked by this lookup.\n */\nexport async function fetchAccountAgeDays(\n provider: { fetchUserMetadata: (u: string) => Promise<Result<ScmUserMetadata, Error>> },\n username: string\n): Promise<number | undefined> {\n try {\n const result = await provider.fetchUserMetadata(username);\n if (!result.ok) return undefined;\n const createdMs = Date.parse(result.value.createdAt);\n if (!Number.isFinite(createdMs)) return undefined;\n return Math.floor((getTimeProvider().now() - createdMs) / 86_400_000);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Assesses the PR author's reputation from the signals available in the PR\n * event: author association + injection flags from the (sanitized) PR body, plus\n * the author's real account age when it was fetched (#3133). `accountAgeDays` is\n * OMITTED when the lookup failed — never fabricated, so the engine skips the\n * `new_account` signal. Returns undefined when reputation is disabled.\n */\nexport function assessPRReputation(\n pr: PRMetadata,\n cache: ReputationCache,\n enableReputation: boolean,\n accountAgeDays: number | undefined\n): ReputationAssessment | undefined {\n if (!enableReputation) return undefined;\n // Only `injectionFlags` is consumed here, and injection detection is\n // role-independent — the userRole arg ('unknown') and the sanitizer's own\n // trustTier output are intentionally irrelevant to this call.\n const sanitizeResult = sanitizeInput(pr.body, 'unknown', pr.author);\n const metadata: GitHubUserMetadata = {\n username: pr.author,\n ...(accountAgeDays !== undefined ? { accountAgeDays } : {}),\n authorAssociation: pr.authorAssociation,\n injectionFlags: sanitizeResult.injectionFlags,\n };\n return assessReputation(metadata, cache);\n}\n\n/** Builds the observability assessment surfaced on the review result (#3123). */\nexport function buildPRTrustAssessment(\n trustResult: ClassifyResult,\n reputation: ReputationAssessment | undefined,\n gateDecision: ReputationGateDecision\n): PRTrustAssessment {\n // Tier-1 (owner/allowlisted) authors cannot be suspicious.\n const isTier1 = trustResult.trustTier === '1';\n return {\n trustTier: trustResult.trustTier,\n userRole: trustResult.userRole,\n isAllowlisted: trustResult.isAllowlisted,\n reputationScore: reputation?.reputationScore,\n suspiciousSignals: isTier1 ? [] : (reputation?.suspiciousSignals ?? []),\n isSuspicious: isTier1 ? false : (reputation?.isSuspicious ?? false),\n enforcedTrustTier: gateDecision.enforcedTier,\n reputationReconciledTier: gateDecision.reconciledTier,\n gatingMode: gateDecision.mode,\n };\n}\n\n/**\n * Assesses the PR author's reputation and applies the gating rollout mode\n * (#3123). Returns the gate decision (for the policy gate) and the assessment\n * surfaced on the result. A suppressed demotion (audit/off) is logged.\n */\nexport function gatePRAuthor(\n pr: PRMetadata,\n trustResult: ClassifyResult,\n accountAgeDays: number | undefined,\n cache: ReputationCache,\n enableReputation: boolean\n): { gateDecision: ReputationGateDecision; trustAssessment: PRTrustAssessment } {\n const reputation = assessPRReputation(pr, cache, enableReputation, accountAgeDays);\n const gateDecision = gateWithReputation(\n trustResult.trustTier,\n reputation,\n resolveReputationGatingMode()\n );\n if (gateDecision.demotionSuppressed) {\n repLogger.warn('Reputation demotion suppressed by gating mode (would block under enforce)', {\n prNumber: pr.number,\n author: pr.author,\n mode: gateDecision.mode,\n classifierTier: trustResult.trustTier,\n reconciledTier: gateDecision.reconciledTier,\n });\n }\n return {\n gateDecision,\n trustAssessment: buildPRTrustAssessment(trustResult, reputation, gateDecision),\n };\n}\n\n// =============================================================================\n// Parsing Helpers\n// =============================================================================\n\nexport function parseSeverity(value: unknown): ReviewSeverity {\n if (typeof value === 'string') {\n const lower = value.toLowerCase();\n if (lower in SEVERITY_ORDER) return lower as ReviewSeverity;\n }\n return 'medium';\n}\n\nexport function parseCategory(value: unknown): ReviewCategory {\n if (typeof value === 'string') {\n const lower = value.toLowerCase();\n if (lower in CATEGORY_DISPLAY_NAMES) return lower as ReviewCategory;\n }\n return 'code_quality';\n}\n\nexport function extractSummary(output: Record<string, unknown>): string {\n if (typeof output.summary === 'string') return output.summary;\n if (typeof output.content === 'string') return output.content;\n if (typeof output.message === 'string') return output.message;\n return 'Review completed';\n}\n\nexport function extractStringField(\n record: Record<string, unknown>,\n ...keys: string[]\n): string | undefined {\n for (const key of keys) {\n const value = record[key];\n if (typeof value === 'string') return value;\n }\n return undefined;\n}\n\n// =============================================================================\n// Finding Parsing\n// =============================================================================\n\nexport function parseFindings(\n output: Record<string, unknown>,\n expertId: string,\n minSeverity: ReviewSeverity\n): ReviewFinding[] {\n const minOrder = SEVERITY_ORDER[minSeverity];\n const sources = collectSources(output);\n\n const findings: ReviewFinding[] = [];\n for (const source of sources) {\n if (!Array.isArray(source)) continue;\n for (const item of source) {\n const finding = parseOneFinding(item, expertId, minOrder);\n if (finding !== null) findings.push(finding);\n }\n }\n return findings;\n}\n\nfunction collectSources(output: Record<string, unknown>): unknown[] {\n return [\n output.findings,\n output.vulnerabilities,\n output.issues,\n (output as { content?: { findings?: unknown } }).content,\n ];\n}\n\nfunction parseOneFinding(item: unknown, expertId: string, minOrder: number): ReviewFinding | null {\n if (typeof item !== 'object' || item === null) return null;\n\n const record = item as Record<string, unknown>;\n const severity = parseSeverity(record.severity);\n if (SEVERITY_ORDER[severity] < minOrder) return null;\n\n return {\n id: randomUUID(),\n category: parseCategory(record.category),\n severity,\n title: extractStringField(record, 'title', 'name') ?? 'Finding',\n description: extractStringField(record, 'description', 'message') ?? '',\n file: typeof record.file === 'string' ? record.file : undefined,\n line: typeof record.line === 'number' ? record.line : undefined,\n suggestion: typeof record.suggestion === 'string' ? record.suggestion : undefined,\n expertId,\n confidence: typeof record.confidence === 'number' ? record.confidence : 0.7,\n };\n}\n\n// =============================================================================\n// Decision Helpers\n// =============================================================================\n\nexport function determineApproval(findings: ReviewFinding[]): boolean {\n const hasBlocking = findings.some((f) => f.severity === 'critical' || f.severity === 'high');\n return !hasBlocking;\n}\n\nexport function determineDecision(\n reviews: ExpertReviewResult[],\n findings: ReviewFinding[]\n): ReviewDecision {\n const hasCritical = findings.some((f) => f.severity === 'critical');\n const hasHigh = findings.some((f) => f.severity === 'high');\n // Zero expert reviews is not unanimous approval (#4581): with `true` here the\n // `hasHigh && !allApproved` branch could never fire on an unreviewed PR, so a\n // HIGH finding silently downgraded from request_changes to comment.\n const allApproved = allOf(reviews, (r) => r.approved, false);\n\n if (hasCritical) return 'request_changes';\n if (hasHigh && !allApproved) return 'request_changes';\n if (findings.length > 0) return 'comment';\n return 'approve';\n}\n\nexport function calculateConsensus(reviews: ExpertReviewResult[]): number {\n if (reviews.length === 0) return 1;\n const approvals = reviews.filter((r) => r.approved).length;\n return approvals / reviews.length;\n}\n\n// =============================================================================\n// Counting Helpers\n// =============================================================================\n\nexport function countBySeverity(findings: ReviewFinding[]): Record<ReviewSeverity, number> {\n const counts: Record<ReviewSeverity, number> = {\n critical: 0,\n high: 0,\n medium: 0,\n low: 0,\n info: 0,\n };\n\n for (const f of findings) {\n counts[f.severity]++;\n }\n\n return counts;\n}\n\nexport function countByCategory(findings: ReviewFinding[]): Record<ReviewCategory, number> {\n const counts: Record<ReviewCategory, number> = {\n security: 0,\n performance: 0,\n code_quality: 0,\n testing: 0,\n documentation: 0,\n architecture: 0,\n };\n\n for (const f of findings) {\n counts[f.category]++;\n }\n\n return counts;\n}\n\nexport function sumFindings(counts: Record<ReviewSeverity, number>): number {\n return Object.values(counts).reduce((a, b) => a + b, 0);\n}\n\n// =============================================================================\n// Summary Generation\n// =============================================================================\n\nexport function generateSummary(\n pr: PRMetadata,\n reviews: ExpertReviewResult[],\n decision: ReviewDecision\n): string {\n const expertSummaries = reviews\n .map((r) => `- **${CATEGORY_DISPLAY_NAMES[r.expertType as ReviewCategory]}**: ${r.summary}`)\n .join('\\n');\n\n return `Reviewed PR #${String(pr.number)}: ${pr.title}\n\n**Decision:** ${decision.replaceAll('_', ' ')}\n**Experts consulted:** ${String(reviews.length)}\n\n${expertSummaries}`;\n}\n\n// =============================================================================\n// GitHub Comment Formatting\n// =============================================================================\n\n/**\n * Formats the review result as a GitHub comment.\n */\nexport function formatReviewComment(result: PRReviewDraft): string {\n const emoji = DECISION_EMOJI[result.decision];\n const decisionText = result.decision.replaceAll('_', ' ').toUpperCase();\n\n const findingsSection = formatFindingsSection(result);\n const statsSection = formatStatsSection(result);\n\n return `## ${emoji} Nexus Agents Review: ${decisionText}\n\n${result.summary}\n\n${findingsSection}\n\n${statsSection}\n\n---\n*Reviewed by [nexus-agents](https://github.com/nexus-substrate/nexus-agents) in ${String(result.totalDurationMs)}ms*`;\n}\n\nfunction formatFindingsSection(result: PRReviewDraft): string {\n const allFindings = result.expertReviews.flatMap((r) => r.findings);\n\n if (allFindings.length === 0) {\n return '_No issues found._';\n }\n\n const sorted = [...allFindings].sort(\n (a, b) => SEVERITY_ORDER[b.severity] - SEVERITY_ORDER[a.severity]\n );\n\n const lines = ['### Findings', ''];\n\n for (const f of sorted) {\n const emoji = SEVERITY_EMOJI[f.severity];\n const loc =\n f.file !== undefined\n ? ` (\\`${f.file}${f.line !== undefined ? `:${String(f.line)}` : ''}\\`)`\n : '';\n lines.push(`${emoji} **${f.title}**${loc}`);\n lines.push(`> ${f.description}`);\n if (f.suggestion !== undefined) {\n lines.push(`> 💡 ${f.suggestion}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\nfunction formatStatsSection(result: PRReviewDraft): string {\n const { findingsBySeverity } = result;\n const total = sumFindings(findingsBySeverity);\n\n const parts: string[] = [];\n for (const severity of ['critical', 'high', 'medium', 'low', 'info'] as ReviewSeverity[]) {\n const count = findingsBySeverity[severity];\n if (count > 0) {\n parts.push(`${SEVERITY_EMOJI[severity]} ${String(count)} ${severity}`);\n }\n }\n\n return `<details>\n<summary>Review Statistics (${String(total)} findings)</summary>\n\n- Experts: ${String(result.expertCount)}\n- Consensus: ${(result.consensusScore * 100).toFixed(0)}%\n- Duration: ${String(result.totalDurationMs)}ms\n- Findings: ${parts.join(', ') || 'none'}\n\n</details>`;\n}\n\n// =============================================================================\n// Failed Review Factory\n// =============================================================================\n\nexport function createFailedReview(\n expertId: string,\n category: ReviewCategory,\n durationMs: number,\n error: string\n): ExpertReviewResult {\n return {\n expertId,\n expertType: category,\n approved: true, // Don't block on failures\n summary: `Review failed: ${error}`,\n findings: [],\n durationMs,\n confidence: 0,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/verdict-aggregation.ts"],"sourcesContent":["/**\n * Aggregating a verdict over a collection that might be empty (#4580).\n *\n * `[].every(p)` is `true` in JavaScript. So `checks.every((c) => c.passed)`\n * reports **pass** when `checks` is empty — absence rendered as health, on\n * exactly the code paths where that is most dangerous.\n *\n * Confirmed instances of the shape:\n * - `verify_audit_chain` returns `ok: true` over zero events, so deleting\n * every audit log file makes a tamper-evident chain verify clean (#4579)\n * - a quality gate with zero checks reported `pass` (#4544)\n * - the QA pipeline stage reported `success` after reviewing zero tasks,\n * letting the graph advance as though implementations had been reviewed\n *\n * ## Why a helper rather than a lint or a type\n *\n * Measured before choosing: 64 non-test `.every()` calls, and most are fine —\n * `github-provider.ts:178` guards with `length > 0` and correctly leaves an\n * empty check set `pending`. A wide surface with sparse defects, so a blanket\n * lint would be mostly false positives, and false positives are what teach\n * people to bypass a gate.\n *\n * What was missing is not enforcement but a *decision point*: nothing made the\n * author say what empty means. `whenEmpty` is a required argument, so they\n * must. Chosen by a 7-voter `higher_order` panel at the supermajority bar.\n *\n * ## Choosing `whenEmpty`\n *\n * Ask what an empty collection is evidence OF. Usually nothing — in which case\n * the honest answer is the non-committal verdict (`pending`, `unmeasured`,\n * `skip`), not the optimistic one. Reserve `true` for cases where vacuous truth\n * is genuinely the contract, and say so at the call site.\n *\n * @module utils/verdict-aggregation\n * (Source: Issue #4580)\n */\n\n/**\n * `predicate` holds for every item — with the empty case named, not defaulted.\n *\n * @param items - The collection to judge.\n * @param predicate - Must hold for each item.\n * @param whenEmpty - The verdict when there is nothing to judge. Required.\n */\nexport function allOf<T>(\n items: readonly T[],\n predicate: (item: T) => boolean,\n whenEmpty: boolean\n): boolean {\n if (items.length === 0) return whenEmpty;\n return items.every(predicate);\n}\n\n/**\n * `predicate` holds for at least one item — with the empty case named.\n *\n * `[].some(p)` is already `false`, which is usually right, but not always: a\n * \"did anything fail?\" check over zero results should often be `unmeasured`\n * rather than a clean `false`. Naming it keeps the reasoning visible.\n *\n * @param items - The collection to judge.\n * @param predicate - Must hold for at least one item.\n * @param whenEmpty - The verdict when there is nothing to judge. Required.\n */\nexport function anyOf<T>(\n items: readonly T[],\n predicate: (item: T) => boolean,\n whenEmpty: boolean\n): boolean {\n if (items.length === 0) return whenEmpty;\n return items.some(predicate);\n}\n\n/**\n * Reduce a collection to an arbitrary verdict, with the empty case named.\n *\n * For verdicts richer than a boolean — a severity, a tri-state gate result —\n * where the empty case is usually `unmeasured` rather than the best value.\n *\n * @param items - The collection to judge.\n * @param aggregate - Folds a non-empty collection into a verdict.\n * @param whenEmpty - The verdict when there is nothing to judge. Required.\n */\nexport function verdictOver<T, V>(\n items: readonly T[],\n aggregate: (items: readonly T[]) => V,\n whenEmpty: V\n): V {\n if (items.length === 0) return whenEmpty;\n return aggregate(items);\n}\n"],"mappings":";AA4CO,SAAS,MACd,OACA,WACA,WACS;AACT,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,MAAM,MAAM,SAAS;AAC9B;","names":[]}
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
} from "./chunk-OQLFRJCW.js";
|
|
50
50
|
|
|
51
51
|
// src/version.ts
|
|
52
|
-
var VERSION = true ? "3.6.
|
|
52
|
+
var VERSION = true ? "3.6.14" : "dev";
|
|
53
53
|
|
|
54
54
|
// src/config/schemas-core.ts
|
|
55
55
|
import { z } from "zod";
|
|
@@ -2316,7 +2316,7 @@ async function runDoctorFix(result) {
|
|
|
2316
2316
|
writeLine2("\u2500".repeat(40));
|
|
2317
2317
|
let fixCount = 0;
|
|
2318
2318
|
if (!result.dataDirectory.rootExists || result.dataDirectory.subdirectories.some((d) => !d.exists || !d.writable)) {
|
|
2319
|
-
const { runSetup } = await import("./setup-command-
|
|
2319
|
+
const { runSetup } = await import("./setup-command-LRV5CMBR.js");
|
|
2320
2320
|
const setupResult = runSetup({
|
|
2321
2321
|
skipMcp: true,
|
|
2322
2322
|
skipRules: true,
|
|
@@ -2429,4 +2429,4 @@ export {
|
|
|
2429
2429
|
startStdioServer,
|
|
2430
2430
|
closeServer
|
|
2431
2431
|
};
|
|
2432
|
-
//# sourceMappingURL=chunk-
|
|
2432
|
+
//# sourceMappingURL=chunk-KZJXRKAE.js.map
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
checkSqlite,
|
|
9
9
|
defaultConfig,
|
|
10
10
|
initDataDirectories
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KZJXRKAE.js";
|
|
12
12
|
import {
|
|
13
13
|
BUILT_IN_EXPERTS
|
|
14
14
|
} from "./chunk-YPNQPT2F.js";
|
|
@@ -2001,4 +2001,4 @@ export {
|
|
|
2001
2001
|
setupCommand,
|
|
2002
2002
|
setupCommandAsync
|
|
2003
2003
|
};
|
|
2004
|
-
//# sourceMappingURL=chunk-
|
|
2004
|
+
//# sourceMappingURL=chunk-YSYSJICI.js.map
|
package/dist/cli.js
CHANGED
|
@@ -13,12 +13,12 @@ import {
|
|
|
13
13
|
generateSummary,
|
|
14
14
|
parseFindings,
|
|
15
15
|
sumFindings
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-JNHHJZT3.js";
|
|
17
17
|
import "./chunk-SAPRUA5H.js";
|
|
18
18
|
import {
|
|
19
19
|
setupCommandAsync,
|
|
20
20
|
verifyCommand
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-YSYSJICI.js";
|
|
22
22
|
import "./chunk-G54Y5OQU.js";
|
|
23
23
|
import {
|
|
24
24
|
AuthHandler,
|
|
@@ -139,7 +139,7 @@ import {
|
|
|
139
139
|
validateCommand,
|
|
140
140
|
validateWorkflow,
|
|
141
141
|
wrapInMarkdownFence
|
|
142
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-4DU2MJLV.js";
|
|
143
143
|
import "./chunk-DJLJGL34.js";
|
|
144
144
|
import "./chunk-HFOQKCD2.js";
|
|
145
145
|
import "./chunk-BQTMMLQQ.js";
|
|
@@ -166,7 +166,7 @@ import {
|
|
|
166
166
|
loadConfig,
|
|
167
167
|
runDoctor,
|
|
168
168
|
validateNexusEnv
|
|
169
|
-
} from "./chunk-
|
|
169
|
+
} from "./chunk-KZJXRKAE.js";
|
|
170
170
|
import {
|
|
171
171
|
buildOpenAICompatAdapters,
|
|
172
172
|
readOpenAICompatEnv
|
|
@@ -183,7 +183,10 @@ import {
|
|
|
183
183
|
executeVoting,
|
|
184
184
|
mapOutcomeToDecision,
|
|
185
185
|
registerConsensusVoteTool
|
|
186
|
-
} from "./chunk-
|
|
186
|
+
} from "./chunk-G3D7AJ3C.js";
|
|
187
|
+
import {
|
|
188
|
+
allOf
|
|
189
|
+
} from "./chunk-KYVWCL7O.js";
|
|
187
190
|
import {
|
|
188
191
|
loadUsageEvents,
|
|
189
192
|
rollupByModel
|
|
@@ -4076,7 +4079,7 @@ ${file.patch}
|
|
|
4076
4079
|
violations: policyResult.violations
|
|
4077
4080
|
});
|
|
4078
4081
|
}
|
|
4079
|
-
const { formatReviewComment: formatReviewComment2 } = await import("./pr-reviewer-helpers-
|
|
4082
|
+
const { formatReviewComment: formatReviewComment2 } = await import("./pr-reviewer-helpers-SBWJ4WP6.js");
|
|
4080
4083
|
const body = formatReviewComment2(result);
|
|
4081
4084
|
const postResult = await provider.createReview(pr.prNumber, body, result.decision);
|
|
4082
4085
|
if (!postResult.ok) {
|
|
@@ -15387,7 +15390,7 @@ async function runReleaseAnnounce(options) {
|
|
|
15387
15390
|
}
|
|
15388
15391
|
results.push(result);
|
|
15389
15392
|
}
|
|
15390
|
-
const allSuccess = results
|
|
15393
|
+
const allSuccess = allOf(results, (r) => r.success, false);
|
|
15391
15394
|
return {
|
|
15392
15395
|
success: allSuccess,
|
|
15393
15396
|
version: opts.version,
|
|
@@ -15418,8 +15421,9 @@ function printReleaseAnnounceResult(result, verbose = false) {
|
|
|
15418
15421
|
}
|
|
15419
15422
|
console.log("");
|
|
15420
15423
|
}
|
|
15421
|
-
|
|
15422
|
-
|
|
15424
|
+
if (result.channels.length === 0) {
|
|
15425
|
+
console.log(`${colors.yellow}${colors.bold}\u26A0 No announcements were generated${colors.reset}`);
|
|
15426
|
+
} else if (allOf(result.channels, (c) => c.success, false)) {
|
|
15423
15427
|
console.log(`${colors.green}${colors.bold}\u2713 All announcements generated${colors.reset}`);
|
|
15424
15428
|
} else {
|
|
15425
15429
|
console.log(`${colors.yellow}${colors.bold}\u26A0 Some announcements failed${colors.reset}`);
|
|
@@ -15446,6 +15450,12 @@ async function releaseAnnounceCommand(args) {
|
|
|
15446
15450
|
const channels = channelList.filter(
|
|
15447
15451
|
(c) => c === "blog" || c === "bluesky"
|
|
15448
15452
|
);
|
|
15453
|
+
if (channels.length === 0) {
|
|
15454
|
+
console.error(
|
|
15455
|
+
`${colors.red}Error: No known announcement channels in "${channelList.join(",")}" (known: blog, bluesky)${colors.reset}`
|
|
15456
|
+
);
|
|
15457
|
+
return 1;
|
|
15458
|
+
}
|
|
15449
15459
|
const result = await runReleaseAnnounce({
|
|
15450
15460
|
version,
|
|
15451
15461
|
channels,
|
|
@@ -15454,7 +15464,7 @@ async function releaseAnnounceCommand(args) {
|
|
|
15454
15464
|
...args.options.releaseUrl !== void 0 && { releaseUrl: args.options.releaseUrl }
|
|
15455
15465
|
});
|
|
15456
15466
|
printReleaseAnnounceResult(result, args.options.verbose);
|
|
15457
|
-
return result.channels
|
|
15467
|
+
return allOf(result.channels, (c) => c.success, false) ? 0 : 1;
|
|
15458
15468
|
}
|
|
15459
15469
|
|
|
15460
15470
|
// src/cli/scaffold.ts
|
|
@@ -22662,7 +22672,7 @@ var ScenarioRunner = class {
|
|
|
22662
22672
|
try {
|
|
22663
22673
|
const stepResults = await this.executeSteps(scenario, testConfig);
|
|
22664
22674
|
const validations = this.validateResults(stepResults, scenario.expectedOutputs);
|
|
22665
|
-
const passed = validations
|
|
22675
|
+
const passed = allOf(validations, (v) => v.passed, false);
|
|
22666
22676
|
const durationMs = getTimeProvider().now() - startTime;
|
|
22667
22677
|
this.log.info("Scenario completed", { scenarioId: scenario.id, passed, durationMs });
|
|
22668
22678
|
return {
|