Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions data-machine.php
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,10 @@ function datamachine_activate_for_site() {
// Migrate USER.md to network-scoped paths and create NETWORK.md on multisite (idempotent).
datamachine_migrate_user_md_to_network_scope();

// Regenerate SITE.md with enriched content and clean up legacy SiteContext transient.
datamachine_regenerate_site_md();
delete_transient( 'datamachine_site_context_data' );

// Clean up legacy per-agent-type log level options (idempotent).
foreach ( array( 'pipeline', 'chat', 'system' ) as $legacy_agent_type ) {
delete_option( "datamachine_log_level_{$legacy_agent_type}" );
Expand Down
165 changes: 18 additions & 147 deletions inc/Core/WordPress/SiteContext.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
<?php
/**
* Cached WordPress site metadata for AI context injection.
* Cached WordPress site metadata — DEPRECATED.
*
* Site context is now provided by SITE.md (auto-regenerated on disk via
* datamachine_regenerate_site_md()). This class remains for backward
* compatibility with code that references the class name or its static
* methods. It will be removed in a future major version.
*
* @package DataMachine\Core\WordPress
* @deprecated 0.48.0 Site context is now provided by SITE.md auto-regeneration.
*/

namespace DataMachine\Core\WordPress;
Expand All @@ -12,137 +20,19 @@ class SiteContext {
const CACHE_KEY = 'datamachine_site_context_data';

/**
* Get site context data with automatic caching.
* Get site context data.
*
* Plugins can extend the context data via the 'datamachine_site_context' filter.
* Note: Filtering bypasses cache to ensure dynamic data is always fresh.
*
* @return array Site metadata, post types, and taxonomies
* @deprecated 0.48.0 Site context is now in SITE.md. This method remains for backward compat.
* @return array Site metadata (empty — context is now file-based).
*/
public static function get_context(): array {
$cached = get_transient( self::CACHE_KEY );

// Clear stale cache if date has changed (ensures current_date is always accurate).
if ( false !== $cached && isset( $cached['site']['current_date'] ) ) {
if ( wp_date( 'Y-m-d' ) !== $cached['site']['current_date'] ) {
delete_transient( self::CACHE_KEY );
$cached = false;
}
}

if ( false !== $cached ) {
return $cached;
}

$context = array(
'site' => self::get_site_metadata(),
'post_types' => self::get_post_types_data(),
'taxonomies' => self::get_taxonomies_data(),
);

/**
* Filter site context data before caching.
*
* Plugins can use this hook to inject custom context data (e.g., events,
* analytics, custom post type summaries). Note: When this filter is used,
* caching is bypassed to ensure dynamic data remains fresh.
*
* @param array $context Site context data with 'site', 'post_types', 'taxonomies' keys
* @return array Modified context data
*/
$context = apply_filters( 'datamachine_site_context', $context );

set_transient( self::CACHE_KEY, $context, 0 ); // 0 = permanent until invalidated

return $context;
}

/**
* Get site metadata.
*
* @return array Site name, URL, language, timezone, current_date
*/
private static function get_site_metadata(): array {
return array(
'name' => get_bloginfo( 'name' ),
'tagline' => get_bloginfo( 'description' ),
'url' => home_url(),
'admin_url' => admin_url(),
'language' => get_locale(),
'timezone' => wp_timezone_string(),
'current_date' => wp_date( 'Y-m-d' ),
);
}

/**
* Get public post types with published counts.
*
* @return array Post type labels, counts, and hierarchy status
*/
private static function get_post_types_data(): array {
$post_types_data = array();
$post_types = get_post_types( array( 'public' => true ), 'objects' );

foreach ( $post_types as $post_type ) {
$count = wp_count_posts( $post_type->name );
$published_count = $count->publish ?? 0;

$post_types_data[ $post_type->name ] = array(
'label' => $post_type->label,
'singular_label' => ( is_object( $post_type->labels ) && isset( $post_type->labels->singular_name ) )
? $post_type->labels->singular_name
: $post_type->label,
'count' => (int) $published_count,
'hierarchical' => $post_type->hierarchical,
);
}

return $post_types_data;
}

/**
* Get public taxonomies with metadata and term counts.
*
* Returns taxonomy structure without individual term listings to keep
* context payload small. Use search_taxonomy_terms tool for term discovery.
*
* @return array Taxonomy labels, term counts, hierarchy, post type associations
*/
private static function get_taxonomies_data(): array {
$taxonomies_data = array();
$taxonomies = get_taxonomies( array( 'public' => true ), 'objects' );

foreach ( $taxonomies as $taxonomy ) {
if ( \DataMachine\Core\WordPress\TaxonomyHandler::shouldSkipTaxonomy( $taxonomy->name ) ) {
continue;
}

$term_count = wp_count_terms(
array(
'taxonomy' => $taxonomy->name,
'hide_empty' => false,
)
);
if ( is_wp_error( $term_count ) ) {
$term_count = 0;
}

$taxonomies_data[ $taxonomy->name ] = array(
'label' => $taxonomy->label,
'singular_label' => ( is_object( $taxonomy->labels ) && isset( $taxonomy->labels->singular_name ) )
? $taxonomy->labels->singular_name
: $taxonomy->label,
'term_count' => (int) $term_count,
'hierarchical' => $taxonomy->hierarchical,
'post_types' => $taxonomy->object_type ?? array(),
);
}

return $taxonomies_data;
return array();
}

/**
* Clear site context cache.
*
* @deprecated 0.48.0 SITE.md regeneration handles freshness. Cleans up legacy transient.
*/
public static function clear_cache(): void {
delete_transient( self::CACHE_KEY );
Expand All @@ -151,29 +41,10 @@ public static function clear_cache(): void {
/**
* Register automatic cache invalidation hooks.
*
* Clears cache when posts, terms, or site options change.
* Comprehensive invalidation hooks eliminate need for time-based expiration.
* @deprecated 0.48.0 Use datamachine_register_site_md_invalidation() instead.
*/
public static function register_cache_invalidation(): void {
add_action( 'save_post', array( __CLASS__, 'clear_cache' ) );
add_action( 'delete_post', array( __CLASS__, 'clear_cache' ) );
add_action( 'wp_trash_post', array( __CLASS__, 'clear_cache' ) );
add_action( 'untrash_post', array( __CLASS__, 'clear_cache' ) );

add_action( 'create_term', array( __CLASS__, 'clear_cache' ) );
add_action( 'edit_term', array( __CLASS__, 'clear_cache' ) );
add_action( 'delete_term', array( __CLASS__, 'clear_cache' ) );
add_action( 'set_object_terms', array( __CLASS__, 'clear_cache' ) );

add_action( 'user_register', array( __CLASS__, 'clear_cache' ) );
add_action( 'delete_user', array( __CLASS__, 'clear_cache' ) );
add_action( 'set_user_role', array( __CLASS__, 'clear_cache' ) );

add_action( 'switch_theme', array( __CLASS__, 'clear_cache' ) );

add_action( 'update_option_blogname', array( __CLASS__, 'clear_cache' ) );
add_action( 'update_option_blogdescription', array( __CLASS__, 'clear_cache' ) );
add_action( 'update_option_home', array( __CLASS__, 'clear_cache' ) );
add_action( 'update_option_siteurl', array( __CLASS__, 'clear_cache' ) );
// No-op. SITE.md invalidation hooks are registered in bootstrap.php.
// This method remains to avoid fatal errors from code calling it directly.
}
}
89 changes: 30 additions & 59 deletions inc/Engine/AI/Directives/SiteContextDirective.php
Original file line number Diff line number Diff line change
@@ -1,81 +1,52 @@
<?php
/**
* Site Context Directive - Priority 80 (Lowest Priority)
* Site Context Directive — DEPRECATED.
*
* Injects WordPress site context information as the final directive in the
* AI directive system. Provides comprehensive site metadata including
* posts, taxonomies, users, and configuration. Toggleable via settings.
* This directive previously injected a JSON blob of WordPress site metadata
* at priority 80. Site context is now provided entirely by SITE.md via the
* CoreMemoryFilesDirective at priority 20.
*
* Priority Order in Directive System:
* 1. Priority 10 - Plugin Core Directive
* 2. Priority 20 - Core Memory Files (SITE.md, RULES.md, SOUL.md, MEMORY.md, USER.md)
* 3. Priority 40 - Pipeline Memory Files (per-pipeline selectable)
* 4. Priority 50 - Pipeline System Prompt
* 5. Priority 60 - Pipeline Context Files
* 6. Priority 70 - Tool Definitions and Workflow Context
* 7. Priority 80 - WordPress Site Context (THIS CLASS)
* This class remains as a no-op for backward compatibility with code that
* references the class name (e.g., datamachine_site_context_directive filter
* consumers). It will be removed in a future major version.
*
* @package DataMachine\Engine\AI\Directives
* @since 0.30.0
* @deprecated 0.48.0 Site context is now provided by SITE.md. This directive is a no-op.
*/

namespace DataMachine\Engine\AI\Directives;

use DataMachine\Core\PluginSettings;
use DataMachine\Core\WordPress\SiteContext;
use DataMachine\Engine\AI\Directives\DirectiveInterface;

defined( 'ABSPATH' ) || exit;

class SiteContextDirective implements DirectiveInterface {

/**
* Returns empty outputs — site context is now in SITE.md.
*
* @deprecated 0.48.0
*
* @param string $provider_name AI provider name.
* @param array $tools Available tools.
* @param string|null $step_id Pipeline step ID.
* @param array $payload Additional payload data.
* @return array Always returns empty array.
*/
public static function get_outputs( string $provider_name, array $tools, ?string $step_id = null, array $payload = array() ): array {
if ( ! self::is_site_context_enabled() ) {
return array();
}

$context_data = SiteContext::get_context();
if ( empty( $context_data ) || ! is_array( $context_data ) ) {
do_action( 'datamachine_log', 'warning', 'Site Context Directive: Empty context generated' );
return array();
}

return array(
array(
'type' => 'system_json',
'label' => 'WORDPRESS SITE CONTEXT',
'data' => $context_data,
),
);
return array();
}

/**
* Check if site context injection is enabled in plugin settings.
* Check if site context injection is enabled.
*
* @return bool True if enabled, false otherwise
* @deprecated 0.48.0 Use the site_context_enabled setting directly. Controls SITE.md auto-refresh.
* @return bool
*/
public static function is_site_context_enabled(): bool {
return PluginSettings::get( 'site_context_enabled', true );
return \DataMachine\Core\PluginSettings::get( 'site_context_enabled', true );
}
}

/**
* Allow plugins to override the site context directive class.
* datamachine-multisite uses this to replace single-site context with multisite context.
*
* @param string $directive_class The directive class to use for site context
* @return string The filtered directive class
*/
$datamachine_site_context_directive = apply_filters( 'datamachine_site_context_directive', SiteContextDirective::class );

// Register the filtered directive for global context (applies to all AI agents - allows replacement by multisite plugin)
if ( $datamachine_site_context_directive ) {
add_filter(
'datamachine_directives',
function ( $directives ) use ( $datamachine_site_context_directive ) {
$directives[] = array(
'class' => $datamachine_site_context_directive,
'priority' => 80,
'contexts' => array( 'all' ),
);
return $directives;
}
);
}
// The directive is no longer registered in the directive system.
// The class exists purely for backward compatibility with code that
// references it by name (e.g., datamachine_site_context_directive filter).
7 changes: 4 additions & 3 deletions inc/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,10 @@
'label' => 'Network Context',
'description' => 'WordPress multisite network topology and shared resources.',
) );
// SiteContext is autoloaded (Core\WordPress\SiteContext) — register its cache invalidation hook here.
add_action( 'init', array( \DataMachine\Core\WordPress\SiteContext::class, 'register_cache_invalidation' ) );
require_once __DIR__ . '/Engine/AI/Directives/SiteContextDirective.php';
// SITE.md auto-regeneration — replaces the former SiteContext + SiteContextDirective system.
// SITE.md is now the single source of truth for site context, auto-refreshing on structural changes.
add_action( 'init', 'datamachine_register_site_md_invalidation' );

require_once __DIR__ . '/Engine/AI/Directives/DailyMemorySelectorDirective.php';
require_once __DIR__ . '/Api/Chat/ChatContextDirective.php';
require_once __DIR__ . '/Api/System/SystemContextDirective.php';
Expand Down
Loading
Loading