[
MAINHACK
]
Mail Test
BC
Config Scan
HOME
Create...
New File
New Folder
Viewing / Editing File: parser.php
<?php // SPDX-FileCopyrightText: 2026 Ovation S.r.l. <dev@novamira.ai> // SPDX-License-Identifier: AGPL-3.0-or-later declare(strict_types=1); namespace Novamira\Skills\Parser; if (!defined('ABSPATH')) { exit(); } /** * Maximum body length (bytes) for an uploaded or submitted skill. Sanity cap. */ const MAX_BODY_BYTES = 1_048_576; // 1 MB /** * Unescape common C-style escape sequences in a skill body. * * AI clients sometimes JSON-encode their tool-call arguments twice, with * the result that real newlines arrive as the two-character sequence `\n` * instead of a real newline. The same can happen with `\t`, `\r`, etc. * Nobody actually wants those escape sequences as literal text in a * skill body, so we run `stripcslashes` on the way in — it converts * `\n` → LF, `\t` → tab, `\\` → `\`, `\"` → `"`, and so on, preserving * any other text as-is. */ function unescape_content(string $raw): string { return stripcslashes($raw); } /** * Strip a single layer of matching surrounding quotes (single or double) from a * trimmed frontmatter value. Leaves the value untouched if it isn't quoted. */ function unquote_value(string $value): string { if (str_starts_with($value, '"') && str_ends_with($value, '"')) { return substr($value, offset: 1, length: -1); } if (str_starts_with($value, "'") && str_ends_with($value, "'")) { return substr($value, offset: 1, length: -1); } return $value; } /** * Parse the raw frontmatter block (the text between the `---` fences) into the * recognized fields. Blank lines, comment lines (`#`), lines without a colon, * and unknown keys are ignored (lenient). Unspecified fields keep their default. * * @return array{name: string, description: string, enable_prompt: bool, enable_agentic: bool} */ function parse_frontmatter(string $frontmatter_raw): array { $name = ''; $description = ''; $enable_prompt = true; $enable_agentic = true; foreach (explode(separator: "\n", string: $frontmatter_raw) as $line) { if (trim($line) === '' || str_starts_with(trim($line), '#')) { continue; } $colon = strpos($line, needle: ':'); if ($colon === false) { continue; } $key = strtolower(trim(substr($line, offset: 0, length: $colon))); $value = unquote_value(trim(substr($line, offset: $colon + 1))); switch ($key) { case 'name': $name = $value; break; case 'description': $description = $value; break; case 'enable_prompt': $enable_prompt = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? true; break; case 'enable_agentic': $enable_agentic = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? true; break; // Unknown keys silently ignored (lenient). } } return [ 'name' => $name, 'description' => $description, 'enable_prompt' => $enable_prompt, 'enable_agentic' => $enable_agentic, ]; } /** * Parse a SKILL.md string. Lenient: a malformed frontmatter block is reported * via `parse_error`, missing fields fall back to sensible defaults, and unknown * keys are silently ignored. * * @return array{name: string, description: string, enable_prompt: bool, enable_agentic: bool, body: string, parse_error: ?string} */ function parse(string $raw): array { $body = $raw; $parse_error = null; $fields = [ 'name' => '', 'description' => '', 'enable_prompt' => true, 'enable_agentic' => true, ]; $normalized = preg_replace(pattern: '/\r\n?/', replacement: "\n", subject: $raw); if (!is_string($normalized)) { $normalized = $raw; } if (str_starts_with($normalized, "---\n")) { $closing = strpos($normalized, needle: "\n---\n", offset: 4); if ($closing === false && str_ends_with($normalized, "\n---")) { // Allow trailing `---` with no newline after. $closing = strlen($normalized) - 4; } if ($closing !== false) { $frontmatter_raw = substr($normalized, offset: 4, length: $closing - 4); $body = ltrim(substr($normalized, offset: $closing + 5), characters: "\n"); $fields = parse_frontmatter($frontmatter_raw); } if ($closing === false) { $parse_error = __('Frontmatter started with --- but had no closing ---', domain: 'novamira'); } } return [ 'name' => $fields['name'], 'description' => $fields['description'], 'enable_prompt' => $fields['enable_prompt'], 'enable_agentic' => $fields['enable_agentic'], 'body' => $body, 'parse_error' => $parse_error, ]; } /** * Normalize a slug candidate to the canonical internal form (WordPress-friendly, * no `custom_` prefix). Returns empty string if no usable slug can be derived — * the caller decides what to do with that. */ function normalize_slug(string $raw): string { $candidate = sanitize_title($raw); if ($candidate === '') { return ''; } if (strlen($candidate) > 60) { $candidate = rtrim(substr($candidate, offset: 0, length: 60), characters: '-'); } return $candidate; } /** * Reconstruct a SKILL.md string from a skill record. The frontmatter * `name:` field carries the slug as-is — the slug is already the * sanitized identifier (`sanitize_title($post_title)`), no prefix or * decoration applied. * * @param array{slug?: string, description?: string, enable_prompt?: bool, enable_agentic?: bool, content?: string} $skill */ function render_skill_md(array $skill): string { return sprintf( "---\nname: %s\ndescription: %s\nenable_prompt: %s\nenable_agentic: %s\n---\n\n%s", $skill['slug'] ?? '', str_replace(search: "\n", replace: ' ', subject: $skill['description'] ?? ''), $skill['enable_prompt'] ?? false ? 'true' : 'false', $skill['enable_agentic'] ?? true ? 'true' : 'false', $skill['content'] ?? '', ); }
Save Changes
Cancel / Back
Close ×
Server Info
Hostname: premium264.web-hosting.com
Server IP: 69.57.162.13
PHP Version: 8.1.34
Server Software: LiteSpeed
System: Linux premium264.web-hosting.com 4.18.0-553.45.1.lve.el8.x86_64 #1 SMP Wed Mar 26 12:08:09 UTC 2025 x86_64
HDD Total: 97.87 GB
HDD Free: 73.68 GB
Domains on IP: N/A (Requires external lookup)
System Features
Safe Mode:
Off
disable_functions:
None
allow_url_fopen:
On
allow_url_include:
Off
magic_quotes_gpc:
Off
register_globals:
Off
open_basedir:
None
cURL:
Enabled
ZipArchive:
Enabled
MySQLi:
Enabled
PDO:
Enabled
wget:
Yes
curl (cmd):
Yes
perl:
Yes
python:
Yes (py3)
gcc:
No
pkexec:
No
git:
Yes
User Info
Username: kidscntx
User ID (UID): 1154
Group ID (GID): 1112
Script Owner UID: 1154
Current Dir Owner: 1154