UNPKG

@puls-atlas/cli

Version:

The Puls Atlas CLI tool for managing Atlas projects

315 lines 15.1 kB
import { logger } from '../../utils/logger.js'; import { normalizeOptionalString } from '../../utils/value.js'; import { createAtlasAiArtifacts } from './artifactsRuntime.js'; import { createSnapshotSelectedGuideSummary, createSnapshotSelectedSymbolSummary, createSnapshotSymbolKey, hasSemanticSnapshotSelectedSymbol } from './snapshotRuntime.js'; import { createResolveTokens } from './resolveTokens.js'; import { compareResolveCandidates, createResolveAmbiguity, createResolveConfidence, createResolvePackageCandidate, createResolvePresentationPackageCandidate, needsResolveNestedPrecisionDrillDown } from './resolveRanking.js'; const ATLAS_AI_RESOLVE_DEFAULT_LIMIT = 3; const ATLAS_AI_RESOLVE_DRILL_DOWN_MAX_PACKAGES = 2; const normalizeResolveLimit = value => { if (Number.isInteger(value) && value > 0) { return value; } const parsedValue = Number.parseInt(String(value ?? ''), 10); return Number.isInteger(parsedValue) && parsedValue > 0 ? parsedValue : ATLAS_AI_RESOLVE_DEFAULT_LIMIT; }; const createResolvePublicPackageCandidate = (candidate, explain = false) => ({ maturity: candidate.maturity, name: candidate.name, reasonCodes: candidate.reasonCodes, role: candidate.role, score: candidate.score, ...(candidate.topGuide?.id ? { topGuideId: candidate.topGuide.id } : {}), ...(candidate.topSymbol?.id ? { topSymbolId: candidate.topSymbol.id } : {}), ...(explain ? { reasons: candidate.reasons } : {}) }); const createResolvePublicGuideCandidate = (candidate, explain = false) => ({ id: candidate.id, packageName: candidate.packageName, reasonCodes: candidate.reasonCodes, score: candidate.score, ...(candidate.reviewStatus ? { reviewStatus: candidate.reviewStatus } : {}), ...(candidate.status ? { status: candidate.status } : {}), ...(candidate.summary ? { summary: candidate.summary } : {}), ...(candidate.title ? { title: candidate.title } : {}), ...(explain ? { reasons: candidate.reasons } : {}) }); const createResolvePublicSymbolCandidate = (candidate, explain = false) => ({ id: candidate.id, packageName: candidate.packageName, reasonCodes: candidate.reasonCodes, score: candidate.score, ...(candidate.kind ? { kind: candidate.kind } : {}), ...(candidate.name ? { name: candidate.name } : {}), ...(candidate.status ? { status: candidate.status } : {}), ...(candidate.summary ? { summary: candidate.summary } : {}), ...(explain ? { reasons: candidate.reasons } : {}) }); const collectLimitedResolveNestedCandidates = (packageCandidates, candidateKey, limit) => (Array.isArray(packageCandidates) ? packageCandidates : []).flatMap(packageCandidate => Array.isArray(packageCandidate?.[candidateKey]) ? packageCandidate[candidateKey] : []).slice(0, limit); const createResolveRequiresDrillDown = (confidence, ambiguity) => confidence === 'low' || ambiguity !== null; const createAtlasAiResolveRecommendedReads = (artifacts, packageCandidates, options = {}) => { const packageLimit = Number.isInteger(options.packageLimit) && options.packageLimit > 0 ? options.packageLimit : 1; const recommendedReads = []; const seenReadTargets = new Set(); const packageContextByName = new Map((Array.isArray(artifacts.packageContexts) ? artifacts.packageContexts : []).map(entry => [entry.name, entry])); const addReadTarget = (kind, path, packageName) => { const normalizedPath = normalizeOptionalString(path); if (!normalizedPath) { return; } const readKey = `${kind}:${packageName ?? ''}:${normalizedPath}`; if (seenReadTargets.has(readKey)) { return; } seenReadTargets.add(readKey); recommendedReads.push({ kind, ...(packageName ? { packageName } : {}), path: normalizedPath }); }; const topPackageCandidates = (Array.isArray(packageCandidates) ? packageCandidates : []).slice(0, packageLimit); if (topPackageCandidates.length === 0) { addReadTarget('consumer-snapshot', artifacts.outputPaths.relativeSnapshotPath, null); return recommendedReads; } addReadTarget('consumer-snapshot', artifacts.outputPaths.relativeSnapshotPath, topPackageCandidates[0].name); for (const packageCandidate of topPackageCandidates) { const packageContext = packageContextByName.get(packageCandidate.name); if (packageCandidate.topGuide?.id && packageContext?.guidesManifestPath) { addReadTarget('package-guides', packageContext.guidesManifestPath, packageCandidate.name); } if (packageContext?.manifestPath) { addReadTarget('package-context', packageContext.manifestPath, packageCandidate.name); } if (packageCandidate.topSymbol?.id && packageContext?.symbolsManifestPath) { addReadTarget('package-symbols', packageContext.symbolsManifestPath, packageCandidate.name); } if (!packageCandidate.topGuide?.id && packageContext?.guidesManifestPath) { addReadTarget('package-guides', packageContext.guidesManifestPath, packageCandidate.name); } if (!packageCandidate.topSymbol?.id && packageContext?.symbolsManifestPath) { addReadTarget('package-symbols', packageContext.symbolsManifestPath, packageCandidate.name); } } return recommendedReads; }; const createResolveDrillDownSelectedGuides = guidesArtifact => { if (!Array.isArray(guidesArtifact?.guides) || guidesArtifact.guides.length === 0) { return []; } return guidesArtifact.guides.map(createSnapshotSelectedGuideSummary).filter(guide => normalizeOptionalString(guide?.id) || normalizeOptionalString(guide?.title)); }; const createResolveDrillDownSelectedSymbols = symbolsArtifact => { if (!Array.isArray(symbolsArtifact?.entrypoints) || symbolsArtifact.entrypoints.length === 0) { return []; } const seenSymbolKeys = new Set(); const selectedSymbols = []; for (const entrypoint of symbolsArtifact.entrypoints) { const entrypointPath = normalizeOptionalString(entrypoint?.path) ?? ''; for (const symbol of Array.isArray(entrypoint?.symbols) ? entrypoint.symbols : []) { const selectedSymbol = createSnapshotSelectedSymbolSummary(symbol); if (!hasSemanticSnapshotSelectedSymbol(selectedSymbol)) { continue; } const symbolKey = createSnapshotSymbolKey(selectedSymbol, entrypointPath); if (!symbolKey || seenSymbolKeys.has(symbolKey)) { continue; } seenSymbolKeys.add(symbolKey); selectedSymbols.push(selectedSymbol); } } return selectedSymbols; }; const createResolveDrillDownPackageCandidate = (snapshotPackage, packageContext, queryTokens) => { const selectedGuides = createResolveDrillDownSelectedGuides(packageContext?.guides); const selectedSymbols = createResolveDrillDownSelectedSymbols(packageContext?.symbols); return createResolvePackageCandidate({ ...snapshotPackage, selectedGuides: selectedGuides.length > 0 ? selectedGuides : snapshotPackage?.selectedGuides, selectedSymbols: selectedSymbols.length > 0 ? selectedSymbols : snapshotPackage?.selectedSymbols }, queryTokens); }; const createResolveDrillDownPackageCandidates = (artifacts, packageCandidates, queryTokens, limit) => { const topPackageCandidates = (Array.isArray(packageCandidates) ? packageCandidates : []).slice(0, Math.min(limit, ATLAS_AI_RESOLVE_DRILL_DOWN_MAX_PACKAGES)); if (topPackageCandidates.length === 0) { return (Array.isArray(packageCandidates) ? packageCandidates : []).slice(0, limit); } const snapshotPackageByName = new Map((Array.isArray(artifacts.snapshotData?.packages) ? artifacts.snapshotData.packages : []).map(entry => [entry.name, entry])); const packageContextByName = new Map((Array.isArray(artifacts.packageContexts) ? artifacts.packageContexts : []).map(entry => [entry.name, entry])); const drilledCandidates = topPackageCandidates.map(packageCandidate => { const snapshotPackage = snapshotPackageByName.get(packageCandidate.name); const packageContext = packageContextByName.get(packageCandidate.name); if (!snapshotPackage || !packageContext) { return packageCandidate; } return createResolveDrillDownPackageCandidate(snapshotPackage, packageContext, queryTokens); }).sort((left, right) => compareResolveCandidates(left, right, left.name, right.name)); const drilledPackageNames = new Set(drilledCandidates.map(candidate => candidate.name)); return [...drilledCandidates, ...(Array.isArray(packageCandidates) ? packageCandidates : []).filter(candidate => !drilledPackageNames.has(candidate.name))].slice(0, limit); }; const createAtlasAiResolveResult = (artifacts, options = {}) => { const query = normalizeOptionalString(options.query); if (!query) { throw new Error('Atlas AI resolve requires a non-empty --query value.'); } const limit = normalizeResolveLimit(options.limit); const explain = options.explain === true; const normalizedScope = normalizeOptionalString(options.scope) ?? null; const queryTokens = createResolveTokens(query, normalizedScope); const packageCandidates = (Array.isArray(artifacts.snapshotData?.packages) ? artifacts.snapshotData.packages : []).map(snapshotPackage => createResolvePackageCandidate(snapshotPackage, queryTokens)).filter(candidate => candidate.score > 0).sort((left, right) => compareResolveCandidates(left, right, left.name, right.name)); if (packageCandidates.length === 0) { return { ambiguity: null, candidateGuides: [], candidatePackages: [], candidateSymbols: [], confidence: 'none', normalizedIntent: { scope: normalizedScope, tokens: queryTokens }, query, recommendedReads: createAtlasAiResolveRecommendedReads(artifacts, []), requiresDrillDown: false, status: 'no-match', warnings: ['No Atlas package matched the provided query from the snapshot-first context.'] }; } const initialPackageCandidates = packageCandidates.slice(0, limit); const initialAmbiguity = createResolveAmbiguity(initialPackageCandidates); const initialConfidence = createResolveConfidence(initialPackageCandidates); const needsPrecisionDrillDown = options.snapshotOnly !== true && needsResolveNestedPrecisionDrillDown(initialPackageCandidates[0] ?? null); const initialNeedsPackageDrillDown = createResolveRequiresDrillDown(initialConfidence, initialAmbiguity); const usedDrillDown = options.snapshotOnly !== true && (initialNeedsPackageDrillDown || needsPrecisionDrillDown); const limitedPackageCandidates = usedDrillDown ? createResolveDrillDownPackageCandidates(artifacts, packageCandidates, queryTokens, initialNeedsPackageDrillDown ? limit : 1) : initialPackageCandidates; const symbolCandidates = collectLimitedResolveNestedCandidates(limitedPackageCandidates, 'symbolCandidates', limit); const ambiguity = createResolveAmbiguity(limitedPackageCandidates); const confidence = createResolveConfidence(limitedPackageCandidates); const requiresDrillDown = usedDrillDown || createResolveRequiresDrillDown(confidence, ambiguity); const presentationPackageCandidates = limitedPackageCandidates.map(createResolvePresentationPackageCandidate); const warnings = []; if (ambiguity) { warnings.push(`Resolver found a close Atlas package tie between ${ambiguity.packageNames.join(' and ')}.`); } if (usedDrillDown) { warnings.push('Resolver used controlled drill-down into the most likely Atlas package artifacts because the snapshot-first match was ambiguous, low confidence, or needed more precise guide and symbol routing.'); } else if (requiresDrillDown) { warnings.push('Resolver recommends controlled drill-down into the most likely Atlas package artifacts before treating this result as final.'); } return { ambiguity, candidateGuides: collectLimitedResolveNestedCandidates(presentationPackageCandidates, 'guideCandidates', limit).map(candidate => createResolvePublicGuideCandidate(candidate, explain)), candidatePackages: presentationPackageCandidates.map(candidate => createResolvePublicPackageCandidate(candidate, explain)), candidateSymbols: symbolCandidates.map(candidate => createResolvePublicSymbolCandidate(candidate, explain)), confidence, normalizedIntent: { scope: normalizedScope, tokens: queryTokens }, query, recommendedReads: createAtlasAiResolveRecommendedReads(artifacts, presentationPackageCandidates, { packageLimit: requiresDrillDown ? ATLAS_AI_RESOLVE_DRILL_DOWN_MAX_PACKAGES : 1 }), requiresDrillDown, status: 'resolved', warnings }; }; const logAiResolveOutcome = (loggerImpl, result) => { loggerImpl.summary('Atlas AI resolve', [{ label: 'Query', value: result.query }, { label: 'Status', value: result.status }, { label: 'Confidence', value: result.confidence }, { label: 'Requires drill-down', value: result.requiresDrillDown ? 'yes' : 'no' }, { label: 'Suggested packages', value: result.candidatePackages.length }, { label: 'Suggested guides', value: result.candidateGuides.length }, { label: 'Suggested symbols', value: result.candidateSymbols.length }]); if (result.candidatePackages.length > 0) { loggerImpl.summary('Atlas AI package matches', result.candidatePackages.map(candidate => ({ label: candidate.name, value: `score ${candidate.score} | ` + `${candidate.maturity ?? 'unknown'} | ` + `${candidate.reasonCodes.join(', ')}` }))); } if (result.candidateGuides.length > 0) { loggerImpl.summary('Atlas AI guide matches', result.candidateGuides.map(candidate => ({ label: candidate.title ?? candidate.id, value: `score ${candidate.score} | ` + `${candidate.packageName} | ` + `${candidate.reasonCodes.join(', ')}` }))); } if (result.candidateSymbols.length > 0) { loggerImpl.summary('Atlas AI symbol matches', result.candidateSymbols.map(candidate => ({ label: candidate.name ?? candidate.id, value: `score ${candidate.score} | ` + `${candidate.packageName} | ` + `${candidate.reasonCodes.join(', ')}` }))); } for (const warning of result.warnings) { loggerImpl.warning(warning); } }; export const runAiResolve = async (options = {}, dependencies = {}, cwd = process.cwd()) => { const loggerImpl = dependencies.logger ?? logger; const { consoleImpl = console } = dependencies; let spinner; try { spinner = loggerImpl.spinner('Resolving Atlas AI task context...'); const artifacts = createAtlasAiArtifacts(options, dependencies, cwd, 'atlas ai resolve'); const result = createAtlasAiResolveResult(artifacts, options); spinner.succeed(result.status === 'resolved' ? 'Atlas AI resolve result is ready.' : 'No Atlas resolve match was found.'); logAiResolveOutcome(loggerImpl, result); if (options.json) { consoleImpl.log(JSON.stringify(result, null, 4)); } return result; } catch (error) { spinner?.fail('Failed to resolve Atlas AI task context.'); loggerImpl.error(error.message); return null; } };