| name | pai-workspace |
| description | Persistent knowledge-base workspace for Hermes Agent. Ingests, indexes, and searches domain-specific documents (OSINT newsletters, security SOPs, research notes) in a curated filesystem hierarchy. Use when user says ingest document, add to knowledge base, search my notes, find in workspace, index folder, workspace search, osint archive, or sop lookup. |
pai-workspace skill
When to use
User intent:
- "ingest this PDF/newsletter into my OSINT workspace" → ingest + classify
- "search my security SOPs for incident response" → workspace search
- "index my downloads folder" → bulk ingest with auto-classification
- "what do I know about X" → search across all workspace domains
- "add this to my knowledge base" → ingest with domain hint
- "list my workspace domains" → show directory structure + manifest
Workspace layout
${PAI_WORKSPACE_DIR}/
├── osint/
│ ├── newsletters/
│ ├── tools/
│ └── techniques/
├── security/
│ ├── sops/
│ ├── playbooks/
│ └── policies/
├── research/
│ ├── people/
│ ├── companies/
│ └── ideas/
└── .manifest.jsonl # auto-generated index
Domains are auto-created on first ingest. Users may add custom domains by creating subdirectories.
Ingest algorithm
Implemented by bin/pai-workspace-ingest <source> [domain-hint]. Per file it:
- Resolves the source (file, directory → recurse, or URL →
curl -fsSL to temp).
- Picks a domain: explicit hint → filename/path keyword match (table below) →
research/misc.
- Sanitizes the name (lowercase,
[a-z0-9._-] only, ISO-date prefix if absent).
- Deduplicates by SHA-256 against the manifest (skip if seen).
- Copies (
cp --preserve=timestamps, never mv) into ${PAI_WORKSPACE_DIR}/<domain>/.
- Appends a JSONL manifest entry (
id, sha256, domain, path, size, ingestedAt).
After a batch, Hermes sends pai-pulse-send --message "Ingested <n> files to <domain>" if >0 were added.
Domain keyword table
| Domain | Keywords |
|---|
| osint/newsletters | newsletter, osint weekly, week in review, intel, briefing |
| osint/tools | tool, github, repository, script, scraper, crawler |
| osint/techniques | technique, method, methodology, tutorial, guide, how-to |
| security/sops | sop, procedure, standard, runbook, protocol, checklist |
| security/playbooks | playbook, incident response, ir, threat, hunt, forensics |
| security/policies | policy, compliance, gdpr, soc2, framework, nist, iso27001 |
| research/people | person, researcher, analyst, profile, biography |
| research/companies | company, firm, vendor, product, startup, acquisition |
| research/ideas | idea, concept, theory, thesis, hypothesis, insight |
Search algorithm
Implemented by bin/pai-workspace-search <query> [domain-scope]. It maps the phrasing to a scope (whole workspace, or a subtree like osint/ / security/sops/), runs ripgrep (--smart-case, context lines; grep -r -i fallback), extracts PDFs via pdftotext when present, and returns matching lines (capped at 50) as JSON. Ranking (filename/recency/frequency weighting, top-N) is left to Hermes over those raw matches — the wrapper does not score.
Manifest maintenance
The manifest (.manifest.jsonl) is append-only and used only for dedup. Losing it is non-fatal — the worst case is a re-ingested duplicate. A standalone reindex (rescan tree → recompute SHA-256 → rewrite manifest, no copying) is not yet implemented; do not re-run pai-workspace-ingest on the workspace root to rebuild it, as ingest copies files and would nest them.
Inputs
| Env var | Default | Purpose |
|---|
PAI_WORKSPACE_DIR | ${HERMES_HOME:-$HOME/.hermes}/workspace | Root of knowledge base |
PAI_WORKSPACE_MAX_SIZE_MB | 2048 | Per-domain soft quota (warn, don't block) |
PAI_PULSE_URL | http://127.0.0.1:31337 | Pulse notify endpoint |
Companion tools
Two hardened wrappers ship in bin/:
bin/pai-workspace-ingest — file copy + sha256 + manifest append. Refuses any source or domain hint whose resolved path escapes the workspace root (no traversal), JSON-escapes manifest fields, and refuses files >100MB.
bin/pai-workspace-search — ripgrep wrapper with PDF text extraction fallback. Rejects shell metacharacters (;, |, $(), backticks, <>) and any query starting with -, and binds the pattern via -e/-- so it can never be smuggled in as an rg/grep flag (argv-injection / --pre RCE).
Both are POSIX shell, shellcheck-clean, and callable directly or via Hermes terminal toolset.
Cost
ZERO AI cost for ingest, search, and reindex. Pure filesystem + ripgrep operations. AI is only invoked if the user asks Hermes to summarize or analyze found documents after search.
Execution in constrained shells
When terminal returns pending_approval, switch to execute_code with Python subprocess.run(). The wrappers in bin/ are pure shell and produce identical output under either invocation path.
Caveats
- PDF support requires
pdftotext from poppler-utils. Without it, PDFs are in manifest but skipped during search.
- No semantic search: This skill uses ripgrep (keyword/regex), not vector embeddings. For semantic search, pair with a Mem0 or Supermemory provider (separate Hermes memory plugin).
- Workspace root enforcement: Ingest and search abort if the resolved path escapes
${PAI_WORKSPACE_DIR} via symlink or traversal.
- 100MB file limit: Large files are refused at ingest to prevent quota exhaustion.
- No auto-delete: This skill never deletes workspace files. Removal is manual
rm (then optionally prune the manifest line).
Skill chain
pai-workspace ingest → calls pai-pulse-send for mobile notification.
pai-workspace search → may chain to omc if the user asks to analyze/summarize the results.
Example flows
- "ingest all PDFs in ~/Downloads/osint-dump" → run
pai-workspace-ingest per file, auto-classify (osint/newsletters or osint/tools), update manifest, pulse notification.
- "find my incident-response SOPs mentioning containment" →
pai-workspace-search scoped to security/sops/, return ranked snippets, offer summary.
References
- PAI filesystem-as-context philosophy:
Personal_AI_Infrastructure/README.md §Memory that compounds.
- For a semantic layer, pair with a vector/embedding memory provider via Hermes's memory plugin system (see the Hermes Agent docs). This skill deliberately stays keyword-only.