config-audit/scanners/lib/string-utils.mjs
Kjell Tore Guttormsen caee558e79 feat(ultraplan-local): v1.6.0 — /ultraresearch-local deep research command
Add /ultraresearch-local for structured research combining local codebase
analysis with external knowledge via parallel agent swarms. Produces research
briefs with triangulation, confidence ratings, and source quality assessment.

New command: /ultraresearch-local with modes --quick, --local, --external, --fg.
New agents: research-orchestrator (opus), docs-researcher, community-researcher,
security-researcher, contrarian-researcher, gemini-bridge (all sonnet).
New template: research-brief-template.md.

Integration: --research flag in /ultraplan-local accepts pre-built research
briefs (up to 3), enriches the interview and exploration phases. Planning
orchestrator cross-references brief findings during synthesis.

Design principle: Context Engineering — right information to right agent at
right time. Research briefs are structured artifacts in the pipeline:
ultraresearch → brief → ultraplan --research → plan → ultraexecute.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 08:58:35 +02:00

74 lines
2 KiB
JavaScript

/**
* String utilities for config-audit scanners.
* Zero external dependencies.
*/
/**
* Count lines in a string.
* @param {string} s
* @returns {number}
*/
export function lineCount(s) {
if (!s) return 0;
return s.split('\n').length;
}
/**
* Truncate a string to maxLen chars with ellipsis.
* @param {string} s
* @param {number} [maxLen=100]
* @returns {string}
*/
export function truncate(s, maxLen = 100) {
if (!s || s.length <= maxLen) return s || '';
return s.slice(0, maxLen - 3) + '...';
}
/**
* Check if two strings have >threshold% content similarity (word overlap).
* @param {string} a
* @param {string} b
* @param {number} [threshold=0.8]
* @returns {boolean}
*/
export function isSimilar(a, b, threshold = 0.8) {
const wordsA = new Set(a.toLowerCase().split(/\s+/).filter(w => w.length > 2));
const wordsB = new Set(b.toLowerCase().split(/\s+/).filter(w => w.length > 2));
if (wordsA.size === 0 || wordsB.size === 0) return false;
let overlap = 0;
for (const w of wordsA) {
if (wordsB.has(w)) overlap++;
}
const similarity = overlap / Math.min(wordsA.size, wordsB.size);
return similarity >= threshold;
}
/**
* Extract all key-like patterns from a settings.json or similar config.
* @param {object} obj
* @param {string} [prefix='']
* @returns {string[]}
*/
export function extractKeys(obj, prefix = '') {
const keys = [];
for (const [key, value] of Object.entries(obj)) {
const fullKey = prefix ? `${prefix}.${key}` : key;
keys.push(fullKey);
if (value && typeof value === 'object' && !Array.isArray(value)) {
keys.push(...extractKeys(value, fullKey));
}
}
return keys;
}
/**
* Normalize a file path for comparison (resolve ~, handle trailing slashes).
* @param {string} p
* @returns {string}
*/
export function normalizePath(p) {
const home = process.env.HOME || process.env.USERPROFILE || '';
let normalized = p.replace(/^~/, home);
normalized = normalized.replace(/[/\\]+$/, '');
return normalized;
}