chore(agent): capture noctalia/gate/niri memory lessons + secret-audit and system-prompt-retrieval managed skills

learned.md: noctalia v5 bar layering + hot-reload loop; secret-gate self-match convention (quoted literals redacted per the convention the line itself states); niri 26.04 event-stream has no hotplug event. New managed skills: git-repo-secret-audit, omp-system-prompt-retrieval.
This commit is contained in:
Frank
2026-10-09 16:37:43 -07:00
parent 989196f664
commit 4d2b024116
3 changed files with 138 additions and 0 deletions
@@ -0,0 +1,68 @@
---
name: git-repo-secret-audit
description: "Audit a git repo for leaked secrets across history, unreachable objects, LFS payloads, git config and host configs — with triage of every hit."
---
# Git repo secret audit (history + unreachable objects, not just the worktree)
Worktree greps prove nothing: the leak you care about is in a commit that was amended
away, in a branch nobody checks out, in an LFS payload, or in `.git/config`.
## 0. Build the known-value set from the live secrets, never from literals
Read the values from the secret store at runtime so no key enters the transcript:
```python
known = {}
for p in (home/".config/secrets").iterdir():
t = p.read_text().strip()
vals = {t}
m = re.search(r":([^:@/]+)@", t) # credential-store URL form
if m: vals.add(m.group(1))
known[p.name] = {v for v in vals if len(v) >= 12}
```
Report ONLY `value[:4] + len + sha256[:10]`. Never print a value, and never quote a
secret-shaped literal in a doc/commit message — the pre-commit gate will (correctly) block it.
## 1. Shapes to match (superset of the Dank gate)
`sk-[A-Za-z0-9]{18,}`, `sk-or-v1-`, `sk-ant-`, `hf_[A-Za-z0-9]{30,}`, `gh[pousr]_[A-Za-z0-9]{30,}`,
`github_pat_`, `glpat-`, `xox[baprs]-`, `(AKIA|ASIA)[0-9A-Z]{16}`, `npm_`, `pypi-`, `AIza[0-9A-Za-z_-]{30,}`,
`(sk|pk)_(live|test)_`, Telegram `\d{8,10}:AA`, JWT `eyJ…\.eyJ…`, `-----BEGIN [A-Z ]*PRIVATE KEY-----`,
`AGE-SECRET-KEY-1`, `ssh-(ed25519|rsa)\s+AAAA`, `scheme://user:pass@host` basic-auth URLs,
`Bearer <24+>`, and the noisy `(password|secret|api_key|token)\s*[:=]\s*<12+>` assignment shape.
## 2. Scope, in this order
```bash
git rev-list --all | wc -l # revs incl. every branch + tag
git ls-tree -r --format='%(objectname) %(path)' <rev> # sha -> path, per rev
git cat-file --batch # stream reachable blob content
git cat-file --batch-all-objects --batch-check # EVERY object, incl. dangling
git rev-list --objects --all # the reachable set; difference = dangling
git log --all --oneline --find-object=<sha> # empty => never committed on any ref
git status --porcelain -uall --ignored=matching # !! lines: on-disk ignored files
git config --list --show-origin # local/global/system credentials
git ls-files | grep -E '(auth\.json|trust\.json|\.env|\.pem|\.key|identity)' # quarantine
```
LFS: scan `.git/lfs/objects/**` as BYTES (`decode("latin-1")`) — a screenshot is a classic
secret carrier and the tracked tree holds only pointers.
## 3. Entropy layer (catches issuer-specific keys you did not list)
Tokens `[A-Za-z0-9+/_-]{32,}` with Shannon entropy >= 4.3 containing both digits and letters.
Flag every one whose value is in `known` as a LIVE leak; list the rest for triage.
## 4. Triage — the shapes that always fire and are fine
- `api_key="sk-no-key-required"` and other doc placeholders
- prose: `Set password: user.set_password(...)`, `authenticate(user, password)`
- 40-hex hits that are git object ids / packfile names (`pack-<40hex>.pack`)
- memory/KB records that *describe* a secret operation
A hit is a leak only when the value matches a live secret, decodes to a real credential, or
sits in a field a program reads.
## 5. Adjacent host scan (the audit's real yield)
Check file modes of the secret dir (`0600`, dir `0700`) — world-readable secret files are the
most common real finding. Then grep the live values across shell rc files, `~/.gitconfig`,
`~/.npmrc`, `~/.netrc`, `~/.curlrc`, `~/.docker/config.json`, `~/.cache/huggingface/token`,
`~/.aws/credentials`, `~/.ssh/config` and report filenames only.
## 6. Report
Per layer: scope numbers + verdict. Every hit: path:line, redacted value, verdict
(leak / benign + why). Name the gate's coverage gap if the project's own hook misses shapes
you matched. Fix modes/chmod on the spot; changes to a security hook are the operator's call.
@@ -0,0 +1,67 @@
---
name: omp-system-prompt-retrieval
description: "Retrieve the exact system prompt an omp session is running: /dump while live, RPC get_state re-render, and the shipped template/override source paths."
---
# Retrieve omp's live system prompt
Three routes, in fidelity order. Verified on omp 18.4.10 (linux/x64).
## 1. `/dump` — the only exact capture of a LIVE session
TUI slash command: "Copy session transcript to clipboard (and write LLM request JSON to tmp)".
The request JSON is the wire-true payload (system blocks + tool schemas). Path is printed in
the `/dump` output; then:
```bash
jq -r '.system[] | strings // .system' <file> # anthropic shape
jq -r '.messages[] | select(.role=="system") | .content' <file> # openai shape
```
**The prompt is never persisted in the session JSONL.** Session entries are only
`title, session, model_change, thinking_level_change, message, custom(tool_execution_start),
model_usage`. So a past session's prompt is unrecoverable from disk — capture live, or re-render.
## 2. RPC `get_state` — re-render without a model call
`omp --mode rpc` answers `{"type":"get_state"}` with `data.systemPrompt: [blocks]` and
`data.dumpTools` (spec: `omp://rpc.md` "get_state payload"). Drive it over stdio:
```python
cmds = json.dumps({"id":"p1","type":"negotiate_protocol","protocolVersion":2}) + "\n" \
+ json.dumps({"id":"s1","type":"get_state"}) + "\n"
p = subprocess.run(["omp","--mode","rpc","--no-session","--no-pty"],
cwd=TARGET_CWD, input=cmds, capture_output=True, text=True, timeout=280)
```
Closing stdin shuts it down cleanly (exit 0), no model call, no session file written.
- Parse stdout line-by-line. Protocol v2 wraps logical frames >1 MiB in `rpc_chunk` frames:
key on `chunkId`, require all `count` indices, `base64.b64decode` and concatenate in `index`
order, then `json.loads` — otherwise a large prompt is silently lost.
- Shape measured on a dotfiles repo cwd: 2 blocks, ~45 KB / ~391 lines —
block 0 = static instruction block (RFC 2119 line, `§ Role`, Engineering/Tone/Escalation,
tool inventory, `xd://` catalog), block 1 = footer opening `<project-context>` +
`<critical>` + the cwd's `AGENTS.md`.
- `--no-extensions` changes nothing in the blocks; tool count rides in `dumpTools`, not the prompt.
- `cwd` matters: the footer is cwd-derived (workstation block, context files, skills, rules).
## 3. Sources (edit here, not in a dump)
| Layer | Path |
|---|---|
| Default instruction template (Handlebars, block 0) | `<pkg>/src/prompts/system/system-prompt.md` |
| Footer | `<pkg>/src/prompts/system/project-prompt.md` |
| Plain-custom template | `<pkg>/src/prompts/system/custom-system-prompt.md` |
| Repo rules (into footer) | the cwd's `AGENTS.md` |
| Overrides | `--system-prompt`, `--system-prompt-template <path>`, `--append-system-prompt`; discovered `SYSTEM.md`, `SYSTEM_TEMPLATE.md`, `APPEND_SYSTEM.md`, `PERSONALITY.md`, `TITLE_SYSTEM.md` |
`<pkg>` = `~/.bun/install/global/node_modules/@oh-my-pi/pi-coding-agent` (resolve via
`readlink -f $(command -v omp)`). Docs: `omp://system-prompt-customization.md`.
Precedence: explicit CLI flag > project `SYSTEM.md` > project `SYSTEM_TEMPLATE.md` >
user `SYSTEM.md` > user `SYSTEM_TEMPLATE.md`. A same-scope literal always beats a discovered
template, so a `SYSTEM_TEMPLATE.md` is inert while a `SYSTEM.md` sits beside it.
## Trap
NEVER paste a rendered `/dump` into `SYSTEM_TEMPLATE.md`: a render freezes settings, tool
catalogs, and mounted-device data. Copy the Handlebars **source** and diff against the shipped
path after every omp upgrade. Plain files (`SYSTEM.md`, `APPEND_SYSTEM.md`) are inserted
verbatim — `{{cwd}}` and friends stay literal there; only the template route compiles Handlebars.