@puls-atlas/cli
Version:
The Puls Atlas CLI tool for managing Atlas projects
315 lines • 15.1 kB
JavaScript
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;
}
};