suparank 1.2.2 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/suparank.js +177 -2
- package/mcp-client.js +120 -11
- package/package.json +1 -1
package/bin/suparank.js
CHANGED
|
@@ -23,6 +23,8 @@ const SUPARANK_DIR = path.join(os.homedir(), '.suparank')
|
|
|
23
23
|
const CONFIG_FILE = path.join(SUPARANK_DIR, 'config.json')
|
|
24
24
|
const CREDENTIALS_FILE = path.join(SUPARANK_DIR, 'credentials.json')
|
|
25
25
|
const SESSION_FILE = path.join(SUPARANK_DIR, 'session.json')
|
|
26
|
+
const CONTENT_DIR = path.join(SUPARANK_DIR, 'content')
|
|
27
|
+
const STATS_FILE = path.join(SUPARANK_DIR, 'stats.json')
|
|
26
28
|
|
|
27
29
|
// Production API URL
|
|
28
30
|
const DEFAULT_API_URL = 'https://api.suparank.io'
|
|
@@ -514,6 +516,165 @@ function showVersion() {
|
|
|
514
516
|
log('https://suparank.io', 'dim')
|
|
515
517
|
}
|
|
516
518
|
|
|
519
|
+
function loadStats() {
|
|
520
|
+
try {
|
|
521
|
+
if (fs.existsSync(STATS_FILE)) {
|
|
522
|
+
return JSON.parse(fs.readFileSync(STATS_FILE, 'utf-8'))
|
|
523
|
+
}
|
|
524
|
+
} catch (e) {}
|
|
525
|
+
return { tool_calls: 0, images_generated: 0, articles_created: 0, words_written: 0 }
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function loadSession() {
|
|
529
|
+
try {
|
|
530
|
+
if (fs.existsSync(SESSION_FILE)) {
|
|
531
|
+
return JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'))
|
|
532
|
+
}
|
|
533
|
+
} catch (e) {}
|
|
534
|
+
return null
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function countSavedContent() {
|
|
538
|
+
try {
|
|
539
|
+
if (fs.existsSync(CONTENT_DIR)) {
|
|
540
|
+
const folders = fs.readdirSync(CONTENT_DIR).filter(f => {
|
|
541
|
+
const stat = fs.statSync(path.join(CONTENT_DIR, f))
|
|
542
|
+
return stat.isDirectory()
|
|
543
|
+
})
|
|
544
|
+
return folders.length
|
|
545
|
+
}
|
|
546
|
+
} catch (e) {}
|
|
547
|
+
return 0
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function getRecentContent(limit = 3) {
|
|
551
|
+
try {
|
|
552
|
+
if (fs.existsSync(CONTENT_DIR)) {
|
|
553
|
+
const folders = fs.readdirSync(CONTENT_DIR)
|
|
554
|
+
.filter(f => fs.statSync(path.join(CONTENT_DIR, f)).isDirectory())
|
|
555
|
+
.map(f => {
|
|
556
|
+
const metaPath = path.join(CONTENT_DIR, f, 'metadata.json')
|
|
557
|
+
let meta = { title: f }
|
|
558
|
+
try {
|
|
559
|
+
if (fs.existsSync(metaPath)) {
|
|
560
|
+
meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'))
|
|
561
|
+
}
|
|
562
|
+
} catch (e) {}
|
|
563
|
+
return { folder: f, ...meta }
|
|
564
|
+
})
|
|
565
|
+
.sort((a, b) => (b.savedAt || '').localeCompare(a.savedAt || ''))
|
|
566
|
+
.slice(0, limit)
|
|
567
|
+
return folders
|
|
568
|
+
}
|
|
569
|
+
} catch (e) {}
|
|
570
|
+
return []
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async function displayDashboard(config, project) {
|
|
574
|
+
const packageJson = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'))
|
|
575
|
+
const credentials = loadCredentials()
|
|
576
|
+
const session = loadSession()
|
|
577
|
+
const stats = loadStats()
|
|
578
|
+
const savedCount = countSavedContent()
|
|
579
|
+
const recentContent = getRecentContent(3)
|
|
580
|
+
const projectConfig = project?.config || {}
|
|
581
|
+
|
|
582
|
+
// Header
|
|
583
|
+
console.log()
|
|
584
|
+
log('╔══════════════════════════════════════════════════════════════════════════════╗', 'cyan')
|
|
585
|
+
log('║ 🚀 SUPARANK MCP SERVER ║', 'cyan')
|
|
586
|
+
log('╚══════════════════════════════════════════════════════════════════════════════╝', 'cyan')
|
|
587
|
+
console.log()
|
|
588
|
+
|
|
589
|
+
// Version and project info
|
|
590
|
+
log(` Version: ${packageJson.version}`, 'dim')
|
|
591
|
+
log(` Project: ${colors.bright}${project?.name || config.project_slug}${colors.reset}`, 'reset')
|
|
592
|
+
log(` URL: ${projectConfig.site?.url || 'Not set'}`, 'dim')
|
|
593
|
+
console.log()
|
|
594
|
+
|
|
595
|
+
// Project Settings Box
|
|
596
|
+
log('┌─────────────────────────────────────────────────────────────────────────────┐', 'yellow')
|
|
597
|
+
log('│ 📋 PROJECT SETTINGS (from Supabase) │', 'yellow')
|
|
598
|
+
log('├─────────────────────────────────────────────────────────────────────────────┤', 'yellow')
|
|
599
|
+
log(`│ Word Count: ${String(projectConfig.content?.default_word_count || 'Not set').padEnd(15)} │ Brand Voice: ${String(projectConfig.brand?.voice || 'Not set').substring(0, 25).padEnd(25)}│`, 'reset')
|
|
600
|
+
log(`│ Reading Level: ${String(projectConfig.content?.reading_level ? `Grade ${projectConfig.content.reading_level}` : 'Not set').padEnd(15)} │ Target: ${String(projectConfig.brand?.target_audience || 'Not set').substring(0, 25).padEnd(25)}│`, 'reset')
|
|
601
|
+
log(`│ Include Images: ${String(projectConfig.content?.include_images ? 'Yes' : 'No').padEnd(15)} │ Niche: ${String(projectConfig.site?.niche || 'Not set').substring(0, 25).padEnd(25)}│`, 'reset')
|
|
602
|
+
log(`│ Keywords: ${String((projectConfig.seo?.primary_keywords || []).slice(0, 3).join(', ') || 'Not set').substring(0, 56).padEnd(56)}│`, 'reset')
|
|
603
|
+
log('└─────────────────────────────────────────────────────────────────────────────┘', 'yellow')
|
|
604
|
+
console.log()
|
|
605
|
+
|
|
606
|
+
// Integrations Status
|
|
607
|
+
log('┌─────────────────────────────────────────────────────────────────────────────┐', 'green')
|
|
608
|
+
log('│ 🔌 INTEGRATIONS │', 'green')
|
|
609
|
+
log('├─────────────────────────────────────────────────────────────────────────────┤', 'green')
|
|
610
|
+
|
|
611
|
+
const wpStatus = credentials.wordpress?.secret_key || credentials.wordpress?.app_password ? '✅ Enabled' : '❌ Not configured'
|
|
612
|
+
const ghostStatus = credentials.ghost?.admin_api_key ? '✅ Enabled' : '❌ Not configured'
|
|
613
|
+
const imageStatus = credentials[credentials.image_provider]?.api_key ? `✅ ${credentials.image_provider}` : '❌ Not configured'
|
|
614
|
+
const webhookStatus = credentials.webhooks && Object.values(credentials.webhooks).some(Boolean) ? '✅ Enabled' : '❌ Not configured'
|
|
615
|
+
const externalMcps = credentials.external_mcps?.length || 0
|
|
616
|
+
|
|
617
|
+
log(`│ WordPress: ${wpStatus.padEnd(20)} │ Ghost CMS: ${ghostStatus.padEnd(20)}│`, 'reset')
|
|
618
|
+
log(`│ Image Gen: ${imageStatus.padEnd(20)} │ Webhooks: ${webhookStatus.padEnd(20)}│`, 'reset')
|
|
619
|
+
log(`│ External MCPs: ${String(externalMcps > 0 ? `✅ ${externalMcps} configured` : '❌ None').padEnd(58)}│`, 'reset')
|
|
620
|
+
log('└─────────────────────────────────────────────────────────────────────────────┘', 'green')
|
|
621
|
+
console.log()
|
|
622
|
+
|
|
623
|
+
// Session Status
|
|
624
|
+
log('┌─────────────────────────────────────────────────────────────────────────────┐', 'magenta')
|
|
625
|
+
log('│ 📝 CURRENT SESSION │', 'magenta')
|
|
626
|
+
log('├─────────────────────────────────────────────────────────────────────────────┤', 'magenta')
|
|
627
|
+
|
|
628
|
+
if (session && session.articles?.length > 0) {
|
|
629
|
+
const totalWords = session.articles.reduce((sum, a) => sum + (a.wordCount || 0), 0)
|
|
630
|
+
const unpublished = session.articles.filter(a => !a.published).length
|
|
631
|
+
log(`│ Articles: ${String(session.articles.length).padEnd(5)} │ Words: ${String(totalWords).padEnd(8)} │ Unpublished: ${String(unpublished).padEnd(14)}│`, 'reset')
|
|
632
|
+
|
|
633
|
+
if (session.articles.length > 0) {
|
|
634
|
+
const latest = session.articles[session.articles.length - 1]
|
|
635
|
+
log(`│ Latest: ${String(`"${latest.title?.substring(0, 45) || 'Untitled'}..."`).padEnd(63)}│`, 'reset')
|
|
636
|
+
}
|
|
637
|
+
} else {
|
|
638
|
+
log(`│ No active session - Start with: "Create a blog post about [topic]" │`, 'dim')
|
|
639
|
+
}
|
|
640
|
+
log('└─────────────────────────────────────────────────────────────────────────────┘', 'magenta')
|
|
641
|
+
console.log()
|
|
642
|
+
|
|
643
|
+
// Recent Content
|
|
644
|
+
if (recentContent.length > 0) {
|
|
645
|
+
log('┌─────────────────────────────────────────────────────────────────────────────┐', 'blue')
|
|
646
|
+
log('│ 📚 RECENT CONTENT │', 'blue')
|
|
647
|
+
log('├─────────────────────────────────────────────────────────────────────────────┤', 'blue')
|
|
648
|
+
recentContent.forEach((content, i) => {
|
|
649
|
+
const title = (content.title || content.folder).substring(0, 50)
|
|
650
|
+
const words = content.wordCount || '?'
|
|
651
|
+
const date = content.savedAt ? new Date(content.savedAt).toLocaleDateString() : '?'
|
|
652
|
+
log(`│ ${i + 1}. ${title.padEnd(50)} ${String(words + ' words').padEnd(12)} ${date.padEnd(10)}│`, 'reset')
|
|
653
|
+
})
|
|
654
|
+
log(`│ Total saved: ${String(savedCount + ' articles').padEnd(60)}│`, 'dim')
|
|
655
|
+
log('└─────────────────────────────────────────────────────────────────────────────┘', 'blue')
|
|
656
|
+
console.log()
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Stats (if available)
|
|
660
|
+
if (stats.tool_calls > 0 || stats.articles_created > 0) {
|
|
661
|
+
log('┌─────────────────────────────────────────────────────────────────────────────┐', 'cyan')
|
|
662
|
+
log('│ 📊 USAGE STATS │', 'cyan')
|
|
663
|
+
log('├─────────────────────────────────────────────────────────────────────────────┤', 'cyan')
|
|
664
|
+
log(`│ Tool Calls: ${String(stats.tool_calls).padEnd(10)} │ Articles: ${String(stats.articles_created).padEnd(10)} │ Images: ${String(stats.images_generated).padEnd(10)}│`, 'reset')
|
|
665
|
+
log(`│ Words Written: ${String(stats.words_written?.toLocaleString() || 0).padEnd(58)}│`, 'reset')
|
|
666
|
+
log('└─────────────────────────────────────────────────────────────────────────────┘', 'cyan')
|
|
667
|
+
console.log()
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// Ready message
|
|
671
|
+
log('─────────────────────────────────────────────────────────────────────────────', 'dim')
|
|
672
|
+
log(' MCP Server ready. Waiting for AI client connection...', 'green')
|
|
673
|
+
log(' Tip: Say "Create a blog post about [topic]" to start', 'dim')
|
|
674
|
+
log('─────────────────────────────────────────────────────────────────────────────', 'dim')
|
|
675
|
+
console.log()
|
|
676
|
+
}
|
|
677
|
+
|
|
517
678
|
async function checkForUpdates(showCurrent = false) {
|
|
518
679
|
const packageJson = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'))
|
|
519
680
|
const currentVersion = packageJson.version
|
|
@@ -590,16 +751,30 @@ async function runUpdate() {
|
|
|
590
751
|
}
|
|
591
752
|
}
|
|
592
753
|
|
|
593
|
-
function runMCP() {
|
|
754
|
+
async function runMCP() {
|
|
594
755
|
const config = loadConfig()
|
|
595
756
|
|
|
596
757
|
if (!config) {
|
|
597
758
|
log('No configuration found. Running setup...', 'yellow')
|
|
598
759
|
console.log()
|
|
599
|
-
runSetup()
|
|
760
|
+
await runSetup()
|
|
600
761
|
return
|
|
601
762
|
}
|
|
602
763
|
|
|
764
|
+
// Fetch project data for dashboard
|
|
765
|
+
let project = null
|
|
766
|
+
try {
|
|
767
|
+
const result = await testConnection(config.api_key, config.project_slug, config.api_url)
|
|
768
|
+
if (result.success) {
|
|
769
|
+
project = result.project
|
|
770
|
+
}
|
|
771
|
+
} catch (e) {
|
|
772
|
+
// Continue without project data
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Display dashboard
|
|
776
|
+
await displayDashboard(config, project)
|
|
777
|
+
|
|
603
778
|
// Find the MCP client script
|
|
604
779
|
const mcpClientPaths = [
|
|
605
780
|
path.join(import.meta.dirname, '..', 'mcp-client.js'),
|
package/mcp-client.js
CHANGED
|
@@ -144,6 +144,47 @@ function ensureContentDir() {
|
|
|
144
144
|
return dir
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Get the path to the stats file (~/.suparank/stats.json)
|
|
149
|
+
*/
|
|
150
|
+
function getStatsFile() {
|
|
151
|
+
return path.join(getSuparankDir(), 'stats.json')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Load usage stats
|
|
156
|
+
*/
|
|
157
|
+
function loadStats() {
|
|
158
|
+
try {
|
|
159
|
+
const file = getStatsFile()
|
|
160
|
+
if (fs.existsSync(file)) {
|
|
161
|
+
return JSON.parse(fs.readFileSync(file, 'utf-8'))
|
|
162
|
+
}
|
|
163
|
+
} catch (e) {}
|
|
164
|
+
return { tool_calls: 0, images_generated: 0, articles_created: 0, words_written: 0 }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Save usage stats
|
|
169
|
+
*/
|
|
170
|
+
function saveStats(stats) {
|
|
171
|
+
try {
|
|
172
|
+
ensureSuparankDir()
|
|
173
|
+
fs.writeFileSync(getStatsFile(), JSON.stringify(stats, null, 2))
|
|
174
|
+
} catch (e) {
|
|
175
|
+
log(`Error saving stats: ${e.message}`)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Increment a stat counter
|
|
181
|
+
*/
|
|
182
|
+
function incrementStat(key, amount = 1) {
|
|
183
|
+
const stats = loadStats()
|
|
184
|
+
stats[key] = (stats[key] || 0) + amount
|
|
185
|
+
saveStats(stats)
|
|
186
|
+
}
|
|
187
|
+
|
|
147
188
|
/**
|
|
148
189
|
* Generate a slug from title for folder naming
|
|
149
190
|
*/
|
|
@@ -1368,6 +1409,21 @@ function buildWorkflowPlan(request, count, publishTo, withImages, project) {
|
|
|
1368
1409
|
|
|
1369
1410
|
// Extract all settings from project.config (database schema)
|
|
1370
1411
|
const targetWordCount = config.content?.default_word_count
|
|
1412
|
+
|
|
1413
|
+
// LOG ALL CONFIG VALUES FOR DEBUGGING
|
|
1414
|
+
log('=== PROJECT CONFIG VALUES ===')
|
|
1415
|
+
log(`Word Count Target: ${targetWordCount}`)
|
|
1416
|
+
log(`Reading Level: ${config.content?.reading_level}`)
|
|
1417
|
+
log(`Brand Voice: ${config.brand?.voice}`)
|
|
1418
|
+
log(`Target Audience: ${config.brand?.target_audience}`)
|
|
1419
|
+
log(`Primary Keywords: ${config.seo?.primary_keywords?.join(', ')}`)
|
|
1420
|
+
log(`Include Images: ${config.content?.include_images}`)
|
|
1421
|
+
log('=============================')
|
|
1422
|
+
|
|
1423
|
+
// CRITICAL: Validate word count is set
|
|
1424
|
+
if (!targetWordCount || targetWordCount < 100) {
|
|
1425
|
+
log(`WARNING: Word count not properly set! Got: ${targetWordCount}`)
|
|
1426
|
+
}
|
|
1371
1427
|
const readingLevel = config.content?.reading_level
|
|
1372
1428
|
const includeImages = config.content?.include_images
|
|
1373
1429
|
const brandVoice = config.brand?.voice
|
|
@@ -1573,27 +1629,40 @@ Use format: [IMAGE: description of what image should show]` : '**Note:** Images
|
|
|
1573
1629
|
action: 'content_write',
|
|
1574
1630
|
instruction: `Write the COMPLETE article following your outline.
|
|
1575
1631
|
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1632
|
+
╔══════════════════════════════════════════════════════════════════╗
|
|
1633
|
+
║ 🚨 MANDATORY WORD COUNT: ${targetWordCount} WORDS MINIMUM 🚨 ║
|
|
1634
|
+
║ This is a strict requirement from the project settings. ║
|
|
1635
|
+
║ The article will be REJECTED if under ${targetWordCount} words. ║
|
|
1636
|
+
╚══════════════════════════════════════════════════════════════════╝
|
|
1637
|
+
|
|
1638
|
+
**Project Requirements (from Supabase database - DO NOT IGNORE):**
|
|
1639
|
+
- Word count: **${targetWordCount} words** (MINIMUM - not a suggestion!)
|
|
1640
|
+
- Reading level: **${readingLevelDisplay}** - Simple sentences, short paragraphs
|
|
1579
1641
|
- Brand voice: ${brandVoice}
|
|
1580
1642
|
- Target audience: ${targetAudience || 'General readers'}
|
|
1581
1643
|
|
|
1644
|
+
**To reach ${targetWordCount} words, you MUST:**
|
|
1645
|
+
- Write 8-10 substantial H2 sections (each 200-400 words)
|
|
1646
|
+
- Include detailed examples, statistics, and actionable advice
|
|
1647
|
+
- Add comprehensive FAQ section (5-8 questions)
|
|
1648
|
+
- Expand each point with thorough explanations
|
|
1649
|
+
|
|
1582
1650
|
**Content Structure:**
|
|
1583
1651
|
- Engaging hook in first 2 sentences
|
|
1584
|
-
- All H2/H3 sections from your outline
|
|
1585
|
-
- Statistics, examples, and actionable tips in
|
|
1652
|
+
- All H2/H3 sections from your outline (expand each thoroughly!)
|
|
1653
|
+
- Statistics, examples, and actionable tips in EVERY section
|
|
1586
1654
|
${shouldGenerateImages ? '- Image placeholders: [IMAGE: description] where images should go' : ''}
|
|
1587
|
-
- FAQ section with
|
|
1655
|
+
- FAQ section with 5-8 Q&As (detailed answers, not one-liners)
|
|
1588
1656
|
- Strong conclusion with clear CTA
|
|
1589
1657
|
|
|
1590
|
-
**After writing, call 'save_content' with:**
|
|
1658
|
+
**After writing ${targetWordCount}+ words, call 'save_content' with:**
|
|
1591
1659
|
- title: Your SEO-optimized title
|
|
1592
1660
|
- content: The full article (markdown)
|
|
1593
1661
|
- keywords: Array of target keywords
|
|
1594
1662
|
- meta_description: Your 150-160 char meta description
|
|
1595
1663
|
|
|
1596
|
-
|
|
1664
|
+
⛔ STOP! Before calling save_content, verify you have ${targetWordCount}+ words.
|
|
1665
|
+
Count the words. If under ${targetWordCount}, ADD MORE CONTENT.`,
|
|
1597
1666
|
store: 'article'
|
|
1598
1667
|
})
|
|
1599
1668
|
|
|
@@ -1804,6 +1873,13 @@ async function executeOrchestratorTool(toolName, args, project) {
|
|
|
1804
1873
|
|
|
1805
1874
|
let response = `# 🚀 Content Creation Workflow Started
|
|
1806
1875
|
|
|
1876
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
1877
|
+
║ 📊 PROJECT REQUIREMENTS (from Supabase database) ║
|
|
1878
|
+
║ Word Count: ${String(plan.settings.target_word_count).padEnd(6)} words (MINIMUM - strictly enforced!) ║
|
|
1879
|
+
║ Brand Voice: ${String(plan.settings.brand_voice || 'Not set').substring(0, 50).padEnd(50)} ║
|
|
1880
|
+
║ Target Audience: ${String(plan.settings.target_audience || 'Not set').substring(0, 45).padEnd(45)} ║
|
|
1881
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
1882
|
+
|
|
1807
1883
|
## Your Request
|
|
1808
1884
|
"${plan.request}"
|
|
1809
1885
|
|
|
@@ -1811,7 +1887,7 @@ async function executeOrchestratorTool(toolName, args, project) {
|
|
|
1811
1887
|
- **URL:** ${plan.project_info.url}
|
|
1812
1888
|
- **Niche:** ${plan.project_info.niche}
|
|
1813
1889
|
|
|
1814
|
-
## Content Settings (from database)
|
|
1890
|
+
## Content Settings (from database - DO NOT USE DEFAULTS)
|
|
1815
1891
|
| Setting | Value |
|
|
1816
1892
|
|---------|-------|
|
|
1817
1893
|
| **Word Count** | ${plan.settings.target_word_count} words |
|
|
@@ -1887,6 +1963,10 @@ ${plan.steps[0].instruction}
|
|
|
1887
1963
|
// Add to articles array (not overwriting previous articles!)
|
|
1888
1964
|
sessionState.articles.push(newArticle)
|
|
1889
1965
|
|
|
1966
|
+
// Track stats
|
|
1967
|
+
incrementStat('articles_created')
|
|
1968
|
+
incrementStat('words_written', wordCount)
|
|
1969
|
+
|
|
1890
1970
|
// Also keep in current working fields for backwards compatibility
|
|
1891
1971
|
sessionState.title = title
|
|
1892
1972
|
sessionState.article = content
|
|
@@ -1907,7 +1987,12 @@ ${plan.steps[0].instruction}
|
|
|
1907
1987
|
|
|
1908
1988
|
const workflow = sessionState.currentWorkflow
|
|
1909
1989
|
const targetWordCount = workflow?.settings?.target_word_count
|
|
1910
|
-
|
|
1990
|
+
// Only 5% tolerance - 2500 word target means minimum 2375 words
|
|
1991
|
+
const wordCountOk = targetWordCount ? wordCount >= targetWordCount * 0.95 : true
|
|
1992
|
+
const shortfall = targetWordCount ? targetWordCount - wordCount : 0
|
|
1993
|
+
|
|
1994
|
+
// Log word count check
|
|
1995
|
+
log(`Word count check: ${wordCount} words (target: ${targetWordCount}, ok: ${wordCountOk})`)
|
|
1911
1996
|
|
|
1912
1997
|
// Find next step
|
|
1913
1998
|
const imageStep = workflow?.steps?.find(s => s.action === 'generate_images')
|
|
@@ -1954,7 +2039,19 @@ Use \`publish_content\` to publish all unpublished articles, or \`get_session\`
|
|
|
1954
2039
|
**Keywords:** ${keywords.join(', ') || 'none specified'}
|
|
1955
2040
|
**Images:** ${newArticle.imageUrl ? '1 cover' : 'no cover'}${newArticle.inlineImages.length > 0 ? ` + ${newArticle.inlineImages.length} inline` : ''}
|
|
1956
2041
|
|
|
1957
|
-
${targetWordCount && !wordCountOk ?
|
|
2042
|
+
${targetWordCount && !wordCountOk ? `
|
|
2043
|
+
╔══════════════════════════════════════════════════════════════════════════╗
|
|
2044
|
+
║ ⛔ WORD COUNT NOT MET - ${shortfall} WORDS SHORT! ║
|
|
2045
|
+
║ Target: ${targetWordCount} words | Actual: ${wordCount} words ║
|
|
2046
|
+
║ ║
|
|
2047
|
+
║ The article does not meet the project's word count requirement. ║
|
|
2048
|
+
║ Please EXPAND the content before publishing: ║
|
|
2049
|
+
║ - Add more detailed explanations to each section ║
|
|
2050
|
+
║ - Include additional examples and statistics ║
|
|
2051
|
+
║ - Expand the FAQ section with more questions ║
|
|
2052
|
+
║ - Add more H2 sections if needed ║
|
|
2053
|
+
╚══════════════════════════════════════════════════════════════════════════╝
|
|
2054
|
+
` : ''}
|
|
1958
2055
|
${!meta_description ? '⚠️ **Warning:** Meta description is missing. Add it for better SEO.\n' : ''}
|
|
1959
2056
|
${articlesListSection}${categoriesSection}
|
|
1960
2057
|
## Next Step${includeImages && imageStep ? ': Generate Images' : ': Ready to Publish or Continue'}
|
|
@@ -2642,6 +2739,9 @@ async function executeImageGeneration(args) {
|
|
|
2642
2739
|
// Persist session to file
|
|
2643
2740
|
saveSession()
|
|
2644
2741
|
|
|
2742
|
+
// Track stats
|
|
2743
|
+
incrementStat('images_generated')
|
|
2744
|
+
|
|
2645
2745
|
const imageNumber = 1 + sessionState.inlineImages.length
|
|
2646
2746
|
const totalImages = sessionState.currentWorkflow?.settings?.total_images || 1
|
|
2647
2747
|
const imageType = imageNumber === 1 ? 'Cover Image' : `Inline Image ${imageNumber - 1}`
|
|
@@ -2705,6 +2805,9 @@ ${imageNumber < totalImages ? `\n**Next:** Generate ${totalImages - imageNumber}
|
|
|
2705
2805
|
// Return base64 data URI
|
|
2706
2806
|
const dataUri = `data:${mimeType};base64,${imageData}`
|
|
2707
2807
|
|
|
2808
|
+
// Track stats
|
|
2809
|
+
incrementStat('images_generated')
|
|
2810
|
+
|
|
2708
2811
|
return {
|
|
2709
2812
|
content: [{
|
|
2710
2813
|
type: 'text',
|
|
@@ -2819,6 +2922,9 @@ ${imageNumber < totalImages ? `\n**Next:** Generate ${totalImages - imageNumber}
|
|
|
2819
2922
|
// Persist session to file
|
|
2820
2923
|
saveSession()
|
|
2821
2924
|
|
|
2925
|
+
// Track stats
|
|
2926
|
+
incrementStat('images_generated')
|
|
2927
|
+
|
|
2822
2928
|
const imageNumber = 1 + sessionState.inlineImages.length
|
|
2823
2929
|
const totalImages = sessionState.currentWorkflow?.settings?.total_images || 1
|
|
2824
2930
|
const imageType = imageNumber === 1 ? 'Cover Image' : `Inline Image ${imageNumber - 1}`
|
|
@@ -3378,6 +3484,9 @@ async function main() {
|
|
|
3378
3484
|
progress('Tool', `Executing ${name}`)
|
|
3379
3485
|
log(`Executing tool: ${name}`)
|
|
3380
3486
|
|
|
3487
|
+
// Track tool call stats
|
|
3488
|
+
incrementStat('tool_calls')
|
|
3489
|
+
|
|
3381
3490
|
// Check if this is an orchestrator tool
|
|
3382
3491
|
const orchestratorTool = ORCHESTRATOR_TOOLS.find(t => t.name === name)
|
|
3383
3492
|
|
package/package.json
CHANGED