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>
This commit is contained in:
commit
4dc868ea62
151 changed files with 34374 additions and 0 deletions
120
hooks/scripts/personalization-score.mjs
Normal file
120
hooks/scripts/personalization-score.mjs
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
#!/usr/bin/env node
|
||||
// Personalization score calculator for linkedin-thought-leadership plugin
|
||||
// Checks 8 asset categories for real user data vs placeholder templates
|
||||
// Standalone: outputs SCORE:N|M/8 assets personalized
|
||||
// Import: export function calculateScore(pluginRoot) => { score, personalized, categories }
|
||||
|
||||
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
||||
import { join, basename, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export function calculateScore(pluginRoot) {
|
||||
let score = 0;
|
||||
let personalized = 0;
|
||||
const categories = 8;
|
||||
|
||||
// --- 1. Voice samples (25 points) ---
|
||||
const voiceFile = join(pluginRoot, 'assets', 'voice-samples', 'authentic-voice-samples.md');
|
||||
if (existsSync(voiceFile)) {
|
||||
const content = readFileSync(voiceFile, 'utf-8');
|
||||
const lineCount = content.split('\n').length;
|
||||
if (lineCount > 50 && !content.includes('[Your Name]')) {
|
||||
score += 25;
|
||||
personalized += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 2. User profile (20 points) ---
|
||||
const profileFile = join(pluginRoot, 'config', 'user-profile.local.md');
|
||||
if (existsSync(profileFile)) {
|
||||
const content = readFileSync(profileFile, 'utf-8');
|
||||
const placeholderCount = (content.match(/\[Your /g) || []).length;
|
||||
if (placeholderCount < 3) {
|
||||
score += 20;
|
||||
personalized += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 3. Case studies (15 points) ---
|
||||
const caseDir = join(pluginRoot, 'assets', 'case-studies');
|
||||
if (existsSync(caseDir)) {
|
||||
let realCases = 0;
|
||||
try {
|
||||
for (const f of readdirSync(caseDir)) {
|
||||
if (!f.endsWith('.md')) continue;
|
||||
if (f === 'case-study-template.md') continue;
|
||||
realCases++;
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
if (realCases >= 2) { score += 15; personalized += 1; }
|
||||
else if (realCases >= 1) { score += 8; }
|
||||
}
|
||||
|
||||
// --- 4. Frameworks (10 points) ---
|
||||
const fwDir = join(pluginRoot, 'assets', 'frameworks');
|
||||
if (existsSync(fwDir)) {
|
||||
let realFw = 0;
|
||||
try {
|
||||
for (const f of readdirSync(fwDir)) {
|
||||
if (!f.endsWith('.md')) continue;
|
||||
if (f === 'framework-template.md') continue;
|
||||
realFw++;
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
if (realFw >= 2) { score += 10; personalized += 1; }
|
||||
else if (realFw >= 1) { score += 5; }
|
||||
}
|
||||
|
||||
// --- 5. High-engagement posts (10 points) ---
|
||||
const postsFile = join(pluginRoot, 'assets', 'examples', 'high-engagement-posts.md');
|
||||
if (existsSync(postsFile)) {
|
||||
const content = readFileSync(postsFile, 'utf-8');
|
||||
const postCount = (content.match(/^## Post [0-9]/gm) || []).length;
|
||||
if (postCount >= 3) { score += 10; personalized += 1; }
|
||||
else if (postCount >= 1) { score += 4; }
|
||||
}
|
||||
|
||||
// --- 6. Demographics (8 points) ---
|
||||
const demoFile = join(pluginRoot, 'assets', 'audience-insights', 'demographics.md');
|
||||
if (existsSync(demoFile)) {
|
||||
const content = readFileSync(demoFile, 'utf-8');
|
||||
const placeholderCount = (content.match(/\[Industry name\]|\[Function\]|\[Country\]|\[X\]%/g) || []).length;
|
||||
if (placeholderCount < 5) {
|
||||
score += 8;
|
||||
personalized += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 7. Engagement patterns (7 points) ---
|
||||
const patternsFile = join(pluginRoot, 'assets', 'audience-insights', 'engagement-patterns.md');
|
||||
if (existsSync(patternsFile)) {
|
||||
const content = readFileSync(patternsFile, 'utf-8');
|
||||
const placeholderCount = (content.match(/\[Day\]|\[Time\]|\[Topic\]|\[Format\]|\[Hook type\]/g) || []).length;
|
||||
if (placeholderCount < 5) {
|
||||
score += 7;
|
||||
personalized += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 8. Post templates (5 points) ---
|
||||
const templatesFile = join(pluginRoot, 'assets', 'templates', 'my-post-templates.md');
|
||||
if (existsSync(templatesFile)) {
|
||||
const content = readFileSync(templatesFile, 'utf-8');
|
||||
const unfilled = (content.match(/\[Name - e\.g\./g) || []).length;
|
||||
const totalTemplates = (content.match(/^## Template [0-9]/gm) || []).length;
|
||||
const filled = totalTemplates - unfilled;
|
||||
if (filled >= 2) { score += 5; personalized += 1; }
|
||||
else if (filled >= 1) { score += 2; }
|
||||
}
|
||||
|
||||
return { score, personalized, categories };
|
||||
}
|
||||
|
||||
// Standalone execution (guarded to prevent stdout contamination on import)
|
||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
const pluginRoot = join(__dirname, '..', '..');
|
||||
const { score, personalized, categories } = calculateScore(pluginRoot);
|
||||
process.stdout.write(`SCORE:${score}|${personalized}/${categories} assets personalized\n`);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue