aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
147 lines (124 loc) • 4.32 kB
JavaScript
;
const { getEnricher, getClient, isCodeIntelAvailable } = require('../index');
/**
* StoryHelper — Code intelligence helper for @sm/@po agent tasks.
*
* All functions return null gracefully when no provider is available.
* Never throws — safe to call unconditionally in task workflows.
*
* Functions:
* - detectDuplicateStory(description) — for @sm story creation (advisory warning)
* - suggestRelevantFiles(description) — for @sm story creation (file suggestions)
* - validateNoDuplicates(description) — for @po story validation (checklist boolean)
*/
/**
* Detect duplicate stories/functionality in the codebase.
* Used by @sm during story creation — returns advisory warning only, never blocks.
*
* @param {string} description - Story description to check for duplicates
* @returns {Promise<{matches: Array, warning: string}|null>} Duplicate info or null
*/
async function detectDuplicateStory(description) {
if (!description) return null;
if (!isCodeIntelAvailable()) return null;
try {
const enricher = getEnricher();
const result = await enricher.detectDuplicates(description, { path: '.' });
if (!result || !result.matches || result.matches.length === 0) return null;
return {
matches: result.matches,
warning: _formatDuplicateWarning(result.matches),
};
} catch {
return null;
}
}
/**
* Suggest relevant files for a new story based on description.
* Composes findReferences + analyzeCodebase for comprehensive file suggestions.
* Used by @sm during story creation to pre-populate "Suggested Files" section.
*
* @param {string} description - Story description to find relevant files for
* @returns {Promise<{files: Array, codebaseContext: Object|null}|null>} File suggestions or null
*/
async function suggestRelevantFiles(description) {
if (!description) return null;
if (!isCodeIntelAvailable()) return null;
try {
const client = getClient();
// Per-capability try/catch — partial results accepted
let files = null;
let codebaseContext = null;
try {
const refs = await client.findReferences(description);
if (refs) {
files = refs;
}
} catch { /* skip — partial result ok */ }
try {
const analysis = await client.analyzeCodebase('.');
if (analysis) {
codebaseContext = analysis;
}
} catch { /* skip — partial result ok */ }
// Return null only if we got nothing at all
if (!files && !codebaseContext) return null;
return {
files: files || [],
codebaseContext,
};
} catch {
return null;
}
}
/**
* Validate that a story description does not duplicate existing functionality.
* Used by @po during story validation — returns boolean for checklist item.
*
* @param {string} description - Story description to validate
* @returns {Promise<{hasDuplicates: boolean, matches: Array, suggestion: string|null}|null>} Validation result or null
*/
async function validateNoDuplicates(description) {
if (!description) return null;
if (!isCodeIntelAvailable()) return null;
try {
const enricher = getEnricher();
const result = await enricher.detectDuplicates(description, { path: '.' });
if (!result) {
return {
hasDuplicates: false,
matches: [],
suggestion: null,
};
}
const hasDuplicates = result.matches && result.matches.length > 0;
return {
hasDuplicates,
matches: result.matches || [],
suggestion: hasDuplicates ? 'Consider ADAPT instead of CREATE — similar functionality exists' : null,
};
} catch {
return null;
}
}
/**
* Format a human-readable warning message from duplicate matches.
* @param {Array} matches - Array of duplicate match objects
* @returns {string} Formatted warning message
* @private
*/
function _formatDuplicateWarning(matches) {
if (!matches || matches.length === 0) return '';
const fileList = matches
.map((m) => m.file || m.path || 'unknown')
.slice(0, 5)
.join(', ');
return `Similar functionality already exists in: ${fileList}. Consider ADAPT instead of CREATE.`;
}
module.exports = {
detectDuplicateStory,
suggestRelevantFiles,
validateNoDuplicates,
// Exposed for testing
_formatDuplicateWarning,
};