diff --git a/packages/core/src/analysis/aggregate.ts b/packages/core/src/analysis/aggregate.ts index 1c3fbac..2087622 100644 --- a/packages/core/src/analysis/aggregate.ts +++ b/packages/core/src/analysis/aggregate.ts @@ -1,10 +1,6 @@ import type { CoreAnalysis, CoreAnalysisInput } from '../types/core.js'; import type { PluginResult } from '../types/plugin.js'; -import { firstNonEmptyLine } from '../utils/text.js'; - -function unique(values: string[]): string[] { - return [...new Set(values.filter((value) => value.trim().length > 0))]; -} +import { firstNonEmptyLine, uniqueNonEmptyStrings } from '../utils/text.js'; function pickLeadResult( pluginResults: PluginResult[], @@ -50,7 +46,7 @@ function buildLikelyCauses( ); } - return unique(causes).slice(0, 5); + return uniqueNonEmptyStrings(causes).slice(0, 5); } function buildNextSteps( @@ -70,7 +66,10 @@ function buildNextSteps( : 'Re-run the command with more verbose logging if the failure is still ambiguous.', ]; - return unique([...pluginSuggestions, ...genericSteps]).slice(0, 6); + return uniqueNonEmptyStrings([...pluginSuggestions, ...genericSteps]).slice( + 0, + 6, + ); } export function aggregateCoreAnalysis( @@ -85,7 +84,7 @@ export function aggregateCoreAnalysis( keySnippet: leadResult?.keySnippet ?? input.signals.snippet, likelyCauses: buildLikelyCauses(input, pluginResults), nextSteps: buildNextSteps(input, pluginResults), - relatedFiles: unique([ + relatedFiles: uniqueNonEmptyStrings([ ...input.signals.relatedFiles, ...pluginResults.flatMap((result) => result.relatedFiles ?? []), ]).slice(0, 10), diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 132f692..88483e1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -49,5 +49,12 @@ export { } from './utils/fs.js'; export { shortHash } from './utils/hash.js'; export { toJson } from './utils/json.js'; +export { + firstNonEmptyLine, + normalizeDiagnosticMessage, + readAnalysisLogText, + unique, + uniqueNonEmptyStrings, +} from './utils/text.js'; export type { E2FPaths } from './utils/paths.js'; export { getE2FPaths } from './utils/paths.js'; diff --git a/packages/core/src/parser/extract.ts b/packages/core/src/parser/extract.ts index bb62d13..74d5307 100644 --- a/packages/core/src/parser/extract.ts +++ b/packages/core/src/parser/extract.ts @@ -1,3 +1,4 @@ +import { unique } from '../utils/text.js'; import type { CandidateSnippet, ExtractedSignals, @@ -10,10 +11,6 @@ const FILE_PATTERN = const GENERIC_PATH_PATTERN = /(?:^|[\s("'`])((?:\.{0,2}\/)?(?:[\w@%+=:,.-]+\/)+[\w@%+=,.-]+)(?:[:(]\d+(?::\d+)?)?/g; -function unique(values: T[]): T[] { - return [...new Set(values)]; -} - function scoreSegment(segment: LogSegment): number { const text = segment.text; let score = 0; diff --git a/packages/core/src/utils/text.ts b/packages/core/src/utils/text.ts index 63cec42..67eb9dc 100644 --- a/packages/core/src/utils/text.ts +++ b/packages/core/src/utils/text.ts @@ -25,3 +25,7 @@ export function readAnalysisLogText(input: CoreAnalysisInput): string { export function unique(values: T[]): T[] { return [...new Set(values)]; } + +export function uniqueNonEmptyStrings(values: string[]): string[] { + return unique(values.filter((value) => value.trim().length > 0)); +} diff --git a/packages/mcp/src/tools/tool-utils.ts b/packages/mcp/src/tools/tool-utils.ts index 6cbee47..568bda9 100644 --- a/packages/mcp/src/tools/tool-utils.ts +++ b/packages/mcp/src/tools/tool-utils.ts @@ -1,3 +1,7 @@ +import { uniqueNonEmptyStrings } from '@error2fix/core'; + +export const unique = uniqueNonEmptyStrings; + export function makeToolText(result: { ok: boolean; error?: { message: string }; @@ -25,10 +29,6 @@ export function truncate( return `${text.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`; } -export function unique(values: string[]): string[] { - return [...new Set(values.filter((value) => value.trim().length > 0))]; -} - function scoreFailureExcerpt( excerpt: string, relatedFiles: string[],