feat: add Writing Instructions setting for LLM system prompt (v0.4.4)
New textarea in Content settings lets admins provide custom instructions appended to the writer's system prompt. Steers style, structure, voice, and formatting without editing code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,14 @@ All notable changes to PRAutoBlogger will be documented in this file.
|
||||
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project uses [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [0.4.4] — 2026-04-19
|
||||
|
||||
### Added
|
||||
- **Writing Instructions setting.** New textarea in Content settings lets you
|
||||
provide custom instructions appended to the LLM's system prompt when writing
|
||||
articles. Use it to steer style, structure, voice, and formatting without
|
||||
editing code.
|
||||
|
||||
## [0.4.3] — 2026-04-19
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -202,6 +202,14 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'default' => 2000,
|
||||
'min' => 500,
|
||||
],
|
||||
[
|
||||
'id' => 'prautoblogger_writing_instructions',
|
||||
'label' => __( 'Writing Instructions', 'prautoblogger' ),
|
||||
'type' => 'textarea',
|
||||
'section' => 'prautoblogger_content',
|
||||
'default' => '',
|
||||
'description' => __( 'Custom instructions appended to the LLM\'s system prompt when writing articles. Use this to steer style, structure, voice, and formatting — e.g. "Always open with a hook question. Use short paragraphs. Cite at least two studies."', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'prautoblogger_topic_exclusions',
|
||||
'label' => __( 'Topic Exclusions', 'prautoblogger' ),
|
||||
|
||||
@@ -32,13 +32,10 @@ class PRAutoBlogger_Content_Generator {
|
||||
* Generate a blog post from an article idea.
|
||||
*
|
||||
* Reads pipeline mode from settings and dispatches to the appropriate method.
|
||||
*
|
||||
* Side effects: LLM API calls (1-3 depending on mode), cost logging.
|
||||
*
|
||||
* @param PRAutoBlogger_Article_Idea $idea The scored idea to generate content for.
|
||||
*
|
||||
* @return string Generated HTML content.
|
||||
*
|
||||
* @throws \RuntimeException On generation failure.
|
||||
*/
|
||||
public function generate( PRAutoBlogger_Article_Idea $idea ): string {
|
||||
@@ -51,7 +48,8 @@ class PRAutoBlogger_Content_Generator {
|
||||
absint( get_option( 'prautoblogger_min_word_count', 800 ) ),
|
||||
absint( get_option( 'prautoblogger_max_word_count', 2000 ) ),
|
||||
get_option( 'prautoblogger_niche_description', '' ),
|
||||
json_decode( get_option( 'prautoblogger_topic_exclusions', '[]' ), true ) ?: []
|
||||
json_decode( get_option( 'prautoblogger_topic_exclusions', '[]' ), true ) ?: [],
|
||||
get_option( 'prautoblogger_writing_instructions', '' )
|
||||
);
|
||||
|
||||
if ( 'single_pass' === $mode ) {
|
||||
@@ -64,8 +62,7 @@ class PRAutoBlogger_Content_Generator {
|
||||
/**
|
||||
* Single-pass generation: one LLM call produces the complete article.
|
||||
*
|
||||
* @param PRAutoBlogger_Content_Request $request
|
||||
*
|
||||
* @param PRAutoBlogger_Content_Request $request Content request with settings.
|
||||
* @return string Generated HTML content.
|
||||
*/
|
||||
private function generate_single_pass( PRAutoBlogger_Content_Request $request ): string {
|
||||
@@ -249,8 +246,7 @@ class PRAutoBlogger_Content_Generator {
|
||||
/**
|
||||
* Build the system prompt shared across all writing stages.
|
||||
*
|
||||
* @param PRAutoBlogger_Content_Request $request
|
||||
*
|
||||
* @param PRAutoBlogger_Content_Request $request Content request with settings.
|
||||
* @return string
|
||||
*/
|
||||
private function build_writer_system_prompt( PRAutoBlogger_Content_Request $request ): string {
|
||||
@@ -262,6 +258,13 @@ class PRAutoBlogger_Content_Generator {
|
||||
$prompt .= ". Write well-researched, engaging, SEO-friendly content. ";
|
||||
$prompt .= "Use a {$request->get_tone()} tone. ";
|
||||
$prompt .= "Output HTML content only — no markdown, no code fences, no commentary.";
|
||||
|
||||
// Append user-defined writing instructions if configured.
|
||||
$instructions = trim( $request->get_writing_instructions() );
|
||||
if ( '' !== $instructions ) {
|
||||
$prompt .= "\n\nAdditional instructions:\n" . $instructions;
|
||||
}
|
||||
|
||||
return $prompt;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ class PRAutoBlogger_Content_Request {
|
||||
private int $max_word_count;
|
||||
private string $niche_description;
|
||||
private array $topic_exclusions;
|
||||
private string $writing_instructions;
|
||||
|
||||
public function __construct(
|
||||
PRAutoBlogger_Article_Idea $idea,
|
||||
@@ -29,15 +30,17 @@ class PRAutoBlogger_Content_Request {
|
||||
int $min_word_count,
|
||||
int $max_word_count,
|
||||
string $niche_description,
|
||||
array $topic_exclusions = []
|
||||
array $topic_exclusions = [],
|
||||
string $writing_instructions = ''
|
||||
) {
|
||||
$this->idea = $idea;
|
||||
$this->pipeline_mode = $pipeline_mode;
|
||||
$this->tone = $tone;
|
||||
$this->min_word_count = $min_word_count;
|
||||
$this->max_word_count = $max_word_count;
|
||||
$this->niche_description = $niche_description;
|
||||
$this->topic_exclusions = $topic_exclusions;
|
||||
$this->idea = $idea;
|
||||
$this->pipeline_mode = $pipeline_mode;
|
||||
$this->tone = $tone;
|
||||
$this->min_word_count = $min_word_count;
|
||||
$this->max_word_count = $max_word_count;
|
||||
$this->niche_description = $niche_description;
|
||||
$this->topic_exclusions = $topic_exclusions;
|
||||
$this->writing_instructions = $writing_instructions;
|
||||
}
|
||||
|
||||
public function get_idea(): PRAutoBlogger_Article_Idea {
|
||||
@@ -67,4 +70,8 @@ class PRAutoBlogger_Content_Request {
|
||||
public function get_topic_exclusions(): array {
|
||||
return $this->topic_exclusions;
|
||||
}
|
||||
|
||||
public function get_writing_instructions(): string {
|
||||
return $this->writing_instructions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ declare(strict_types=1);
|
||||
* Plugin Name: PRAutoBlogger
|
||||
* Plugin URI: https://peptiderepo.com/prautoblogger
|
||||
* Description: Monitors social media for trending topics, generates SEO-friendly blog posts using AI, and publishes them on a daily schedule with full cost tracking and self-improvement metrics.
|
||||
* Version: 0.4.3
|
||||
* Version: 0.4.4
|
||||
* Requires at least: 6.0
|
||||
* Requires PHP: 7.4
|
||||
* Author: PeptideRepo
|
||||
@@ -35,7 +35,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
| and limits without magic strings.
|
||||
*/
|
||||
|
||||
define( 'PRAUTOBLOGGER_VERSION', '0.4.3' );
|
||||
define( 'PRAUTOBLOGGER_VERSION', '0.4.4' );
|
||||
define( 'PRAUTOBLOGGER_DB_VERSION', '1.1.0' );
|
||||
define( 'PRAUTOBLOGGER_PLUGIN_FILE', __FILE__ );
|
||||
define( 'PRAUTOBLOGGER_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||
|
||||
@@ -25,7 +25,8 @@ class ContentRequestTest extends BaseTestCase {
|
||||
1000,
|
||||
2500,
|
||||
'Technology and innovation',
|
||||
[ 'politics', 'religion' ]
|
||||
[ 'politics', 'religion' ],
|
||||
'Always open with a hook question.'
|
||||
);
|
||||
|
||||
$this->assertSame( $idea, $request->get_idea() );
|
||||
@@ -35,6 +36,7 @@ class ContentRequestTest extends BaseTestCase {
|
||||
$this->assertSame( 2500, $request->get_max_word_count() );
|
||||
$this->assertSame( 'Technology and innovation', $request->get_niche_description() );
|
||||
$this->assertSame( [ 'politics', 'religion' ], $request->get_topic_exclusions() );
|
||||
$this->assertSame( 'Always open with a hook question.', $request->get_writing_instructions() );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +65,25 @@ class ContentRequestTest extends BaseTestCase {
|
||||
$this->assertIsArray( $request->get_topic_exclusions() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writing_instructions defaults to empty string when omitted.
|
||||
*/
|
||||
public function test_writing_instructions_defaults_to_empty(): void {
|
||||
$idea_data = $this->get_article_idea_fixture();
|
||||
$idea = new \PRAutoBlogger_Article_Idea( $idea_data );
|
||||
|
||||
$request = new \PRAutoBlogger_Content_Request(
|
||||
$idea,
|
||||
'auto',
|
||||
'professional',
|
||||
1000,
|
||||
2500,
|
||||
'Tech'
|
||||
);
|
||||
|
||||
$this->assertSame( '', $request->get_writing_instructions() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with empty topic exclusions.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user