
    i              
          U d Z ddlZddlZddlZddlZddlZddlmZ ddlm	Z	 ddl
mZmZmZ ddlmZ ddlmZmZmZmZmZmZmZ ddlmZ  ej        e          Zg d	Zh d
ZdededefdZ de	dee	         fdZ!dZ"de	dee	         fdZ#dedefdZ$dZ%dZ&dZ'dZ(dZ)dZ*dZ+dZ,dZ-dZ.dZ/i d d!d"d#d$d%d&d'd(d)d*d+d,d-d.d/d0d1d2d3d4d5d6d7d8d9d:d;d<d=d>d?d@dAZ0dBZ1defdCZ2dDZ3dEZ4dFZ5dGZ6 e            Z7ee8ef         e9dH<    ej:                    Z;dIZ<de	fdJZ=dKdLdMe>ddfdNZ?dOe	de@eeAeB         f         fdPZCdOe	dee@         fdQZDdOe	dRe@eeAeB         f         dSeAe@         dTe@eef         ddf
dUZEdVe	dOe	dWe@dXede@f
dYZFdVe	de8e>e@ef         fdZZGd[e@d\d]d^d]de>fd_ZH	 	 dmd\d]d^d]defd`ZIdndad]defdbZJe3fdededceBdefddZKdee         fdeZLdfe	defdgZMdfe	defdhZNdfe	defdiZOdfe	defdjZPdodee         dke>defdlZQdS )pzSystem prompt assembly -- identity, platform hints, skills index, context files.

All functions are stateless. AIAgent._build_system_prompt() calls these to
assemble pieces, then combines them with memory and ephemeral prompts.
    N)OrderedDict)Path)get_hermes_homeget_skills_diris_wsl)Optional)extract_skill_conditionsextract_skill_descriptionget_all_skills_dirsget_disabled_skill_namesiter_skill_index_filesparse_frontmatterskill_matches_platform)atomic_json_write)
)z2ignore\s+(previous|all|above|prior)\s+instructionsprompt_injection)zdo\s+not\s+tell\s+the\s+userdeception_hide)zsystem\s+prompt\s+overridesys_prompt_override)z<disregard\s+(your|all|any)\s+(instructions|rules|guidelines)disregard_rules)zVact\s+as\s+(if|though)\s+you\s+(have\s+no|don\'t\s+have)\s+(restrictions|limits|rules)bypass_restrictions)z9<!--[^>]*(?:ignore|override|system|secret|hidden)[^>]*-->html_comment_injection)z5<\s*div\s+style\s*=\s*["\'][\s\S]*?display\s*:\s*none
hidden_div)z5translate\s+.*\s+into\s+.*\s+and\s+(execute|run|eval)translate_execute)z?curl\s+[^\n]*\$\{?\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API)
exfil_curl)z0cat\s+[^\n]*(\.env|credentials|\.netrc|\.pgpass)read_secrets>
      ​   ‌   ‍   ‪   ‫   ‬   ‭   ‮   ⁠   ﻿contentfilenamereturnc                    g }t           D ],}|| v r&|                    dt          |          d           -t          D ]:\  }}t	          j        || t          j                  r|                    |           ;|rKt                              d|d	                    |                     d| dd	                    |           dS | S )zCScan context file content for injection. Returns sanitized content.zinvisible unicode U+04XzContext file %s blocked: %sz, z
[BLOCKED: z' contained potential prompt injection (z). Content not loaded.])
_CONTEXT_INVISIBLE_CHARSappendord_CONTEXT_THREAT_PATTERNSresearch
IGNORECASEloggerwarningjoin)r%   r&   findingscharpatternpids         9/home/ubuntu/.hermes/hermes-agent/agent/prompt_builder.py_scan_context_contentr9   7   s    H ) D D7??OOB3t99BBBCCC 1 ! !9Wgr}55 	!OOC    z4h		(@S@STTTyHyyTYYW_M`M`yyyyN    startc                 ~    |                                  }|g|j        D ]}|dz                                  r|c S dS )zWalk *start* and its parents looking for a ``.git`` directory.

    Returns the directory containing ``.git``, or ``None`` if we hit the
    filesystem root without finding one.
    z.gitN)resolveparentsexists)r;   currentparents      r8   _find_git_rootrB   L   sU     mmooG-W_-  VO##%% 	MMM	4r:   )
.hermes.mdz	HERMES.mdcwdc                     t          |           }|                                 }|g|j        D ]5}t          D ]!}||z  }|                                r|c c S "|r||k    r n6dS )zDiscover the nearest ``.hermes.md`` or ``HERMES.md``.

    Search order: *cwd* first, then each parent directory up to (and
    including) the git repository root.  Returns the first match, or
    ``None`` if nothing is found.
    N)rB   r=   r>   _HERMES_MD_NAMESis_file)rD   stop_atr@   	directoryname	candidates         r8   _find_hermes_mdrL   \   s     S!!GkkmmG00  	$ 	! 	!D!D(I  "" !      !  	yG++E4r:   c                     |                      d          rB|                     dd          }|dk    r&| |dz   d                             d          }|r|n| S | S )a;  Remove optional YAML frontmatter (``---`` delimited) from *content*.

    The frontmatter may contain structured config (model overrides, tool
    settings) that will be handled separately in a future PR.  For now we
    strip it so only the human-readable markdown body is injected into the
    system prompt.
    z---z
---      N
)
startswithfindlstrip)r%   endbodys      r8   _strip_yaml_frontmatterrW   q   sj     %   -ll7A&&"993788$++D11D,44W,Nr:   a  You are Hermes Agent, an intelligent AI assistant created by Nous Research. You are helpful, knowledgeable, and direct. You assist users with a wide range of tasks including answering questions, writing and editing code, analyzing information, creative work, and executing actions via your tools. You communicate clearly, admit uncertainty when appropriate, and prioritize being genuinely useful over being verbose unless otherwise directed below. Be targeted and efficient in your exploration and investigations.zIf the user asks about configuring, setting up, or using Hermes Agent itself, load the `hermes-agent` skill with skill_view(name='hermes-agent') before answering. Docs: https://hermes-agent.nousresearch.com/docsu  You have persistent memory across sessions. Save durable facts using the memory tool: user preferences, environment details, tool quirks, and stable conventions. Memory is injected into every turn, so keep it compact and focused on facts that will still matter later.
Prioritize what reduces future user steering — the most valuable memory is one that prevents the user from having to correct or remind you again. User preferences and recurring corrections matter more than procedural task details.
Do NOT save task progress, session outcomes, completed-work logs, or temporary TODO state to memory; use session_search to recall those from past transcripts. If you've discovered a new way to do something, solved a problem that could be necessary later, save it as a skill with the skill tool.
Write memories as declarative facts, not instructions to yourself. 'User prefers concise responses' ✓ — 'Always respond concisely' ✗. 'Project uses pytest with xdist' ✓ — 'Run tests with pytest -n 4' ✗. Imperative phrasing gets re-read as a directive in later sessions and can cause repeated work or override the user's current request. Procedures and workflows belong in skills, not memory.zWhen the user references something from a past conversation or you suspect relevant cross-session context exists, use session_search to recall it before asking them to repeat themselves.u  After completing a complex task (5+ tool calls), fixing a tricky error, or discovering a non-trivial workflow, save the approach as a skill with skill_manage so you can reuse it next time.
When using a skill and finding it outdated, incomplete, or wrong, patch it immediately with skill_manage(action='patch') — don't wait to be asked. Skills that aren't maintained become liabilities.u  # You are a Kanban worker
You were spawned by the Hermes Kanban dispatcher to execute ONE task from the shared board at `~/.hermes/kanban.db`. Your task id is in `$HERMES_KANBAN_TASK`; your workspace is `$HERMES_KANBAN_WORKSPACE`. The `kanban_*` tools in your schema are your primary coordination surface — they write directly to the shared SQLite DB and work regardless of terminal backend (local/docker/modal/ssh).

## Lifecycle

1. **Orient.** Call `kanban_show()` first (no args — it defaults to your task). The response includes title, body, parent-task handoffs (summary + metadata), any prior attempts on this task if you're a retry, the full comment thread, and a pre-formatted `worker_context` you can treat as ground truth.
2. **Work inside the workspace.** `cd $HERMES_KANBAN_WORKSPACE` before any file operations. The workspace is yours for this run. Don't modify files outside it unless the task explicitly asks.
3. **Heartbeat on long operations.** Call `kanban_heartbeat(note=...)` every few minutes during long subprocesses (training, encoding, crawling). Skip heartbeats for short tasks.
4. **Block on genuine ambiguity.** If you need a human decision you cannot infer (missing credentials, UX choice, paywalled source, peer output you need first), call `kanban_block(reason="...")` and stop. Don't guess. The user will unblock with context and the dispatcher will respawn you.
5. **Complete with structured handoff.** Call `kanban_complete(summary=..., metadata=...)`. `summary` is 1–3 human-readable sentences naming concrete artifacts. `metadata` is machine-readable facts (`{changed_files: [...], tests_run: N, decisions: [...]}`). Downstream workers read both via their own `kanban_show`. Never put secrets / tokens / raw PII in either field — run rows are durable forever.
6. **If follow-up work appears, create it; don't do it.** Use `kanban_create(title=..., assignee=<right-profile>, parents=[your-task-id])` to spawn a child task for the appropriate specialist profile instead of scope-creeping into the next thing.

## Orchestrator mode

If your task is itself a decomposition task (e.g. a planner profile given a high-level goal), use `kanban_create` to fan out into child tasks — one per specialist, each with an explicit `assignee` and `parents=[...]` to express dependencies. Then `kanban_complete` your own task with a summary of the decomposition. Do NOT execute the work yourself; your job is routing, not implementation.

## Do NOT

- Do not shell out to `hermes kanban <verb>` for board operations. Use the `kanban_*` tools — they work across all terminal backends.
- Do not complete a task you didn't actually finish. Block it.
- Do not assign follow-up work to yourself. Assign it to the right specialist profile.
- Do not call `delegate_task` as a board substitute. `delegate_task` is for short reasoning subtasks inside your own run; board tasks are for cross-agent handoffs that outlive one API loop.u<  # Tool-use enforcement
You MUST use your tools to take action — do not describe what you would do or plan to do without actually doing it. When you say you will perform an action (e.g. 'I will run the tests', 'Let me check the file', 'I will create the project'), you MUST immediately make the corresponding tool call in the same response. Never end your turn with a promise of future action — execute it now.
Keep working until the task is actually complete. Do not stop with a summary of what you plan to do next time. If you have tools available that can accomplish the task, use them instead of telling the user what you would do.
Every response should either (a) contain tool calls that make progress, or (b) deliver a final result to the user. Responses that only describe intentions without acting are not acceptable.)gptcodexgeminigemmagroku
  # Execution discipline
<tool_persistence>
- Use tools whenever they improve correctness, completeness, or grounding.
- Do not stop early when another tool call would materially improve the result.
- If a tool returns empty or partial results, retry with a different query or strategy before giving up.
- Keep calling tools until: (1) the task is complete, AND (2) you have verified the result.
</tool_persistence>

<mandatory_tool_use>
NEVER answer these from memory or mental computation — ALWAYS use a tool:
- Arithmetic, math, calculations → use terminal or execute_code
- Hashes, encodings, checksums → use terminal (e.g. sha256sum, base64)
- Current time, date, timezone → use terminal (e.g. date)
- System state: OS, CPU, memory, disk, ports, processes → use terminal
- File contents, sizes, line counts → use read_file, search_files, or terminal
- Git history, branches, diffs → use terminal
- Current facts (weather, news, versions) → use web_search
Your memory and user profile describe the USER, not the system you are running on. The execution environment may differ from what the user profile says about their personal setup.
</mandatory_tool_use>

<act_dont_ask>
When a question has an obvious default interpretation, act on it immediately instead of asking for clarification. Examples:
- 'Is port 443 open?' → check THIS machine (don't ask 'open where?')
- 'What OS am I running?' → check the live system (don't use user profile)
- 'What time is it?' → run `date` (don't guess)
Only ask for clarification when the ambiguity genuinely changes what tool you would call.
</act_dont_ask>

<prerequisite_checks>
- Before taking an action, check whether prerequisite discovery, lookup, or context-gathering steps are needed.
- Do not skip prerequisite steps just because the final action seems obvious.
- If a task depends on output from a prior step, resolve that dependency first.
</prerequisite_checks>

<verification>
Before finalizing your response:
- Correctness: does the output satisfy every stated requirement?
- Grounding: are factual claims backed by tool outputs or provided context?
- Formatting: does the output match the requested format or schema?
- Safety: if the next step has side effects (file writes, commands, API calls), confirm scope before executing.
</verification>

<missing_context>
- If required context is missing, do NOT guess or hallucinate an answer.
- Use the appropriate lookup tool when missing information is retrievable (search_files, web_search, read_file, etc.).
- Ask a clarifying question only when the information cannot be retrieved by tools.
- If you must proceed with incomplete information, label assumptions explicitly.
</missing_context>u  # Google model operational directives
Follow these operational rules strictly:
- **Absolute paths:** Always construct and use absolute file paths for all file system operations. Combine the project root with relative paths.
- **Verify first:** Use read_file/search_files to check file contents and project structure before making changes. Never guess at file contents.
- **Dependency checks:** Never assume a library is available. Check package.json, requirements.txt, Cargo.toml, etc. before importing.
- **Conciseness:** Keep explanatory text brief — a few sentences, not paragraphs. Focus on actions and results over narration.
- **Parallel tool calls:** When you need to perform multiple independent operations (e.g. reading several files), make all the tool calls in a single response rather than sequentially.
- **Non-interactive commands:** Use flags like -y, --yes, --non-interactive to prevent CLI tools from hanging on prompts.
- **Keep going:** Work autonomously until the task is fully resolved. Don't stop with a plan — execute it.
)zgpt-5rY   whatsappu  You are on a text messaging communication platform, WhatsApp. Please do not use markdown as it does not render. You can send media files natively: to deliver a file to the user, include MEDIA:/absolute/path/to/file in your response. The file will be sent as a native WhatsApp attachment — images (.jpg, .png, .webp) appear as photos, videos (.mp4, .mov) play inline, and other files arrive as downloadable documents. You can also include image URLs in markdown format ![alt](url) and they will be sent as photos.telegramu'  You are on a text messaging communication platform, Telegram. Standard markdown is automatically converted to Telegram format. Supported: **bold**, *italic*, ~~strikethrough~~, ||spoiler||, `inline code`, ```code blocks```, [links](url), and ## headers. Telegram has NO table syntax — prefer bullet lists or labeled key: value pairs over pipe tables (any tables you do emit are auto-rewritten into row-group bullets, which you can produce directly for cleaner output). You can send media files natively: to deliver a file to the user, include MEDIA:/absolute/path/to/file in your response. Images (.png, .jpg, .webp) appear as photos, audio (.ogg) sends as voice bubbles, and videos (.mp4) play inline. You can also include image URLs in markdown format ![alt](url) and they will be sent as native photos.discorda[  You are in a Discord server or group chat communicating with your user. You can send media files natively: include MEDIA:/absolute/path/to/file in your response. Images (.png, .jpg, .webp) are sent as photo attachments, audio as file attachments. You can also include image URLs in markdown format ![alt](url) and they will be sent as attachments.slackaV  You are in a Slack workspace communicating with your user. You can send media files natively: include MEDIA:/absolute/path/to/file in your response. Images (.png, .jpg, .webp) are uploaded as photo attachments, audio as file attachments. You can also include image URLs in markdown format ![alt](url) and they will be uploaded as attachments.signala  You are on a text messaging communication platform, Signal. Please do not use markdown as it does not render. You can send media files natively: to deliver a file to the user, include MEDIA:/absolute/path/to/file in your response. Images (.png, .jpg, .webp) appear as photos, audio as attachments, and other files arrive as downloadable documents. You can also include image URLs in markdown format ![alt](url) and they will be sent as photos.emailuv  You are communicating via email. Write clear, well-structured responses suitable for email. Use plain text formatting (no markdown). Keep responses concise but complete. You can send file attachments — include MEDIA:/absolute/path/to/file in your response. The subject line is preserved for threading. Do not include greetings or sign-offs unless contextually appropriate.cronuj  You are running as a scheduled cron job. There is no user present — you cannot ask questions, request clarification, or wait for follow-up. Execute the task fully and autonomously, making reasonable decisions where needed. Your final response is automatically delivered to the job's configured destination — put the primary content directly in your response.cliu  You are a CLI AI Agent. Try not to use markdown but simple text renderable inside a terminal. File delivery: there is no attachment channel — the user reads your response directly in their terminal. Do NOT emit MEDIA:/path tags (those are only intercepted on messaging platforms like Telegram, Discord, Slack, etc.; on the CLI they render as literal text). When referring to a file you created or changed, just state its absolute path in plain text; the user can open it from there.smsu   You are communicating via SMS. Keep responses concise and use plain text only — no markdown, no formatting. SMS messages are limited to ~1600 characters, so be brief and direct.bluebubblesuR  You are chatting via iMessage (BlueBubbles). iMessage does not render markdown formatting — use plain text. Keep responses concise as they appear as text messages. You can send media files natively: include MEDIA:/absolute/path/to/file in your response. Images (.jpg, .png, .heic) appear as photos and other files arrive as attachments.
mattermostu  You are in a Mattermost workspace communicating with your user. Mattermost renders standard Markdown — headings, bold, italic, code blocks, and tables all work. You can send media files natively: include MEDIA:/absolute/path/to/file in your response. Images (.jpg, .png, .webp) are uploaded as photo attachments, audio and video as file attachments. Image URLs in markdown format ![alt](url) are rendered as inline previews automatically.matrixu  You are in a Matrix room communicating with your user. Matrix renders Markdown — bold, italic, code blocks, and links work; the adapter converts your Markdown to HTML for rich display. You can send media files natively: include MEDIA:/absolute/path/to/file in your response. Images (.jpg, .png, .webp) are sent as inline photos, audio (.ogg, .mp3) as voice/audio messages, video (.mp4) inline, and other files as downloadable attachments.feishuuu  You are in a Feishu (Lark) workspace communicating with your user. Feishu renders Markdown in messages — bold, italic, code blocks, and links are supported. You can send media files natively: include MEDIA:/absolute/path/to/file in your response. Images (.jpg, .png, .webp) are uploaded and displayed inline, audio files as voice messages, and other files as attachments.weixina  You are on Weixin/WeChat. Markdown formatting is supported, so you may use it when it improves readability, but keep the message compact and chat-friendly. You can send media files natively: include MEDIA:/absolute/path/to/file in your response. Images are sent as native photos, videos play inline when supported, and other files arrive as downloadable documents. You can also include image URLs in markdown format ![alt](url) and they will be downloaded and sent as native media when possible.wecomu*  You are on WeCom (企业微信 / Enterprise WeChat). Markdown formatting is supported. You CAN send media files natively — to deliver a file to the user, include MEDIA:/absolute/path/to/file in your response. The file will be sent as a native WeCom attachment: images (.jpg, .png, .webp) are sent as photos (up to 10 MB), other files (.pdf, .docx, .xlsx, .md, .txt, etc.) arrive as downloadable documents (up to 20 MB), and videos (.mp4) play inline. Voice messages are supported but must be in AMR format — other audio formats are automatically sent as file attachments. You can also include image URLs in markdown format ![alt](url) and they will be downloaded and sent as native photos. Do NOT tell the user you lack file-sending capability — use MEDIA: syntax whenever a file delivery is appropriate.qqbota  You are on QQ, a popular Chinese messaging platform. QQ supports markdown formatting and emoji. You can send media files natively: include MEDIA:/absolute/path/to/file in your response. Images are sent as native photos, and other files arrive as downloadable documents.yuanbaou  You are on Yuanbao (腾讯元宝), a Chinese AI assistant platform. Markdown formatting is supported (code blocks, tables, bold/italic). You CAN send media files natively — to deliver a file to the user, include MEDIA:/absolute/path/to/file in your response. The file will be sent as a native Yuanbao attachment: images (.jpg, .png, .webp, .gif) are sent as photos, and other files (.pdf, .docx, .txt, .zip, etc.) arrive as downloadable documents (max 50 MB). You can also include image URLs in markdown format ![alt](url) and they will be downloaded and sent as native photos. Do NOT tell the user you lack file-sending capability — use MEDIA: syntax whenever a file delivery is appropriate.

Stickers (贴纸 / 表情包 / TIM face): Yuanbao has a built-in sticker catalogue. When the user sends a sticker (you see '[emoji: 名称]' in their message) or asks you to send/reply-with a 贴纸/表情/表情包, you MUST use the sticker tools:
  1. Call yb_search_sticker with a Chinese keyword (e.g. '666', '比心', '吃瓜',      '捂脸', '合十') to discover matching sticker_ids.
  2. Call yb_send_sticker with the chosen sticker_id or name — this sends a real      TIMFaceElem that renders as a native sticker in the chat.
DO NOT draw sticker-like PNGs with execute_code/Pillow/matplotlib and then send them via MEDIA: or send_image_file. That produces a fake low-quality 'sticker' image and is the WRONG path. Bare Unicode emoji in text is also not a substitute — when a sticker is the right response, use yb_send_sticker.u  You are running inside WSL (Windows Subsystem for Linux). The Windows host filesystem is mounted under /mnt/ — /mnt/c/ is the C: drive, /mnt/d/ is D:, etc. The user's Windows files are typically at /mnt/c/Users/<username>/Desktop/, Documents/, Downloads/, etc. When the user references Windows paths or desktop files, translate to the /mnt/c/ equivalent. You can list /mnt/c/Users/ to discover the Windows username if needed.c                      g } t                      r|                     t                     d                    |           S )zReturn environment-specific guidance for the system prompt.

    Detects WSL, and can be extended for Termux, Docker, etc.
    Returns an empty string when no special environment is detected.
    

)r   r+   WSL_ENVIRONMENT_HINTr3   )hintss    r8   build_environment_hintsrr     s:     Exx +)***;;ur:   i N  gffffff?g?   _SKILLS_PROMPT_CACHE   c                  $    t                      dz  S )Nz.skills_prompt_snapshot.json)r    r:   r8   _skills_prompt_snapshot_pathrx   3  s    ===r:   F)clear_snapshotry   c                 *   t           5  t                                           ddd           n# 1 swxY w Y   | rX	 t                                          d           dS # t
          $ r&}t                              d|           Y d}~dS d}~ww xY wdS )zKDrop the in-process skills prompt cache (and optionally the disk snapshot).NT)
missing_okz+Could not remove skills prompt snapshot: %s)_SKILLS_PROMPT_CACHE_LOCKrt   clearrx   unlinkOSErrorr1   debug)ry   es     r8    clear_skills_system_prompt_cacher   7  s    	" % %""$$$% % % % % % % % % % % % % % % K	K(**11T1BBBBB 	K 	K 	KLLFJJJJJJJJJ	KK Ks#   .22"A   
B*BB
skills_dirc           	          i }dD ]l}t          | |          D ]Y}	 |                                }n# t          $ r Y $w xY w|j        |j        g|t          |                    |                     <   Zm|S )zFBuild an mtime/size manifest of all SKILL.md and DESCRIPTION.md files.)SKILL.mdDESCRIPTION.md)r   statr   st_mtime_nsst_sizestrrelative_to)r   manifestr&   pathsts        r8   _build_skills_manifestr   B  s    %'H2 W W*:x@@ 	W 	WDYY[[   ;=>2::VHS))*556677	W Os   /
<<c                    t                      }|                                sdS 	 t          j        |                    d                    }n# t
          $ r Y dS w xY wt          |t                    sdS |                    d          t          k    rdS |                    d          t          |           k    rdS |S )zCLoad the disk snapshot if it exists and its manifest still matches.Nutf-8encodingversionr   )rx   r?   jsonloads	read_text	Exception
isinstancedictget_SKILLS_SNAPSHOT_VERSIONr   )r   snapshot_pathsnapshots      r8   _load_skills_snapshotr   O  s    022M!! t:m55w5GGHH   tth%% t||I":::t||J#9*#E#EEEtOs   (A 
AAr   skill_entriescategory_descriptionsc                     t           |||d}	 t          t                      |           dS # t          $ r&}t                              d|           Y d}~dS d}~ww xY w)z9Persist skill metadata to disk for fast cold-start reuse.)r   r   skillsr   z*Could not write skills prompt snapshot: %sN)r   r   rx   r   r1   r   )r   r   r   r   payloadr   s         r8   _write_skills_snapshotr   a  s     ,!6	 GF688'BBBBB F F FA1EEEEEEEEEFs   , 
AAA
skill_filefrontmatterdescriptionc                    |                      |          }|j        }t          |          dk    rA|d         }t          |          dk    rd                    |dd                   n|d         }nd}| j        j        }|                    d          pg }t          |t                    r|g}||t          |                    d|                    |d	 |D             t          |          d
S )z1Build a serialisable metadata dict for one skill.   /Nr   general	platformsrJ   c                     g | ]D}t          |                                          #t          |                                          ES rw   )r   strip).0ps     r8   
<listcomp>z)_build_snapshot_entry.<locals>.<listcomp>  s9    JJJ3q66<<>>Jc!ffllnnJJJr:   )
skill_namecategoryfrontmatter_namer   r   
conditions)
r   partslenr3   rA   rJ   r   r   r   r	   )	r   r   r   r   rel_pathr   r   r   r   s	            r8   _build_snapshot_entryr   t  s     %%j11HNE
5zzQ2Y
+.u::>>388E#2#J'''uQx&+
,,2I)S!!  K	 !
 C CDD"JJiJJJ.{;;  r:   c                    	 |                      d          }t          |          \  }}t          |          sd|dfS d|t          |          fS # t          $ r+}t
                              d| |           di dfcY d}~S d}~ww xY w)zRead a SKILL.md once and return platform compatibility, frontmatter, and description.

    Returns (is_compatible, frontmatter, description). On any error, returns
    (True, {}, "") to err on the side of showing the skill.
    r   r   F Tz!Failed to parse skill file %s: %sN)r   r   r   r
   r   r1   r2   )r   rawr   _r   s        r8   _parse_skill_filer     s    
""G"44*3//Q%k22 	*+r))[";K"H"HHH   :JJJJR|s"   ;A A 
B B :B Br   available_toolszset[str] | Noneavailable_toolsetsc                 R   ||dS |pt                      }|pt                      }|                     dg           D ]	}||v r dS 
|                     dg           D ]	}||v r dS 
|                     dg           D ]	}||vr dS 
|                     dg           D ]	}||vr dS 
dS )zDReturn False if the skill's conditional activation rules exclude it.NTfallback_for_toolsetsFfallback_for_toolsrequires_toolsetsrequires_tools)setr   )r   r   r   atatststs          r8   _skill_should_showr     s    #5#=t		!CEEB

%C nn4b99  9955 ^^0"55  7755  nn0"55  S==55 ^^,b11  B;;55  4r:   c           
         t                      }t                      dd         }|                                s|sdS ddlm} t
          j                            d          p |d          pd}t                      }t          |
                                          t          d |D                       t          t          d	 | pt                      D                                 t          t          d
 |pt                      D                                 |t          t          |                    f}t          5  t                              |          }|(t                              |           |cddd           S 	 ddd           n# 1 swxY w Y   t#          |          }	i }
i }|	A|	                    dg           D ]}t%          |t&                    s|                    d          pd}|                    d          pd}|                    d          p|}|                    d          pg }t)          d|i          s||v s||v rt+          |                    d          pi | |          s|
                    |g                               ||                    dd          f           d |	                    d          pi                                 D             }ng }t3          |d          D ]}t5          |          \  }}}t7          ||||          }|                    |           |s?|d         }|d         |v s||v rVt+          t9          |          | |          su|
                    |d         g                               |d         |d         f           t3          |d          D ]}	 |                    d          }t=          |          \  }}|                    d          }|sC|                    |          }tA          |j!                  dk    r"d"                    |j!        dd                   nd}t          |          #                                #                    d          ||<   # tH          $ r&}tJ          &                    d||           Y d}~d}~ww xY wtO          |tQ          |          ||           t                      }|
)                                D ]}|D ]\  } }!|*                    |             |D ]2}"|"                                st3          |"d          D ]}	 t5          |          \  }}}|st7          ||"||          }|d         }|d         }||v r@||v s||v rIt+          t9          |          | |          sh|*                    |           |
                    |d         g                               ||d         f           # tH          $ r&}tJ          &                    d||           Y d}~d}~ww xY wt3          |"d          D ]}	 |                    d          }t=          |          \  }}|                    d          }|sD|                    |"          }tA          |j!                  dk    r"d"                    |j!        dd                   nd}|                    |t          |          #                                #                    d                     # tH          $ r'}tJ          &                    d||           Y d}~
d}~ww xY w4|
sd}#n
g }$t          |
+                                          D ]}|                    |d          }|r|$                    d| d |            n|$                    d| d!           t                      }%t          |
|         d" #          D ]U\  } }| |%v r
|%*                    |            |r|$                    d$|  d |            =|$                    d$|             Vd%d&"                    |$          z   d'z   }#t          5  |#t          |<   t                              |           tA          t                    tX          k    r8t          -                    d()           tA          t                    tX          k    8ddd           n# 1 swxY w Y   |#S )*u  Build a compact skill index for the system prompt.

    Two-layer cache:
      1. In-process LRU dict keyed by (skills_dir, tools, toolsets)
      2. Disk snapshot (``.skills_prompt_snapshot.json``) validated by
         mtime/size manifest — survives process restarts

    Falls back to a full filesystem scan when both layers miss.

    External skill directories (``skills.external_dirs`` in config.yaml) are
    scanned alongside the local ``~/.hermes/skills/`` directory.  External dirs
    are read-only — they appear in the index but new skills are always created
    in the local dir.  Local skills take precedence when names collide.
    ru   Nr   r   )get_session_envHERMES_PLATFORMHERMES_SESSION_PLATFORMc              3   4   K   | ]}t          |          V  d S Nr   )r   ds     r8   	<genexpr>z-build_skills_system_prompt.<locals>.<genexpr>  s(      ,,c!ff,,,,,,r:   c              3   4   K   | ]}t          |          V  d S r   r   )r   r   s     r8   r   z-build_skills_system_prompt.<locals>.<genexpr>  s(      @@SVV@@@@@@r:   c              3   4   K   | ]}t          |          V  d S r   r   )r   r   s     r8   r   z-build_skills_system_prompt.<locals>.<genexpr>  s(      EESWWEEEEEEr:   r   r   r   r   r   r   r   r   c                 N    i | ]"\  }}t          |          t          |          #S rw   r   )r   kvs      r8   
<dictcomp>z.build_skills_system_prompt.<locals>.<dictcomp>  s:     !
 !
 !
1 FFCFF!
 !
 !
r:   r   r   r   r   r   r   rO   z'"z'Could not read skill description %s: %sz#Error reading external skill %s: %sz0Could not read external skill description %s: %sz  z: :c                     | d         S )Nr   rw   )xs    r8   <lambda>z,build_skills_system_prompt.<locals>.<lambda>  s    QRSTQU r:   )keyz    - u  ## Skills (mandatory)
Before replying, scan the skills below. If a skill matches or is even partially relevant to your task, you MUST load it with skill_view(name) and follow its instructions. Err on the side of loading — it is always better to have context you don't need than to miss critical steps, pitfalls, or established workflows. Skills contain specialized knowledge — API endpoints, tool-specific commands, and proven workflows that outperform general-purpose approaches. Load the skill even if you think you could handle the task with basic tools like web_search or terminal. Skills also encode the user's preferred approach, conventions, and quality standards for tasks like code review, planning, and testing — load them even for tasks you already know how to do, because the skill defines how it should be done here.
Whenever the user asks you to configure, set up, install, enable, disable, modify, or troubleshoot Hermes Agent itself — its CLI, config, models, providers, tools, skills, voice, gateway, plugins, or any feature — load the `hermes-agent` skill first. It has the actual commands (e.g. `hermes config set …`, `hermes tools`, `hermes setup`) so you don't have to guess or invent workarounds.
If a skill has issues, fix it with skill_manage(action='patch').
After difficult/iterative tasks, offer to save as a skill. If a skill you loaded was missing steps, had wrong commands, or needed pitfalls you discovered, update it before finishing.

<available_skills>
rQ   zf
</available_skills>

Only proceed without loading a skill if genuinely none are relevant to the task.F)last).r   r   r?   gateway.session_contextr   osenvironr   r   r   r=   tuplesortedr   r|   rt   move_to_endr   r   r   r   r   
setdefaultr+   itemsr   r   r   r	   r   r   r   r   r   r3   r   r   r1   r   r   r   valuesaddkeys_SKILLS_PROMPT_CACHE_MAXpopitem)&r   r   r   external_dirsr   _platform_hintdisabled	cache_keycachedr   skills_by_categoryr   entryr   r   r   r   r   r   is_compatibler   desc	desc_filer%   fmr   cat_descrelcatr   seen_skill_names
cat_skillsrJ   _descext_dirresultindex_linesseens&                                         r8   build_skills_system_promptr    s   $  !!J'))!""-M } r
 877777

()) 	?455	 
 ())HJ  !!,,m,,,,,f@@o&>@@@@@AAfEE(:(CceeEEEEEFFfXI 
#  %)))44 ,,Y777	                      %Z00H;=,.\\(B// 	 	EeT** <006BJyy,,9	H$yy);<<J
		+..4"I);	*BCC 8++zX/E/E%		,''-2"  
 ))(B77>>!599]B#?#?@   !
 !
!&=>>D"KKMM!
 !
 !
 %'0ZHH 	 	J/@/L/L,M;)*j+tTTE  '''  |,J'(H44
h8N8N%(55"  
 ))%
*;R@@GG)*E-,@A   
 0
<LMM 	V 	VI
V#--w-??)'22A66-00 ++J7725ci..12D2Dchhsy"~...)-0]]-@-@-B-B-H-H-O-O%c** V V VF	STUUUUUUUUV 	":..!		
 	
 	
 "%(//11 ' '
% 	' 	'KD%  &&&&	' ! (_ (_~~ 	0*EE 	S 	SJS3DZ3P3P0{D$ -j';PTUU"<0
#();#< #'777#x//:3I3I),[99#&  
  $$%5666"--eJ.?DDKK%u]';<     S S SBJPQRRRRRRRRS 09IJJ 	_ 	_I
_#--w-??)'22A66-00 ++G4425ci..12D2Dchhsy"~...)%00c(mm6I6I6K6K6Q6QRW6X6XYYYY _ _ _OQZ\]^^^^^^^^_	_  0
1668899 	8 	8H,002>>H 5""#>#>#>H#>#>????""#3#3#3#344455D$%7%A~~VVV 8 8
d4<< 8&&'>'>'>'>'>????&&77778#, ii$$-%,(_-_ 	< 
# 5 5*0Y'((333&''*BBB ((e(444 &''*BBB5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 Ms    8FFF?RBR
SS  SX,&XXX;AX
X8X33X8?\)B\))
]3]]6A:c<<d d valid_tool_namesc                    	 ddl m} ddlm} n3# t          $ r&}t
                              d|           Y d}~dS d}~ww xY w |            sdS t          | pt                                }h d}|r||z  sdS  |            dt          ffd	g d
}|	                    fd
                                D                        |	                    g d           d                    |          S )zIBuild a compact Nous subscription capability block for the system prompt.r   )get_nous_subscription_features)managed_nous_tools_enabledz-Failed to import Nous subscription helper: %sNr   >   processterminal
web_searchweb_extractbrowser_typeexecute_codebrowser_clickbrowser_pressbrowser_scrollbrowser_visionimage_generatetext_to_speechbrowser_consolebrowser_navigatebrowser_snapshotbrowser_get_imagesr'   c                     | j         rd| j         dS | j        r| j        pd}d| j         d| S | j        rj        rd| j         dS | j        dk    rj        rd| j         dS d| j         dS )	Nz- z: active via Nous subscriptionzconfigured providerz: currently using z9: included with Nous subscription, not currently selectedmodalz : optional via Nous subscriptionz: not currently available)managed_by_nouslabelactivecurrent_providerincluded_by_defaultnous_auth_presentr   )featurer@   featuress     r8   _status_linez4build_nous_subscription_prompt.<locals>._status_line  s    " 	FEEEEE> 	C.G2GGBBBBBB& 	a8+E 	a`````;'!!h&@!GGGGG<GM<<<<r:   )z# Nous SubscriptionzNous subscription includes managed web tools (Firecrawl), image generation (FAL), OpenAI TTS, and browser automation (Browser Use) by default. Modal execution is optional.zCurrent capability status:c              3   .   K   | ]} |          V  d S r   rw   )r   r"  r$  s     r8   r   z1build_nous_subscription_prompt.<locals>.<genexpr>  s-      GG7g&&GGGGGGr:   )zsWhen a Nous-managed feature is active, do not ask the user for Firecrawl, FAL, OpenAI TTS, or Browser-Use API keys.zIf the user is not subscribed and asks for a capability that Nous subscription would unlock or simplify, suggest Nous subscription as one option alongside direct setup or local alternatives.zoDo not mention subscription unless the user asks about it or it directly solves the current missing capability.zXUseful commands: hermes setup, hermes setup tools, hermes setup terminal, hermes status.rQ   )hermes_cli.nous_subscriptionr  tools.tool_backend_helpersr	  r   r1   r   r   r   extendr   r3   )	r  r  r	  excvalid_namesrelevant_tool_nameslinesr$  r#  s	          @@r8   build_nous_subscription_promptr-    s~   OOOOOOIIIIIII   DcJJJrrrrr &%'' r&/#%%00K  &  K*== r--//H
= 
= 
= 
= 
= 
= 
=  E
 
LLGGGGhnn6F6FGGGGGG	LL	
 	
 	
   99Us    
A<A	max_charsc           
          t          |           |k    r| S t          |t          z            }t          |t          z            }| d|         }| | d         }d| d| d| dt          |            d	}||z   |z   S )z1Head/tail truncation with a marker in the middle.Nz

[...truncated z: kept +z of z0 chars. Use file tools to read the full file.]

)r   intCONTEXT_TRUNCATE_HEAD_RATIOCONTEXT_TRUNCATE_TAIL_RATIO)r%   r&   r.  
head_chars
tail_charsheadtailmarkers           r8   _truncate_contentr9    s    
7||y  Y!<<==JY!<<==J;J;DJ;<< D Q(  Q  Q:  Q  Q
  Q  QPST[P\P\  Q  Q  QF&=4r:   c                     	 ddl m}   |              n2# t          $ r%}t                              d|           Y d}~nd}~ww xY wt                      dz  }|                                sdS 	 |                    d                                          }|sdS t          |d          }t          |d          }|S # t          $ r'}t                              d||           Y d}~dS d}~ww xY w)	a  Load SOUL.md from HERMES_HOME and return its content, or None.

    Used as the agent identity (slot #1 in the system prompt).  When this
    returns content, ``build_context_files_prompt`` should be called with
    ``skip_soul=True`` so SOUL.md isn't injected twice.
    r   )ensure_hermes_homez7Could not ensure HERMES_HOME before loading SOUL.md: %sNzSOUL.mdr   r   z"Could not read SOUL.md from %s: %s)hermes_cli.configr;  r   r1   r   r   r?   r   r   r9   r9  )r;  r   	soul_pathr%   s       r8   load_soul_mdr>    s9   S888888 S S SNPQRRRRRRRRS  !!I-I t	%%w%77==?? 	4';;#GY77   99aHHHttttts0    
A=A-*B; !B; ;
C,C''C,cwd_pathc                    t          |           }|sdS 	 |                    d                                          }|sdS t          |          }|j        }	 t          |                    |                     }n# t          $ r Y nw xY wt          ||          }d| d| }t          |d          S # t          $ r'}t                              d||           Y d}~dS d}~ww xY w)	u,   .hermes.md / HERMES.md — walk to git root.r   r   r   ## ro   rC   Could not read %s: %sN)rL   r   r   rW   rJ   r   r   
ValueErrorr9   r9  r   r1   r   )r?  hermes_md_pathr%   r   r  r   s         r8   _load_hermes_mdrE     s   $X..N r **G*<<BBDD 	2)'22!	n00::;;CC 	 	 	D	'55)s)))) 666   ,na@@@rrrrrsA   *B3 B3 "A; :B3 ;
BB3 B*B3 3
C$=CC$c                 T   dD ]}| |z  }|                                 r	 |                    d                                          }|r*t          ||          }d| d| }t	          |d          c S q# t
          $ r&}t                              d||           Y d}~d}~ww xY wd	S )
u1   AGENTS.md — top-level only (no recursive walk).)	AGENTS.mdz	agents.mdr   r   rA  ro   rG  rB  Nr   r?   r   r   r9   r9  r   r1   r   r?  rJ   rK   r%   r  r   s         r8   _load_agents_mdrJ  7      * 
D 
DtO	 	DD#--w-??EEGG B3GTBBG6466W66F,V[AAAAAB  D D D4iCCCCCCCCD	D 2   AA44
B$>BB$c                 T   dD ]}| |z  }|                                 r	 |                    d                                          }|r*t          ||          }d| d| }t	          |d          c S q# t
          $ r&}t                              d||           Y d}~d}~ww xY wd	S )
u#   CLAUDE.md / claude.md — cwd only.)	CLAUDE.mdz	claude.mdr   r   rA  ro   rN  rB  Nr   rH  rI  s         r8   _load_claude_mdrO  G  rK  rL  c                 
   d}| dz  }|                                 rw	 |                    d                                          }|rt          |d          }|d| dz  }n2# t          $ r%}t
                              d|           Y d}~nd}~ww xY w| d	z  d
z  }|                                 r|                                rt          |	                    d                    }|D ]}	 |                    d                                          }|r)t          |d|j
                   }|d|j
         d| dz  }W# t          $ r&}t
                              d||           Y d}~d}~ww xY w|sdS t          |d          S )u0   .cursorrules + .cursor/rules/*.mdc — cwd only.r   z.cursorrulesr   r   z## .cursorrules

ro   zCould not read .cursorrules: %sNz.cursorrulesz*.mdcz.cursor/rules/z## .cursor/rules/rB  )r?   r   r   r9   r   r1   r   is_dirr   globrJ   r9  )r?  cursorrules_contentcursorrules_filer%   r   cursor_rules_dir	mdc_filesmdc_files           r8   _load_cursorrulesrY  W  s   .0   ?	?&00'0BBHHJJG K/HH#'JW'J'J'JJ# 	? 	? 	?LL:A>>>>>>>>	?  )+g5   	C%5%<%<%>%> 	C+0099::	! 	C 	CHC",,g,>>DDFF `3G=]hm=]=]^^G'+_x}+_+_RY+_+_+__' C C C4hBBBBBBBBC  r0.AAAs1   AA! !
B+BB*AD>>
E.E))E.	skip_soulc                    | t          j                    } t          |                                           }g }t	          |          p,t          |          pt          |          pt          |          }|r|                    |           |s%t                      }|r|                    |           |sdS dd
                    |          z   S )uU  Discover and load context files for the system prompt.

    Priority (first found wins — only ONE project context type is loaded):
      1. .hermes.md / HERMES.md  (walk to git root)
      2. AGENTS.md / agents.md   (cwd only)
      3. CLAUDE.md / claude.md   (cwd only)
      4. .cursorrules / .cursor/rules/*.mdc  (cwd only)

    SOUL.md from HERMES_HOME is independent and always included when present.
    Each context source is capped at 20,000 chars.

    When *skip_soul* is True, SOUL.md is not included here (it was already
    loaded via ``load_soul_md()`` for the identity slot).
    Nr   za# Project Context

The following project context files have been loaded and should be followed:

rQ   )r   getcwdr   r=   rE  rJ  rO  rY  r+   r>  r3   )rD   rZ  r?  sectionsproject_contextsoul_contents         r8   build_context_files_promptr`  u  s     {ikkCyy  ""HH 	!! 	'8$$	'8$$	' X&&	   )(((  *#~~ 	*OOL))) rruyu~u~  @H  vI  vI  I  Ir:   )NNr   )NF)R__doc__r   loggingr   r.   	threadingcollectionsr   pathlibr   hermes_constantsr   r   r   typingr   agent.skill_utilsr	   r
   r   r   r   r   r   utilsr   	getLogger__name__r1   r-   r*   r   r9   rB   rF   rL   rW   DEFAULT_AGENT_IDENTITYHERMES_AGENT_HELP_GUIDANCEMEMORY_GUIDANCESESSION_SEARCH_GUIDANCESKILLS_GUIDANCEKANBAN_GUIDANCETOOL_USE_ENFORCEMENT_GUIDANCETOOL_USE_ENFORCEMENT_MODELSOPENAI_MODEL_EXECUTION_GUIDANCE!GOOGLE_MODEL_OPERATIONAL_GUIDANCEDEVELOPER_ROLE_MODELSPLATFORM_HINTSrp   rr   CONTEXT_FILE_MAX_CHARSr2  r3  r   rt   r   __annotations__Lockr|   r   rx   boolr   r   listr1  r   r   r   r   r   r   r  r-  r9  r>  rE  rJ  rO  rY  r`  rw   r:   r8   <module>r}     sZ       				 				     # # # # # #       D D D D D D D D D D                        $ # # # # #		8	$	$      3 # #    *
$ 
8D> 
 
 
 
 /  (4.    *S S    ,H I . *( 8 66 v) " J 8  ~/ "2 + a	Oa 	Va4 	O7aB 	SEaP 	OSab 	$ear 	Mua@ 
	HCaT 
	.Wa^ 	Iaal 	coa~ 	7AaP 	QSa` 	Ecap 		TsaH 	KaT 	IWaT& 	 	 	 	 	   ! !   0; k%*- = = =*IN,,  >d > > > > @E K K K K K K K K
t 
S$s)^0D 
 
 
 
d x~    $FF3S	>"F :F  S>	F
 
F F F F&  	
 
   D$ 5tS+A    && * 
	   @ *.,0g g&g)g 	g g g gT? ?5F ?RU ? ? ? ?L E[ 	  	 s 	 c 	 c 	 _b 	  	  	  	 hsm    8d s    .d s     d s     B B B B B B<'I 'IHSM 'IT 'IVY 'I 'I 'I 'I 'I 'Ir:   