Support external vault roots across tooling
This commit is contained in:
@@ -9,8 +9,23 @@ Capture an external resource into the vault as a reference note, store its
|
||||
original bytes under `raw/`, decompose it into topics, and link those topics into
|
||||
the knowledge graph. Follow every step.
|
||||
|
||||
Obey `AGENTS.md` and `02_areas/ways-of-working.md` (read them if not already
|
||||
loaded this session; if `ways-of-working.md` does not exist yet, skip it).
|
||||
Before ingesting, call `read_instructions` on `knowledge-database` and read both
|
||||
returned documents: the framework's `AGENTS.md` and `AGENT_QUERY_GUIDE.md`.
|
||||
The server resolves their location; they are not data-vault nodes and must not
|
||||
be fetched with `read_note`. If this call fails, report the missing instructions
|
||||
before changing the vault.
|
||||
|
||||
Keep the caller's task instructions in scope. For additional vault policies,
|
||||
query `graph_search_nodes(type="policy")` and `graph_search_nodes(type="guideline")`,
|
||||
then `read_note` relevant results and follow their policy links. Do not assume
|
||||
`02_areas/ways-of-working.md` exists; an empty search means no policies were found,
|
||||
not that framework or caller instructions cease to apply.
|
||||
|
||||
The `knowledge-database` MCP server is the only interface for vault access: use `graph_summary`, `graph_search_nodes`, `graph_neighbors` and
|
||||
`read_note` to find context; `capture_raw` for source bytes; `write_note` and
|
||||
`edit_note` for notes; `refresh_graph(validate=true)` to regenerate and validate.
|
||||
Use `git_commit` / `git_push` only when the caller explicitly authorizes them.
|
||||
Skills stay in the framework. Never infer the vault from this skill's location.
|
||||
|
||||
The output is a `type: note` reference note at
|
||||
`03_resources/<resource-name>/index.md`, one `type: note` **topic note** per
|
||||
@@ -47,10 +62,9 @@ Pick a short kebab-case `<resource-name>` from the title / filename / video
|
||||
title. Keep it descriptive and unique (e.g. `cyclonedx-sbom-spec`,
|
||||
`oauth2-rfc6749`).
|
||||
|
||||
```bash
|
||||
ls 03_resources/ # check the slug is not already taken
|
||||
mkdir -p 03_resources/<resource-name>/raw
|
||||
```
|
||||
Check for an existing resource with `graph_search_nodes(query="<resource-name>")`
|
||||
and `read_note(path="03_resources/<resource-name>/index.md")`. The capture and
|
||||
write tools create parent directories; no separate folder-creation step is needed.
|
||||
|
||||
If the slug already exists, either you are re-ingesting (update in place, tell the
|
||||
user) or pick a more specific slug.
|
||||
@@ -59,47 +73,31 @@ user) or pick a more specific slug.
|
||||
|
||||
Always preserve the source bytes untouched. Pick the branch that matches step 1.
|
||||
|
||||
**Local file** — copy verbatim, keep the original name:
|
||||
```bash
|
||||
cp "<source-path>" "03_resources/<resource-name>/raw/"
|
||||
```
|
||||
**Local file** — use `capture_raw` with the supplied `source_path` and a
|
||||
vault-relative `path` such as `03_resources/<resource-name>/raw/<filename>`.
|
||||
This preserves the original bytes and filename.
|
||||
|
||||
**PDF / binary at a URL** — download verbatim:
|
||||
```bash
|
||||
curl -fsSL "<url>" -o "03_resources/<resource-name>/raw/<filename>"
|
||||
```
|
||||
**PDF / binary at a URL** — use `capture_raw(url="<url>", path=".../raw/<filename>")`.
|
||||
Check the returned byte count and SHA-256.
|
||||
|
||||
**Webpage / link** — record the URL and save the readable content. Use `WebFetch`
|
||||
to retrieve it (request markdown), then write the result and a `source.url`:
|
||||
```bash
|
||||
printf '%s\n' "<url>" > "03_resources/<resource-name>/raw/source.url"
|
||||
```
|
||||
Save the fetched body to `raw/page.md` (write the `WebFetch` output to that file).
|
||||
**Webpage / link** — save the URL using
|
||||
`write_note(path=".../raw/source.url", content="<url>\n")`. Capture the original
|
||||
page with `capture_raw(url="<url>", path=".../raw/page.html")`. If the invoking
|
||||
agent allows `WebFetch`, it may fetch a readable version of that external source;
|
||||
save the returned body with `write_note(path=".../raw/page.md", content=...)`.
|
||||
|
||||
**YouTube video** — pull the transcript into `raw/`. Record the URL first, then
|
||||
try, in order:
|
||||
```bash
|
||||
printf '%s\n' "<url>" > "03_resources/<resource-name>/raw/source.url"
|
||||
|
||||
# Preferred: yt-dlp grabs subs + metadata without downloading the video
|
||||
yt-dlp --skip-download --write-subs --write-auto-subs --sub-lang en \
|
||||
--convert-subs srt --write-info-json \
|
||||
-o "03_resources/<resource-name>/raw/%(title)s.%(ext)s" "<url>"
|
||||
|
||||
# Fallback if yt-dlp is absent: python transcript API
|
||||
python3 -c "import youtube_transcript_api" 2>/dev/null && \
|
||||
python3 -c "from youtube_transcript_api import YouTubeTranscriptApi as A; \
|
||||
import sys; vid='<VIDEO_ID>'; \
|
||||
open('03_resources/<resource-name>/raw/transcript.txt','w').write(\
|
||||
'\n'.join(s['text'] for s in A.get_transcript(vid)))"
|
||||
```
|
||||
If neither tool is installed, tell the user which to install (`pip install yt-dlp`
|
||||
or `pip install youtube-transcript-api`) and stop before fabricating a transcript.
|
||||
Never invent transcript text.
|
||||
**YouTube video** — record the URL with `write_note`. If a transcript is supplied,
|
||||
capture its file with `capture_raw(source_path=..., path=".../raw/transcript.txt")`
|
||||
or preserve supplied text with `write_note`. These MCP tools do not extract
|
||||
YouTube transcripts. If none is supplied, report the missing transcript and stop
|
||||
before writing content notes. Never invent transcript text or run shell extractors.
|
||||
|
||||
## 4. Read the captured content
|
||||
|
||||
Read what you just stored in `raw/` (the file, `page.md`, the transcript). This —
|
||||
Use `read_note(path="03_resources/<resource-name>/raw/<filename>")` for captured
|
||||
text. Path reads access the file directly and work before graph regeneration.
|
||||
For a captured binary, use the invoking agent’s explicitly permitted inspection
|
||||
tool on the absolute path returned by `capture_raw`; if unavailable, report it. This —
|
||||
not the live URL — is the source of truth for the rest of the steps, so the note
|
||||
matches the bytes we preserved. For long content, read enough to identify every
|
||||
distinct topic, not just the intro.
|
||||
@@ -110,24 +108,10 @@ Reason about the **individual topics** inside the resource — the concrete
|
||||
concepts, standards, formats, tools, or entities it discusses. For each topic,
|
||||
decide whether the vault already knows about it, and whether it points outward.
|
||||
|
||||
For every candidate topic, search the vault for a matching note:
|
||||
|
||||
```bash
|
||||
# free-text over the graph (id / title / path / tags)
|
||||
jq -r --arg q "<topic>" '.nodes[]
|
||||
| select((.id|ascii_downcase|contains($q)) or (.title|ascii_downcase|contains($q)))
|
||||
| "\(.id)\t\(.path)"' index/graph.json
|
||||
|
||||
# by tag
|
||||
jq -r --arg t "<topic-tag>" '.nodes[] | select(.tags[]? == $t) | "\(.id)\t\(.path)"' index/graph.json
|
||||
|
||||
# full-text fallback across the PARA buckets
|
||||
grep -rIl "<topic>" 01_projects 02_areas 03_resources 04_archives
|
||||
```
|
||||
|
||||
Prefer the `knowledge-database` MCP server when available (`graph_search_nodes`,
|
||||
`graph_neighbors`, `read_note`) — it is the same index. Open promising matches and
|
||||
confirm they are really the same concept before linking.
|
||||
For every candidate topic, use `graph_search_nodes(query="<topic>")` and tag
|
||||
filters from `graph_summary`, then traverse `graph_neighbors` and `read_note`
|
||||
promising matches. Confirm they cover the same concept before linking.
|
||||
The query searches metadata, not note bodies; read candidate notes when needed.
|
||||
|
||||
Classify each topic:
|
||||
- **Internal** — the vault has a note for it → link with
|
||||
@@ -145,7 +129,7 @@ Classify each topic:
|
||||
|
||||
## 6. Write a note per topic
|
||||
|
||||
Each distinct topic from step 5 becomes its own note at
|
||||
Use `write_note` to create each distinct topic from step 5 at
|
||||
`03_resources/<resource-name>/<topic-name>.md` (kebab-case slug). This is what
|
||||
makes topics first-class, individually linkable graph nodes.
|
||||
|
||||
@@ -188,11 +172,11 @@ the *source* needs from the *destination* — "what to expect there".
|
||||
- Link each topic note back to its resource with
|
||||
`[<resource title>](resource/<resource-name>){"type":"part-of"}`, and outward to
|
||||
internal notes (same link form) and external sources (URLs).
|
||||
- Legacy `[[wiki links]]` are rejected by `make validate-graph` — never write them.
|
||||
- Legacy `[[wiki links]]` are rejected by `refresh_graph(validate=true)` — never write them.
|
||||
|
||||
## 7. Write the resource note (index)
|
||||
|
||||
Filename: `03_resources/<resource-name>/index.md`. This is the hub that ties the
|
||||
Use `write_note` for `03_resources/<resource-name>/index.md`. This hub ties the
|
||||
raw source to the topic notes.
|
||||
|
||||
```md
|
||||
@@ -230,7 +214,7 @@ last_reviewed: <today, YYYY-MM-DD>
|
||||
- `ingested` and `last_reviewed` = today (the `currentDate` from session context).
|
||||
- `type` must stay within the vault taxonomy enum
|
||||
(`hub|project|area|guideline|policy|feature|roadmap|decision|note|meta|reference`),
|
||||
which `generate-graph.py --validate` enforces — use `note`;
|
||||
which `refresh_graph(validate=true)` enforces — use `note`;
|
||||
the `resource` tag is what marks it.
|
||||
- Markdown link targets resolve by `id`, rel-path, title, or unique stem — always
|
||||
use the `id` as the target, e.g. `[Human title](resource/foo/bar)`.
|
||||
@@ -244,16 +228,14 @@ So the resource is reachable in the graph, not just reaching out:
|
||||
the most relevant existing notes (the strong internal topics from step 5) — e.g.
|
||||
under a `## Resources` or `## Related` section, creating it if absent. Add a
|
||||
`{"type":"…","link-info":"…"}` brace where the relationship is more specific than
|
||||
`related`. Use individual edits.
|
||||
`related`. Use `edit_note` for each existing note.
|
||||
- If several resources accumulate, maintain `03_resources/index.md` as a hub that
|
||||
lists them, and link the new note from there (create the hub if it does not
|
||||
exist, with `type: hub`, `tags: [resource, hub]`).
|
||||
|
||||
## 9. Regenerate and validate the graph
|
||||
|
||||
```bash
|
||||
make graph
|
||||
```
|
||||
Use `refresh_graph(validate=true)`.
|
||||
|
||||
This regenerates `index/graph.json` and validates links and edge relationship
|
||||
types. If validation reports unresolved links, fix the targets (typos, wrong `id`,
|
||||
@@ -265,17 +247,14 @@ invalid graph, so this must pass.
|
||||
|
||||
## 10. Stage (do not commit unless asked)
|
||||
|
||||
Per `ways-of-working.md`, NEVER use `git add -A` or `git add .`. Stage each
|
||||
touched path individually:
|
||||
Without explicit publishing authorization, leave the changes uncommitted and
|
||||
report the touched paths. The MCP interface has no stage-only tool.
|
||||
|
||||
```bash
|
||||
git add 03_resources/<resource-name>/ # note + raw/
|
||||
git add index/graph.json
|
||||
git add <any-existing-note-you-backlinked>.md
|
||||
```
|
||||
|
||||
Only commit or open a PR if the user explicitly asks. Changes go via small, clear
|
||||
pull requests.
|
||||
When a commit is authorized, use `git_commit(message=..., paths=[...])` with an
|
||||
explicit list covering the resource folder, backlinked notes and `index/graph.json`.
|
||||
This tool stages those paths and commits them; do not use wildcard paths.
|
||||
Use `git_push` only when pushing is explicitly authorized. Follow the caller's
|
||||
branch and review policy.
|
||||
|
||||
## 11. Report
|
||||
|
||||
|
||||
@@ -11,8 +11,18 @@ Markdown is the source of truth; `index/graph.json` is a generated navigation
|
||||
index. Use this skill to answer questions FROM the vault and to surface the
|
||||
exact notes that back the answer.
|
||||
|
||||
Obey `AGENTS.md` (read them if not loaded this
|
||||
session). This skill is read-only — do not modify notes here.
|
||||
Call `read_instructions` on `knowledge-database` to read the framework's
|
||||
`AGENTS.md` and `AGENT_QUERY_GUIDE.md` before querying the vault. These documents
|
||||
are not data-vault nodes. If unavailable, report the missing instructions.
|
||||
This skill is read-only — do not modify notes here.
|
||||
|
||||
## Vault access
|
||||
|
||||
The `knowledge-database` MCP server is the only interface for vault access. Use `graph_summary` to orient, `graph_search_nodes` to find notes,
|
||||
`graph_neighbors` to traverse links, and `read_note` to read the source.
|
||||
Use `refresh_graph` when the generated index is stale. Skills and operational
|
||||
documentation belong to the framework; never infer the data vault from this
|
||||
skill's file location.
|
||||
|
||||
## Layout (PARA)
|
||||
|
||||
@@ -25,84 +35,58 @@ session). This skill is read-only — do not modify notes here.
|
||||
03_resources/repos/repo-landscape.md index of documented repos
|
||||
04_archives/ inactive items
|
||||
index/graph.json generated graph (nodes + edges)
|
||||
README.md vault hub; AGENT_QUERY_GUIDE.md graph query reference
|
||||
|
||||
```
|
||||
|
||||
Every note carries taxonomy frontmatter: `id`, `title`, `type`
|
||||
(`hub|project|area|guideline|policy|feature|roadmap|decision|note|meta|reference`, enforced by
|
||||
`generate-graph.py --validate`), `status`
|
||||
`refresh_graph(validate=true)`), `status`
|
||||
(`active|draft|deprecated`), `tags`, `last_reviewed`, optional `repo`. Notes are
|
||||
linked with typed markdown links — `[text](note-id){"type":"…"}` — that resolve
|
||||
by `id`. Legacy `[[wiki links]]` are rejected by `make validate-graph`.
|
||||
by `id`. Legacy `[[wiki links]]` are rejected by `refresh_graph(validate=true)`.
|
||||
|
||||
## 1. Orient
|
||||
|
||||
Start broad, then narrow:
|
||||
|
||||
- `README.md` — vault hub and entry points.
|
||||
- `03_resources/repos/repo-landscape.md` — index of every documented repo, grouped.
|
||||
- `02_areas/ways-of-working.md` — global engineering policy.
|
||||
- `03_resources/meta/taxonomy.md` — what the frontmatter fields and PARA buckets mean.
|
||||
|
||||
For a repo question, read `03_resources/repos/<repo>/info.md` directly
|
||||
(`ls 03_resources/repos/` to confirm the repo is documented; if absent, say so —
|
||||
it is not yet in the vault).
|
||||
Start broad, then narrow. Begin with `graph_summary`, then find hubs using
|
||||
`graph_search_nodes(type="hub")` and read relevant ones with `read_note`.
|
||||
Discover repo scopes and tags from the graph rather than assuming a particular
|
||||
folder layout. An external data vault need not contain a root README or
|
||||
framework operational documents.
|
||||
|
||||
## 2. Query the graph
|
||||
|
||||
`index/graph.json` is for navigation by tag / type / repo / links. Use `jq`.
|
||||
Full schema and more examples are in `AGENT_QUERY_GUIDE.md`.
|
||||
Use the MCP tools directly:
|
||||
|
||||
```bash
|
||||
# all documented repos
|
||||
jq -r '.nodes[].repo' index/graph.json | sort -u
|
||||
| Need | Tool |
|
||||
|------|------|
|
||||
| Repo scopes, tag spellings, most-linked notes | `graph_summary` |
|
||||
| Notes in a repo | `graph_search_nodes(repo="<repo>")` |
|
||||
| Notes of a type | `graph_search_nodes(type="policy")` |
|
||||
| Notes with a tag | `graph_search_nodes(tag="<tag>")` |
|
||||
| Search ids, titles, paths and tags | `graph_search_nodes(query="<term>")` |
|
||||
| Incoming/outgoing links, relationship types and annotations | `graph_neighbors(id="<note-id>")` |
|
||||
|
||||
# every note in one repo
|
||||
jq -r '.nodes[] | select(.repo=="tenant-controller") | "\(.id)\t\(.path)"' index/graph.json
|
||||
|
||||
# all notes of a type (e.g. policies, decisions, projects)
|
||||
jq -r '.nodes[] | select(.type=="policy") | "\(.id)\t\(.path)"' index/graph.json
|
||||
|
||||
# notes carrying a tag
|
||||
jq -r '.nodes[] | select(.tags[]? == "kcp") | "\(.id)\t\(.path)"' index/graph.json
|
||||
|
||||
# most-linked (central) notes
|
||||
jq -r '.nodes | sort_by(-(.inbound_count // 0))[] | "\(.inbound_count // 0)\t\(.id)"' index/graph.json | head
|
||||
|
||||
# outgoing links from a note, with relationship type + annotation
|
||||
NOTE_ID="repo/controller-util/info"
|
||||
jq -r --arg id "$NOTE_ID" '.edges[] | select(.from==$id) | "\(.type)\t\(.to)\t\(.annotation)"' index/graph.json
|
||||
|
||||
# incoming links to a note (who references it, and how)
|
||||
jq -r --arg id "$NOTE_ID" '.edges[] | select(.to==$id) | "\(.type)\t\(.from)\t\(.annotation)"' index/graph.json
|
||||
|
||||
# edges of a specific relationship type (e.g. supersedes, depends-on)
|
||||
jq -r '.edges[] | select(.type=="supersedes") | "\(.from) -> \(.to)"' index/graph.json
|
||||
```
|
||||
|
||||
Full-text fallback when you do not know the tag/type/id:
|
||||
|
||||
```bash
|
||||
grep -rIl "<term>" 01_projects 02_areas 03_resources 04_archives
|
||||
```
|
||||
`query` does not search note bodies. Read candidate notes with `read_note` and
|
||||
follow their neighbors when metadata search is insufficient. Report any search
|
||||
coverage limits; do not use filesystem or shell searches.
|
||||
|
||||
## 3. Read the source notes
|
||||
|
||||
The graph gives ids and paths; the ANSWER lives in the Markdown. Open the
|
||||
matched `.path` files and read them. Follow the typed markdown links
|
||||
The graph gives ids and paths; the ANSWER lives in the Markdown. Read matched
|
||||
notes with `read_note(id="<note-id>")` or `read_note(path="<vault-relative-path>")`. Follow the typed markdown links
|
||||
`[text](id){"type":"…"}`, resolving each to its node `path` to pull in related
|
||||
policies, decisions, and repo info. Use each edge's `type` and `annotation` to
|
||||
judge which links are worth following; links are the intended navigation, so
|
||||
traverse them rather than guessing.
|
||||
|
||||
If the graph looks stale or a path is missing, you may regenerate read-only:
|
||||
`python3 scripts/generate-graph.py` (does not touch Markdown). Do not commit.
|
||||
If the graph looks stale or a path is missing, use `refresh_graph`. This updates
|
||||
only the generated index; it does not touch Markdown. Do not commit.
|
||||
|
||||
## 4. Answer with citations
|
||||
|
||||
- Answer the question directly from what the notes say.
|
||||
- Cite the backing notes by vault-relative `path` (e.g.
|
||||
`03_resources/repos/gittea/info.md`) and/or `id` so the user can verify.
|
||||
`03_resources/<resource>/index.md`) and/or `id` so the user can verify.
|
||||
- Distinguish vault facts from inference. If the vault does not cover it, say so
|
||||
plainly — do not invent. Note `status: draft`/`deprecated` when relevant.
|
||||
- For "what links to / relates to X" questions, report the inbound/outbound
|
||||
|
||||
Reference in New Issue
Block a user