Skip to content
Back to skills

Kb Search

BSecurity

This skill should be used when searching the knowledge base for files matching keywords or YAML frontmatter facets (tag, category) across domains.

  • 16 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
data-aigoc++shellbashrailsawsdebugginggitapiperformance

Works with

  • claude code
  • api

Security analysis

B88/100
  • criticalSends environment variables or credentials to an external URL

Pro scans all 2 files and shows the line behind each finding

Scanned October 7, 2026

npx -y skills add jikig-ai/soleur --skill kb-search --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Kb Search?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Kb Search
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/jikig-ai-kb-search/badge)](https://www.skillsdirectory.com/skills/jikig-ai-kb-search)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: kb-search
description: "This skill should be used when searching the knowledge base for files matching keywords or YAML frontmatter facets (tag, category) across domains."
---

<!-- grok-harness-invoke:start -->
**Grok Build (`plugins/soleur/lib/harness.ts` `invokeSkill()`):** Read this SKILL.md in this process and run it to completion. A one-segment `soleur:<name>` in this document names a SKILL — on Grok Build, Read `plugins/soleur/skills/<name>/SKILL.md` in this process; it is not a nested tool_use. A multi-segment id such as `soleur:<domain>:<name>` names an AGENT: spawn it, never Read it, and on Grok Build spawn_subagent takes the id with its colons replaced by hyphens (`agentIdToGrokSubagentType`). **Claude Code:** Skill tool for a skill (`soleur:<name>`), Task tool with `subagent_type` for an agent. Forbidden is executing a subset, not the Read.
<!-- grok-harness-invoke:end -->

# KB Search

Search the knowledge base across all domains. Returns learnings-scoped title matches first (tier 1, cap 8), then learnings-content matches (tier 2, cap 12). Optional `--tag` and `--category` flags filter `knowledge-base/project/learnings/` by YAML frontmatter before grep runs, cutting result-set noise during cross-referencing.

## Arguments

<search_query> #$ARGUMENTS </search_query>

Accepted forms:

- `soleur:kb-search <keyword>` — existing behavior, unchanged.
- `soleur:kb-search --tag <value>` — filter learnings by frontmatter `tags:`.
- `soleur:kb-search --category <value>` — filter learnings by frontmatter `category:`.
- `soleur:kb-search --tag <value> --category <value> <keyword>` — combine (AND).
- `soleur:kb-search --no-paraphrase <keyword>` — sensitive-query manual override; skips Phase 2.5 entirely. Use when the query carries a secret, customer name, or in-flight incident reference (anything you would not want forwarded to a paraphrase generator).
- `soleur:kb-search --clear-cache` — remove `.soleur/cache/kb-search/query-paraphrases.ndjson`. The cache regenerates lazily on the next non-`--no-paraphrase` invocation.

If `$ARGUMENTS` is empty, ask: "What would you like to search for in the knowledge base?"

### Flag Semantics

- Values are matched **case-insensitively** and **as literals** (fixed-string, not regex). `--tag n+1` matches the literal tag `n+1`.
- Duplicate flags (`--tag a --tag b`) error with usage hint.
- Unknown flags (`--taag`) error with usage hint listing supported flags.
- Faceted queries scope to `knowledge-base/project/learnings/` only; other KB subtrees are skipped for facet lookups.
- Tag-only or category-only queries emit `title + path` per file (no content snippet). Combined with a keyword, snippets come from the keyword match.

## Privacy & Cost

- Runtime paraphrase is inline; no countable spend. If Option B is ever adopted, caps land with it.
- Operator queries MAY be sent to the Anthropic API (Haiku) by [scripts/learning-retrieval-bench.sh](../../../../scripts/learning-retrieval-bench.sh) only when the operator runs the bench-rerun gate (explicit opt-in via the existing `--confirm` flag). No runtime invocation forwards queries outside the executing agent's existing context.
- Use `--no-paraphrase` for queries containing secrets, customer PII, or in-flight incident details. The skill auto-refuses paraphrase when the query matches the sensitive-shape regex (value-shape blobs + vendor key prefixes for Anthropic, OpenAI, GitHub, AWS, Stripe, Slack, JWT — see Phase 2.5 for the full byte-exact regex); `--no-paraphrase` is the manual override for queries that are sensitive but don't trip the regex (e.g., a customer name as a bare topic keyword).
- Cache location: `.soleur/cache/kb-search/query-paraphrases.ndjson` (gitignored per `.soleur/` convention; refuses to write outside `.soleur/cache/`). File mode 0600, directory mode 0700 (re-applied on every append so a pre-existing looser-mode directory is hardened). The cache row stores `{sha256, query, variants, cached_at}` — `query` is stored in plaintext to enable manual inspection / debugging; rely on the directory + file modes (and `--clear-cache`) for confidentiality. TTL: 14 days.

## Execution

**Support-persona path (no Bash).** If you are running as the **Soleur
Support** persona — your system prompt identifies you as "Soleur Support"
and `kb-search` is your only loaded skill — do NOT execute any of the
`bash`/` ```bash ` blocks in the phases below. On the support surface
every non-safe-bash Bash call is denied AND records a support escalation
that renders a false "Ask an agent" handoff at turn end. The corpus is
already on disk under the session cwd: `knowledge-base/INDEX.md`,
`knowledge-base/kb-tags.txt`, `knowledge-base/kb-categories.txt`, and
`knowledge-base/project/learnings/*.md` are committed inside the deployed
plugin root (all `knowledge-base/…` paths in the support section are
relative to the support session cwd = plugin root, i.e.
`plugins/soleur/knowledge-base/` at repo root — not the operator KB,
which `denyReadExtra` obscures) — there is no index generator to run
there (the `--soft` ensure call is for operator checkouts; skip it
silently). If a Bash call IS denied with a read-only app-help message, do
not retry Bash — switch to this path.

| Phase | Tool-path equivalent |
|---|---|
| 0 arg parse | unchanged — pure in-context |
| 1 facet validation | Read `knowledge-base/kb-tags.txt` and/or `kb-categories.txt`; match the flag value case-insensitively as a WHOLE LINE (fixed-string semantics — do not reproduce the `\b` regex, see #8473); on miss emit the same `No matches. Valid values: …` output |
| 2 facet filter | Glob `knowledge-base/project/learnings/*.md`, then Read each file's frontmatter (the corpus is ~a dozen files) or Grep `^tags:`/`^category:` lines; AND-combine flags as today |
| 2.5 paraphrase | still runs inline (Option C is agent-inline); SKIP both `kb-search-cache.sh` calls — every non-allowlisted Bash call on this surface denies AND records a support escalation (the cache write would fail in the read-only sandbox anyway) |
| 3 keyword search | Tier 1: Grep `knowledge-base/INDEX.md` for the keyword, keep only links rooted at `knowledge-base/project/learnings/` (cap 8); Tier 2: Grep content under `knowledge-base/project/learnings/` excluding `archive/` (cap 12). When facet flags were given, restrict BOTH tiers to the Phase-2 surviving file list (same AND semantics as the bash path). Same dedupe-by-path merge |
| 4 display | unchanged, EXCEPT the zero-result remediation: drop the scripts/ensure-kb-index.sh regenerate suggestion (a shell-out denies + escalates on this surface) — offer the spelling/keyword/tag-file suggestions only |
| `--clear-cache` | unavailable on the support path (it needs a delete — there is no tool for it); say so instead of attempting it |

### Phase 0: Parse Arguments

Parse `$ARGUMENTS` into `$TAG`, `$CATEGORY`, and `$KEYWORD`. Track whether each flag was already seen to detect duplicates. On duplicate or unknown flag, emit:

```text
Usage: soleur:kb-search [--tag VALUE] [--category VALUE] [KEYWORD]
```

Then exit without searching.

### Phase 1: Facet Validation (only if `--tag` or `--category` supplied)

**Regenerate first — the facet files are untracked caches (ADR-235), so they are routinely absent in a fresh clone and routinely stale on a branch that just added a learning.** This call is the fix for the #8177 class, where a search reported "no prior art" for a file written minutes earlier. It is silent and costs ~60 ms when the index is already fresh; it only regenerates (~3 s) when something eligible changed.

```bash
[ -f scripts/ensure-kb-index.sh ] && bash scripts/ensure-kb-index.sh --soft || true
```

Then validate. **A missing artifact is NOT an error here** — `--soft` cannot regenerate in a repo that has no generator, which is every self-hosted install, and exiting 1 there would make `--tag` unusable with a Soleur-only remediation the user cannot run. Fall back to reading the frontmatter directly:

The two arms below are ONE `if/else`, not two blocks (#8384 review). An earlier draft ended the
fallback with the comment "skip the artifact check" and then ran the artifact check
unconditionally anyway — so on a fresh checkout the fallback passed, the artifact grep failed
on the absent file, and the user was told `Valid values: knowledge-base/kb-tags.txt`, naming a
file that did not exist. That is the exact Soleur-only remediation ADR-235 says this change
removes. The earlier draft also grepped `^tags:` only, so a `--category`-only query (where
`$TAG` is empty) degenerated to "any line starting with `tags:`" and ALWAYS hit.

```bash
tag_lc=$(printf '%s' "${TAG:-}" | tr '[:upper:]' '[:lower:]')
cat_lc=$(printf '%s' "${CATEGORY:-}" | tr '[:upper:]' '[:lower:]')
if [ ! -f knowledge-base/kb-tags.txt ] || [ ! -f knowledge-base/kb-categories.txt ]; then
  # No facet cache and no way to build one: validate each supplied value against the corpus
  # itself. Whole-word, case-insensitive, over its OWN frontmatter line only -- one branch per
  # flag, each guarded on the flag being set, so an empty value can never degenerate the regex.
  if [ -n "$TAG" ] \
     && ! git grep -ilE "^tags:.*\\b${TAG}\\b" -- 'knowledge-base/**/*.md' >/dev/null 2>&1; then
    echo "No matches for --tag ${TAG}."
    exit 0
  fi
  if [ -n "$CATEGORY" ] \
     && ! git grep -ilE "^category:[[:space:]]*${CATEGORY}[[:space:]]*$" -- 'knowledge-base/**/*.md' >/dev/null 2>&1; then
    echo "No matches for --category ${CATEGORY}."
    exit 0
  fi
  # Every supplied value is real. Continue to Phase 2 -- the artifact checks in the else-arm
  # are NOT reached.
else
  # Facet caches present: validate each supplied value against its artifact (case-insensitive,
  # fixed-string, whole-line). On miss, emit and exit.
  if [ -n "$TAG" ] && ! grep -Fxq "$tag_lc" knowledge-base/kb-tags.txt; then
    echo "No matches. Valid values: knowledge-base/kb-tags.txt"
    exit 0
  fi
  if [ -n "$CATEGORY" ] && ! grep -Fxq "$cat_lc" knowledge-base/kb-categories.txt; then
    echo "No matches. Valid values: knowledge-base/kb-categories.txt"
    exit 0
  fi
fi
```

### Phase 2: Filter Learnings by Frontmatter (only if `--tag` or `--category` supplied)

Walk `knowledge-base/project/learnings/*.md`. For each file, parse YAML frontmatter with the same awk idiom the index generator uses:

```awk
/^---$/ { c++; next }
c != 1 { next }
# then match on ^tags: or ^category:
```

Accept both inline (`tags: [a, b]`) and block (`tags:\n  - a`) forms. Compare values case-insensitively. Collect the surviving file paths.

If both flags are supplied, a file must match BOTH to survive (AND).

### Phase 2.5: Paraphrase Pre-Pass (Stage 2, #4176)

<!-- stage-2-paraphrase-union-v1 -->

Stage 2 (#4176) adaptive pre-pass — runs after Phase 3 has produced a baseline two-tier grep result for `$KEYWORD`, and only when that result is sparse. The agent paraphrases inline within its own context (Option C runtime); no runtime API call is generated by this skill.

**Trigger condition (ALL must hold):**

1. `$KEYWORD` is non-empty (facet-only queries skip).
2. Phase 3's two-tier grep returned **< 5 unique paths** (combined tier-1 + tier-2 dedupe-by-path count).
3. `--no-paraphrase` was NOT passed.
4. `$KEYWORD` does NOT match the sensitive-shape regex below (case-insensitive). The regex blends value-shape anchoring (assignment-blobs, postgres dsn=) and vendor-prefix anchoring (Anthropic, OpenAI, GitHub, AWS, Stripe, Slack, JWT triple-blob) — prefix anchors avoid blocking topic-keyword queries like `"JWT token refresh"` or `"keypress event"` while still catching credential paste-throughs. Source of truth is `SENSITIVE_QUERY_REGEX` in [scripts/learning-retrieval-bench.sh](../../../../scripts/learning-retrieval-bench.sh); [plugins/soleur/test/kb-search-lockstep.test.sh](../../../test/kb-search-lockstep.test.sh) asserts byte-equality across both files.

   ```text
   ((=|:)[[:space:]]*[a-zA-Z0-9+/]{16,}|sk-(ant-)?[a-zA-Z0-9_-]{20,}|sk_live_[a-zA-Z0-9]{20,}|pk_live_[a-zA-Z0-9]{20,}|rk_live_[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36}|gho_[a-zA-Z0-9]{36}|ghs_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9_]{40,}|AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}|xox[abprs]-[a-zA-Z0-9-]{10,}|eyJ[A-Za-z0-9_-]{15,}\.[A-Za-z0-9_-]{15,}\.[A-Za-z0-9_.+/=-]{15,}|dsn=)
   ```

If any condition fails, skip Phase 2.5 and return Phase 3's result unchanged.

**Sensitive-query guard (manual override = `--no-paraphrase`):** when condition 4 fails, emit and exit:

```text
kb-search: refusing to paraphrase query containing sensitive value-shape token. Pass --no-paraphrase to explicitly bypass.
```

**Cache lookup:**

```bash
variants=$(bash "${CLAUDE_PLUGIN_ROOT}/skills/kb-search/scripts/kb-search-cache.sh" lookup "$KEYWORD")
```

On hit (newline-separated variants, < 14 days old) skip variant generation and go straight to union execution. On miss (empty output) proceed.

**Variant generation (Option C agent-inline):**

> Generate exactly 3 paraphrase variants of `$KEYWORD`. Each variant should use different vocabulary while preserving semantic intent (swap verbs for nouns where natural, substitute domain-canonical synonyms). Output one variant per line, no preamble.

**Cache write:**

```bash
bash "${CLAUDE_PLUGIN_ROOT}/skills/kb-search/scripts/kb-search-cache.sh" append "$KEYWORD" "$v1" "$v2" "$v3"
```

**Union execution:** for each of the 4 strings (original `$KEYWORD` + 3 variants), run Phase 3's two-tier grep under its own per-tier 8+12 caps. The 4 per-variant ranked lists are then merged into a single flat hit-count rerank capped at 20 — per-tier identity does not survive the union (by design — union-by-hit-count is the new ranking signal), and 20 is the absolute ceiling on what kb-search returns.

**Fallback policy:** paraphrase failure (rate limit, network, model refusal, all variants empty) → emit to stderr and fall back to Phase 3's baseline result:

```text
kb-search: WARN — paraphrase generation failed: <cause> — falling back to baseline grep
```

Per-session cap breach (executing agent has generated more paraphrase calls than is sane in one session) → emit `kb-search: WARN — paraphrase cap reached — falling back to baseline grep` and use the baseline result.

### Phase 3: Keyword Search

- **Facet-only (no keyword):** Emit one line per surviving file in `- [Title](path)` form (title read from frontmatter or first `# heading`).
- **Keyword-only (no facets):** Run the two-tier search with per-tier caps so tier-1 noise titles cannot starve tier-2 content matches (see #4119):
  1. **Tier 1 (cap 8):** Run `[ -f scripts/ensure-kb-index.sh ] && bash scripts/ensure-kb-index.sh --soft || true` first (untracked cache — see Phase 1; the call is idempotent, so a second one after Phase 1 costs only the ~60 ms probe), then grep `knowledge-base/INDEX.md` for the keyword, then restrict to lines whose link target is rooted under `knowledge-base/project/learnings/`. Anchor the filter so future paths like `sessions/learnings-retrospective/` cannot leak.
  2. **Tier 2 (cap 12):** Grep `knowledge-base/project/learnings/**/*.md` content for the keyword. Exclude `archive/`.
- Output tier-1 first, then tier-2, deduped by path. Maximum 20 total; each tier self-caps.
- **Facets + keyword:** Apply keyword grep **only** to the facet-filtered file list (not the whole KB). Use `grep -F` (fixed-string).

Missing `INDEX.md` after the ensure call → the repo has no generator (a self-hosted install). Note it and continue with the Tier-2 content grep only; never exit non-zero on this, and never print a remediation naming a script the user does not have.

### Phase 4: Display Results

Title matches come first, then content matches. Cap total at 20; if truncated, append:

```text
Showing top 20 of N matches. Narrow the query for more specific results.
```

Zero results → suggest:

- Check spelling
- Try broader or alternative keywords
- For `--tag`/`--category` misses, inspect `knowledge-base/kb-tags.txt` or `kb-categories.txt`
- Run `bash scripts/ensure-kb-index.sh` to refresh the untracked caches (Soleur repo only; it is a no-op when they are already fresh)

## Examples

### Tag filter with keyword

```text
soleur:kb-search --tag eager-loading rails
```

Returns learnings tagged `eager-loading` whose content matches `rails`.

### Category filter alone

```text
soleur:kb-search --category performance-issues
```

Returns all learnings with `category: performance-issues` as title+path.

### Combined facets

```text
soleur:kb-search --tag n+1 --category performance-issues
```

Returns learnings tagged `n+1` AND categorized as `performance-issues`.

### Miss with hint

```text
soleur:kb-search --tag nonexistent-tag
# Output:
# No matches. Valid values: knowledge-base/kb-tags.txt
```

The agent can read the artifact in a follow-up round-trip to self-correct.

## Output Format

```text
## Title Matches

1. [Title](knowledge-base/path) — title match
2. [Title](knowledge-base/path) — title match

## Content Matches

3. [Title](knowledge-base/path) — content match (line N: "...snippet...")
```

Tag-only or category-only output uses the `## Title Matches` block only, without snippets.

Files in this skill

  • SKILL.md10.8 KB
  • scripts/kb-search-cache.sh5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.