Initial Arka plugin registry: Official plugins mirror + Arka-signed index

11 plugins from github.com/librefang/librefang-registry plugins/.
index.json / index.json.sig are signed with Arka's Ed25519 key
(not upstream stats.librefang.ai). Private key is not in this repo.
This commit is contained in:
ixoblakp committed 2026-09-01 10:22:07 +03:00
commit 8dec8f6038
62 files changed
+5189

No files matched your search

+32
View File
@@ -0,0 +1,32 @@
# keyword-memory
Extracts keywords and named entities from user messages and returns them as contextual memories. Gives agents awareness of conversation topics without requiring external NLP libraries.
## Extraction Techniques
- **Plain keywords**: Splits words, filters English stopwords (~50 words), removes short tokens
- **Capitalized phrases**: Detects multi-word proper nouns and mid-sentence capitalized words
- **Emails and URLs**: Regex pattern matching
- **Numbers with units**: e.g. 500ms, 10GB, 3.5GHz
- **Dates**: YYYY-MM-DD, MM/DD/YYYY, DD.MM.YYYY formats
- **Technical terms**: camelCase, snake_case, dotted identifiers (e.g. `os.path`)
Results are deduplicated and capped at 10 keywords.
## Hooks
| Hook | Script | Description |
|------|--------|-------------|
| ingest | `hooks/ingest.py` | Extracts keywords from the user message and returns them as a memory fragment |
## Example Output
```json
{"type": "ingest_result", "memories": [{"content": "[keyword-memory] Key topics: GPT-4, machine_learning, data pipeline, https://example.com"}]}
```
If no meaningful keywords are found, returns an empty memories list.
## Usage
Installed automatically when enabled in agent configuration. No external dependencies required (stdlib only).
+14
View File
@@ -0,0 +1,14 @@
# keyword-memory hooks
Python hook scripts for the keyword-memory plugin. Each script reads a JSON request from stdin and writes a JSON response to stdout.
## Scripts
| Script | Hook | Description |
|--------|------|-------------|
| `ingest.py` | ingest | Receives `{"message": "..."}`, extracts keywords and named entities, returns them as memory fragments |
## Protocol
- **Input**: JSON object on stdin (fields vary by hook type)
- **Output**: JSON object on stdout (`ingest_result` with memories)
+148
View File
@@ -0,0 +1,148 @@
#!/usr/bin/env python3
"""Keyword memory ingest hook.
Extracts keywords and named entities from user messages and returns
them as contextual memories so agents have topic awareness.
Receives via stdin:
{"type": "ingest", "agent_id": "...", "message": "user message text"}
Prints to stdout:
{"type": "ingest_result", "memories": [{"content": "..."}]}
"""
import json
import re
import sys
# Compact English stopword set (~50 common words)
STOPWORDS = frozenset({
"a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for",
"of", "with", "by", "from", "is", "are", "was", "were", "be", "been",
"being", "have", "has", "had", "do", "does", "did", "will", "would",
"could", "should", "may", "might", "shall", "can", "need", "must",
"it", "its", "i", "me", "my", "you", "your", "he", "she", "we",
"they", "them", "their", "this", "that", "these", "those", "what",
"which", "who", "how", "when", "where", "why", "if", "then", "so",
"not", "no", "just", "also", "very", "too", "about", "up", "out",
"all", "some", "any", "each", "every", "into", "over", "after",
})
# Minimum word length for plain keyword extraction
MIN_WORD_LEN = 3
# Maximum keywords to return
MAX_KEYWORDS = 10
# Pattern: email addresses
RE_EMAIL = re.compile(r"[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}")
# Pattern: URLs (http/https/ftp) — excludes trailing punctuation
RE_URL = re.compile(r"https?://[^\s,)>]+(?<=[a-zA-Z0-9/])|ftp://[^\s,)>]+(?<=[a-zA-Z0-9/])")
# Pattern: numbers with units (e.g. 500ms, 10GB, 3.5GHz, 200k)
RE_NUMBER_UNIT = re.compile(r"\b\d+(?:\.\d+)?(?:ms|s|min|hr|h|kb|mb|gb|tb|ghz|mhz|hz|k|m|px|em|rem|%)\b", re.IGNORECASE)
# Pattern: dates (YYYY-MM-DD, MM/DD/YYYY, DD.MM.YYYY)
RE_DATE = re.compile(
r"\b\d{4}-\d{2}-\d{2}\b"
r"|\b\d{1,2}/\d{1,2}/\d{2,4}\b"
r"|\b\d{1,2}\.\d{1,2}\.\d{2,4}\b"
)
# Pattern: camelCase or PascalCase identifiers
RE_CAMEL = re.compile(r"\b[a-z]+(?:[A-Z][a-z0-9]+)+\b|\b(?:[A-Z][a-z0-9]+){2,}\b")
# Pattern: snake_case identifiers (at least one underscore)
RE_SNAKE = re.compile(r"\b[a-zA-Z][a-zA-Z0-9]*(?:_[a-zA-Z0-9]+)+\b")
# Pattern: dotted technical terms (e.g. api.endpoint, os.path)
RE_DOTTED = re.compile(r"\b[a-zA-Z][a-zA-Z0-9]*(?:\.[a-zA-Z][a-zA-Z0-9]*)+\b")
def extract_patterns(text):
"""Extract structured patterns: emails, URLs, numbers+units, dates, tech terms."""
found = []
for pattern in (RE_EMAIL, RE_URL, RE_NUMBER_UNIT, RE_DATE, RE_CAMEL, RE_SNAKE, RE_DOTTED):
found.extend(pattern.findall(text))
return found
def extract_capitalized_phrases(text):
"""Detect consecutive capitalized words (likely proper nouns / named entities).
Skips single capitalized words at sentence boundaries by requiring
either multi-word phrases or mid-sentence capitalized words.
"""
phrases = []
# Find sequences of 2+ capitalized words
for match in re.finditer(r"\b([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\b", text):
phrases.append(match.group(0))
# Find single capitalized words that are NOT at sentence start
# (preceded by a lowercase letter, comma, or mid-sentence punctuation)
for match in re.finditer(r"(?<=[a-z,;]\s)([A-Z][a-zA-Z0-9]+)\b", text):
word = match.group(1)
if word.lower() not in STOPWORDS and len(word) >= MIN_WORD_LEN:
phrases.append(word)
return phrases
def extract_plain_keywords(text):
"""Split text into words and filter out stopwords and short tokens."""
# Remove URLs and emails first so they don't pollute word splitting
cleaned = RE_URL.sub(" ", text)
cleaned = RE_EMAIL.sub(" ", cleaned)
# Split on non-alphanumeric (keep hyphens inside words)
words = re.findall(r"[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9]|[a-zA-Z]", cleaned)
keywords = []
for w in words:
lower = w.lower()
if lower not in STOPWORDS and len(lower) >= MIN_WORD_LEN:
keywords.append(lower)
return keywords
def deduplicate_keywords(items):
"""Deduplicate while preserving insertion order. Case-insensitive for plain words."""
seen = set()
result = []
for item in items:
key = item.lower()
if key not in seen:
seen.add(key)
result.append(item)
return result
def main():
request = json.loads(sys.stdin.read())
message = request.get("message", "")
if not message.strip():
print(json.dumps({"type": "ingest_result", "memories": []}))
return
# Collect keywords from all extraction methods (patterns first for priority)
all_keywords = []
all_keywords.extend(extract_patterns(message))
all_keywords.extend(extract_capitalized_phrases(message))
all_keywords.extend(extract_plain_keywords(message))
# Deduplicate and cap at MAX_KEYWORDS
keywords = deduplicate_keywords(all_keywords)[:MAX_KEYWORDS]
if not keywords:
print(json.dumps({"type": "ingest_result", "memories": []}))
return
topic_str = ", ".join(keywords)
memories = [
{"content": f"[keyword-memory] Key topics: {topic_str}"}
]
print(json.dumps({"type": "ingest_result", "memories": memories}))
if __name__ == "__main__":
main()
+39
View File
@@ -0,0 +1,39 @@
name = "keyword-memory"
version = "0.1.0"
description = "Extracts keywords and named entities from user messages and returns them as contextual memories"
author = "librefang"
[hooks]
ingest = "hooks/ingest.py"
[i18n.zh]
name = "关键词记忆"
description = "从用户消息中抽取关键词与命名实体,作为上下文记忆返回。"
[i18n.zh-TW]
name = "關鍵字記憶"
description = "從使用者訊息中抽取關鍵字與命名實體,作為上下文記憶返回。"
[i18n.ja]
name = "キーワードメモリ"
description = "ユーザーメッセージからキーワードと固有表現を抽出し、文脈メモリとして返す。"
[i18n.ko]
name = "키워드 메모리"
description = "사용자 메시지에서 키워드와 개체명을 추출하여 컨텍스트 메모리로 반환."
[i18n.de]
name = "Keyword-Gedächtnis"
description = "Extrahiert Schlüsselwörter und Named-Entities aus Nutzernachrichten und liefert sie als Kontext-Gedächtnis zurück."
[i18n.es]
name = "Memoria por palabras clave"
description = "Extrae palabras clave y entidades de los mensajes del usuario y las devuelve como memoria contextual."
[i18n.fr]
name = "Mémoire par mots-clés"
description = "Extrait mots-clés et entités nommées des messages utilisateur et les restitue comme mémoire contextuelle."
[integrity]
"hooks/ingest.py" = "6e9de6524c394e6879e4ab694dfdfbffb2bd423c3df2ef374dd59bf006836330"