Obsidian Canvas Boards
AgriciDaniel/claude-obsidian
Creates, inspects and updates Obsidian JSON Canvas boards in a vault, with text, file, link, group and edge nodes, using safe recoverable edits.
Query the Dev Knowledge Graph via SPARQL instead of grepping raw session files.
$ npx skills add robertoshimizu/session-graph --skill devkg-sparql -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install robertoshimizu/session-graph devkg-sparql --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/robertoshimizu/session-graph.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/devkg-sparql .claude/skills/devkg-sparql && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "devkg-sparql" agent skill from https://github.com/robertoshimizu/session-graph/tree/main/.claude/skills/devkg-sparql into .claude/skills/devkg-sparql/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "devkg-sparql", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/robertoshimizu/session-graph/tree/main/.claude/skills/devkg-sparqlType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add robertoshimizu/session-graph --skill devkg-sparql -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install robertoshimizu/session-graph devkg-sparql --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/robertoshimizu/session-graph.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/devkg-sparql .agents/skills/devkg-sparql && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "devkg-sparql" agent skill from https://github.com/robertoshimizu/session-graph/tree/main/.claude/skills/devkg-sparql into .agents/skills/devkg-sparql/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "devkg-sparql", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add robertoshimizu/session-graph --skill devkg-sparql -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install robertoshimizu/session-graph devkg-sparql --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/robertoshimizu/session-graph.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/devkg-sparql .cursor/skills/devkg-sparql && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "devkg-sparql" agent skill from https://github.com/robertoshimizu/session-graph/tree/main/.claude/skills/devkg-sparql into .cursor/skills/devkg-sparql/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "devkg-sparql", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/robertoshimizu/session-graph.git --path .claude/skills/devkg-sparql--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add robertoshimizu/session-graph --skill devkg-sparql -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install robertoshimizu/session-graph devkg-sparql --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/robertoshimizu/session-graph.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/devkg-sparql .gemini/skills/devkg-sparql && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "devkg-sparql" agent skill from https://github.com/robertoshimizu/session-graph/tree/main/.claude/skills/devkg-sparql into .gemini/skills/devkg-sparql/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "devkg-sparql", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install robertoshimizu/session-graph devkg-sparqlInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add robertoshimizu/session-graph --skill devkg-sparql -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/robertoshimizu/session-graph.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/devkg-sparql .github/skills/devkg-sparql && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "devkg-sparql" agent skill from https://github.com/robertoshimizu/session-graph/tree/main/.claude/skills/devkg-sparql into .github/skills/devkg-sparql/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "devkg-sparql", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add robertoshimizu/session-graph --skill devkg-sparql -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install robertoshimizu/session-graph devkg-sparql --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/robertoshimizu/session-graph.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/devkg-sparql .opencode/skills/devkg-sparql && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "devkg-sparql" agent skill from https://github.com/robertoshimizu/session-graph/tree/main/.claude/skills/devkg-sparql into .opencode/skills/devkg-sparql/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "devkg-sparql", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
devkg-sparqlQuery the Dev Knowledge Graph via SPARQL instead of grepping raw session files.
Devkg Sparql is an agent skill from robertoshimizu/session-graph. Query the Dev Knowledge Graph via SPARQL instead of grepping raw session files. Use this when asked about technologies, relationships between tools, session history, where a topic was discussed, or cross-platform knowledge. Prefer provenance-first SPARQL (message + session) over label-only CONTAINS or grep.
Its SKILL.md is about 7.6k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Knowledge Management, covering Knowledge graphs. The repository describes itself as: Turn your scattered AI coding sessions into a queryable knowledge graph. Multi-platform (Claude Code, ChatGPT, DeepSeek, Grok, Warp), W3C ontology, Wikidata entity linking, SPARQL. The licence is Apache-2.0.
12 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 4adefaa. It shows what the files ask for, not the result of running them.
Pre-approves these tools, so the agent can use them without asking each time:
Bash(curl:*)Bash(jq:*)From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
curljqFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
w3.orgwikidata.orgquery.wikidata.orgrdfs.orgpurl.orgFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Devkg Sparql loads about 7.6k tokens when it runs. Until then it costs about 80 tokens; SKILL.md has 1,794 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from robertoshimizu/session-graph at commit 4adefaa, republished under its Apache-2.0 licence (© robertoshimizu). 1,794 words, ~7,582 tokens.
.claude/skills/devkg-sparql/SKILL.md (or your agent's skills folder).NEVER READ LARGE SPARQL RESULTS, SESSION FILES, JSONL, LOGS, OR GENERATED ARTIFACTS ALL AT ONCE. Always add LIMIT, select only needed variables, inspect counts first, and summarize. Never dump huge result sets, ID lists, raw JSON, or transcript content into chat.
Query the developer knowledge graph at http://localhost:3030/devkg/sparql via SPARQL. This graph contains extracted knowledge triples, entities, Wikidata links, and session metadata from Claude Code, pi, Codex, Cursor, ChatGPT, DeepSeek, Grok, and Warp sessions.
Content limit: sioc:content on messages is capped at ~2000 characters at ingest. SPARQL is enough to locate sessions and reason lightly from triples + snippets. For full quotes or deep thread reconstruction, normalize hasSourceFile and read the JSONL only when needed.
| User intent | Do this first | Do NOT start with |
|---|---|---|
| "Where / which sessions discussed X?" | Template 5 (topic + intent + provenance) | Label-only Template 6, or grep |
| "What do we know about technology X?" | Template 1 (entity + provenance) | Grep |
| "How does X relate to Y?" | Template 2 | Grep |
| "Find the exact message wording" | Template 5 or 9 → then JSONL only if snippet truncated | Grepping all projects |
Default for session-discovery questions: multi-signal filter (topic and intent terms) on both triple labels and sioc:content, always joining extractedFrom / extractedInSession, ordered by DESC(?created), with LIMIT.
When Fuseki returns provenance hits, do not fall back to grep. Grep only if Fuseki is down or returns 0 rows after a provenance query.
Always use this pattern (POST, URL-encoded query, JSON output). Include Fuseki auth when required:
curl -s -X POST 'http://localhost:3030/devkg/sparql' \
-u admin:admin \
-H 'Accept: application/sparql-results+json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "query=YOUR_SPARQL_HERE" \
| jq -r '.results.bindings[] | [.var1.value, .var2.value] | @tsv'Adjust the jq expression to match your SELECT variables. Use @tsv for compact tabular output. Always LIMIT results.
For multi-line queries, use double quotes around the --data-urlencode value and escape inner quotes:
curl -s -X POST 'http://localhost:3030/devkg/sparql' \
-u admin:admin \
-H 'Accept: application/sparql-results+json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "query=PREFIX devkg: <http://devkg.local/ontology#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT DISTINCT ?label WHERE {
?e a devkg:Entity ; rdfs:label ?label .
FILTER(LANG(?label) = \"\")
} LIMIT 10" \
| jq -r '.results.bindings[] | .label.value'If Fuseki is unreachable (curl fails or times out) or a provenance query (Template 5/8) returns 0 results, then fall back to grep-based session search:
grep -rli "keyword" ~/.claude/projects ~/.pi/agent/sessions ~/.cursor/projects 2>/dev/null | head -20Then read matching JSONL files with bounded Python extraction. Only use this as a last resort.
hasSourceFile to Disk (and Pruned Sources)hasSourceFile is NOT always a real absolute path. Normalize before any Read:
hasSourceFile prefix | Real on-disk location |
|---|---|
/Users/... | absolute path — use as-is |
/claude-sessions/<munged>/<file> | ~/.claude/projects/<munged>/<file> |
/pi-sessions/<munged>/<file> | ~/.pi/agent/sessions/<munged>/<file> |
/codex-sessions/<path> | ~/.codex/sessions/<path> |
/cursor-sessions/projects/<slug>/... | ~/.cursor/projects/<slug>/... |
resolve_session_path() {
local sf="$1" p=""
case "$sf" in
/Users/*) p="$sf" ;;
/claude-sessions/*) p="$HOME/.claude/projects/${sf#/claude-sessions/}" ;;
/pi-sessions/*) p="$HOME/.pi/agent/sessions/${sf#/pi-sessions/}" ;;
/codex-sessions/*) p="$HOME/.codex/sessions/${sf#/codex-sessions/}" ;;
/cursor-sessions/projects/*) p="$HOME/.cursor/projects/${sf#/cursor-sessions/projects/}" ;;
*) p="$sf" ;;
esac
local stem="${p%.jsonl}"
if [ -f "$p" ]; then echo "FILE:$p"; return; fi
if [ -f "$stem" ]; then echo "FILE:$stem"; return; fi
if [ -d "$stem/subagents" ]; then echo "SUBAGENTS:$stem/subagents"; return; fi
if [ -d "$p/subagents" ]; then echo "SUBAGENTS:$p/subagents"; return; fi
echo "PRUNED:$p"
}If the path is PRUNED, do NOT grep the filesystem. Re-query KnowledgeTriples for that session via extractedInSession and reconstruct from labels + any remaining sioc:content.
Present SPARQL results as markdown tables. Never dump raw JSON to the user.
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX prov: <http://www.w3.org/ns/prov#>
PREFIX sioc: <http://rdfs.org/sioc/ns#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX devkg: <http://devkg.local/ontology#>
PREFIX data: <http://devkg.local/data/>
PREFIX wd: <http://www.wikidata.org/entity/>| Class | Parent | Description |
|---|---|---|
devkg:Session | prov:Activity, sioc:Forum | A working session (conversation) |
devkg:Message | sioc:Post, prov:Entity | A message in a session |
devkg:UserMessage | devkg:Message | Human message |
devkg:AssistantMessage | devkg:Message | AI message |
devkg:ToolCall | prov:Activity | Legacy tool invocation nodes (may be absent in new ingests — do not rely on them) |
devkg:ToolResult | prov:Entity | Legacy tool output (may be absent in new ingests) |
devkg:CodeArtifact | prov:Entity, schema:SoftwareSourceCode | Code file/snippet |
devkg:Entity | prov:Entity | Extracted technical concept |
devkg:KnowledgeTriple | — | Reified triple (subject→predicate→object) with provenance |
devkg:Project | prov:Entity | A development project |
devkg:Developer | prov:Agent | Human developer |
devkg:AIModel | prov:Agent | AI model (Claude, GPT, etc.) |
devkg:Topic | skos:Concept | Knowledge topic |
| Predicate | Domain → Range | Notes |
|---|---|---|
devkg:usedInSession | Message/ToolCall → Session | Links content to its session |
devkg:hasParentMessage | Message → Message | Thread structure |
devkg:mentionsTopic | Message → Topic | Topic tagging |
devkg:invokedTool | AssistantMessage → ToolCall | Tool usage |
devkg:hasToolResult | ToolCall → ToolResult | Tool output |
devkg:producedArtifact | Activity → CodeArtifact | Code generation |
devkg:belongsToProject | Session → Project | Project membership |
devkg:extractedFrom | KnowledgeTriple → Message | Triple provenance |
devkg:extractedInSession | KnowledgeTriple → Session | Triple provenance |
devkg:tripleSubject | KnowledgeTriple → Entity | Reified subject |
devkg:tripleObject | KnowledgeTriple → Entity | Reified object |
devkg:triplePredicateLabel | KnowledgeTriple → xsd:string | Predicate name |
| Property | On | Value |
|---|---|---|
sioc:content | Message | Message text (truncated ~2000 chars at ingest) |
rdfs:label | Entity/Session/Project | Display name |
dcterms:created | Session/Message | ISO datetime |
devkg:hasSourcePlatform | Session | claude-code, pi-coding-agent, codex, cursor, chatgpt, deepseek, grok, warp |
devkg:hasSourceFile | Session | Logical path to raw source — normalize before Read (see "Resolving hasSourceFile to Disk") |
devkg:hasToolName | ToolCall | Legacy — prefer KnowledgeTriples + message content for discovery |
devkg:hasWorkingDirectory | Session | Project directory path |
owl:sameAs | Entity | Wikidata URI (e.g., wd:Q28865) |
These connect devkg:Entity to devkg:Entity via direct edges AND are stored as devkg:triplePredicateLabel strings on reified devkg:KnowledgeTriple nodes:
uses, dependsOn, enables, isPartOf, hasPart, implements, extends, alternativeTo, solves, produces, configures, composesWith, provides, requires, isTypeOf, builtWith, deployedOn, storesIn, queriedWith, integratesWith, broader, narrower, relatedTo, servesAs
Returns all relationships (outbound + inbound) for an entity, with source file and content snippet for provenance. Use CONTAINS for fuzzy matching.
SELECT DISTINCT ?direction ?predicate ?otherLabel ?sourceFile ?platform
(SUBSTR(?content, 1, 150) AS ?snippet) WHERE {
{
?triple a devkg:KnowledgeTriple ;
devkg:tripleSubject ?s ;
devkg:triplePredicateLabel ?predicate ;
devkg:tripleObject ?o ;
devkg:extractedFrom ?msg ;
devkg:extractedInSession ?session .
?s rdfs:label ?sLabel .
?o rdfs:label ?otherLabel .
FILTER(CONTAINS(LCASE(STR(?sLabel)), "ENTITY_LOWER"))
BIND("outbound" AS ?direction)
} UNION {
?triple a devkg:KnowledgeTriple ;
devkg:tripleSubject ?o ;
devkg:triplePredicateLabel ?predicate ;
devkg:tripleObject ?obj ;
devkg:extractedFrom ?msg ;
devkg:extractedInSession ?session .
?obj rdfs:label ?oLabel .
?o rdfs:label ?otherLabel .
FILTER(CONTAINS(LCASE(STR(?oLabel)), "ENTITY_LOWER"))
BIND("inbound" AS ?direction)
}
OPTIONAL { ?session devkg:hasSourceFile ?sourceFile }
OPTIONAL { ?session devkg:hasSourcePlatform ?platform }
OPTIONAL { ?msg sioc:content ?content }
}
ORDER BY ?direction ?predicateReplace ENTITY_LOWER with the lowercase entity name (e.g., neo4j, opentelemetry).
The sourceFile column is a logical path to the original JSONL/JSON file — normalize it with resolve_session_path (see "Resolving hasSourceFile to Disk") before Read; if it resolves to PRUNED, reconstruct from the triples instead.
SELECT DISTINCT ?predicate ?sourceSnippet WHERE {
?triple a devkg:KnowledgeTriple ;
devkg:tripleSubject ?s ;
devkg:triplePredicateLabel ?predicate ;
devkg:tripleObject ?o ;
devkg:extractedFrom ?msg .
?s rdfs:label ?sLabel .
?o rdfs:label ?oLabel .
OPTIONAL { ?msg sioc:content ?c . BIND(SUBSTR(?c, 1, 150) AS ?sourceSnippet) }
FILTER(
CONTAINS(LCASE(STR(?sLabel)), "ENTITY_X") &&
CONTAINS(LCASE(STR(?oLabel)), "ENTITY_Y")
)
}SELECT DISTINCT ?subjectLabel ?objectLabel WHERE {
?triple a devkg:KnowledgeTriple ;
devkg:tripleSubject ?s ;
devkg:triplePredicateLabel "PREDICATE" ;
devkg:tripleObject ?o .
?s rdfs:label ?subjectLabel .
?o rdfs:label ?objectLabel .
FILTER(CONTAINS(LCASE(STR(?subjectLabel)), "ENTITY_LOWER")
|| CONTAINS(LCASE(STR(?objectLabel)), "ENTITY_LOWER"))
}Replace PREDICATE with one of the 24 predicates (e.g., uses, integratesWith).
SELECT ?session ?platform ?created ?title WHERE {
?session a devkg:Session .
OPTIONAL { ?session devkg:hasSourcePlatform ?platform }
OPTIONAL { ?session dcterms:created ?created }
OPTIONAL { ?session dcterms:title ?title }
}
ORDER BY DESC(?created)
LIMIT 50Use this first for session discovery, career/product/person questions, or "exact piece of a session." Do not start with label-only Template 6.
Replace TOPIC_LOWER (required) and add intent terms in the second FILTER (at least one).
Example: topic=linkedin, intent=profile|career|roberto|headline|authority.
SELECT DISTINCT ?created ?platform ?sourceFile ?subj ?pred ?obj
(SUBSTR(REPLACE(STR(?content), "\n", " "), 1, 200) AS ?snippet)
WHERE {
{
# Path A: KnowledgeTriple labels match topic + intent
?kt a devkg:KnowledgeTriple ;
devkg:tripleSubject ?s ;
devkg:tripleObject ?o ;
devkg:triplePredicateLabel ?pred ;
devkg:extractedFrom ?msg ;
devkg:extractedInSession ?sess .
?s rdfs:label ?subj .
?o rdfs:label ?obj .
FILTER(LANG(?subj) = "" && LANG(?obj) = "")
BIND(LCASE(CONCAT(STR(?subj), " ", STR(?obj))) AS ?tripleText)
FILTER(CONTAINS(?tripleText, "TOPIC_LOWER"))
FILTER(
CONTAINS(?tripleText, "INTENT1")
|| CONTAINS(?tripleText, "INTENT2")
|| CONTAINS(?tripleText, "INTENT3")
)
}
UNION
{
# Path B: message text matches topic + intent (catches misses in entity extraction)
?msg a ?msgType ;
sioc:content ?content ;
sioc:has_container ?sess .
FILTER(?msgType IN (devkg:AssistantMessage, devkg:UserMessage))
?kt a devkg:KnowledgeTriple ;
devkg:extractedFrom ?msg ;
devkg:extractedInSession ?sess ;
devkg:tripleSubject ?s ;
devkg:tripleObject ?o ;
devkg:triplePredicateLabel ?pred .
?s rdfs:label ?subj .
?o rdfs:label ?obj .
FILTER(LANG(?subj) = "" && LANG(?obj) = "")
BIND(LCASE(STR(?content)) AS ?msgText)
FILTER(CONTAINS(?msgText, "TOPIC_LOWER"))
FILTER(
CONTAINS(?msgText, "INTENT1")
|| CONTAINS(?msgText, "INTENT2")
|| CONTAINS(?msgText, "INTENT3")
)
}
OPTIONAL { ?msg sioc:content ?content }
OPTIONAL { ?sess devkg:hasSourcePlatform ?platform }
OPTIONAL { ?sess devkg:hasSourceFile ?sourceFile }
OPTIONAL { ?sess dcterms:created ?created }
}
ORDER BY DESC(?created)
LIMIT 40Present as a session table grouped by sourceFile (date, platform, hit count, 1–2 sample facts/snippets). Reason from triples + snippets when possible; open JSONL only if the user needs full wording beyond the 2000-char cap.
If intent is unknown, keep topic FILTER and drop the intent FILTER (broader recall).
Simpler label scan. Prefer Template 5 when the user asks where or which sessions.
SELECT DISTINCT ?session ?platform ?created ?sourceFile WHERE {
?triple a devkg:KnowledgeTriple ;
devkg:extractedInSession ?session .
{ ?triple devkg:tripleSubject ?e . ?e rdfs:label ?label . }
UNION
{ ?triple devkg:tripleObject ?e . ?e rdfs:label ?label . }
FILTER(CONTAINS(LCASE(STR(?label)), "TOPIC_LOWER"))
OPTIONAL { ?session devkg:hasSourcePlatform ?platform }
OPTIONAL { ?session dcterms:created ?created }
OPTIONAL { ?session devkg:hasSourceFile ?sourceFile }
}
ORDER BY DESC(?created)
LIMIT 30SELECT ?label (GROUP_CONCAT(DISTINCT ?platform; separator=", ") AS ?platforms)
(COUNT(DISTINCT ?platform) AS ?platformCount) WHERE {
?triple a devkg:KnowledgeTriple ;
devkg:tripleSubject ?e ;
devkg:extractedInSession ?session .
?session devkg:hasSourcePlatform ?platform .
?e rdfs:label ?label .
FILTER(LANG(?label) = "")
}
GROUP BY ?label
HAVING(COUNT(DISTINCT ?platform) > 1)
ORDER BY DESC(?platformCount)
LIMIT 40SELECT ?label ?wikidataURI ?description WHERE {
?entity a devkg:Entity ;
rdfs:label ?label ;
owl:sameAs ?wikidataURI .
FILTER(STRSTARTS(STR(?wikidataURI), "http://www.wikidata.org"))
FILTER(CONTAINS(LCASE(STR(?label)), "ENTITY_LOWER"))
FILTER(LANG(?label) = "")
OPTIONAL { ?entity dcterms:description ?description }
}
LIMIT 20Searches both user and assistant messages (assistant text holds most extractable knowledge).
SELECT ?platform ?created ?sourceFile
(SUBSTR(REPLACE(STR(?content), "\n", " "), 1, 200) AS ?snippet)
WHERE {
?msg a ?msgType ;
sioc:content ?content ;
sioc:has_container ?session .
FILTER(?msgType IN (devkg:AssistantMessage, devkg:UserMessage))
OPTIONAL { ?session dcterms:created ?created }
OPTIONAL { ?session devkg:hasSourcePlatform ?platform }
OPTIONAL { ?session devkg:hasSourceFile ?sourceFile }
FILTER(CONTAINS(LCASE(?content), "KEYWORD_LOWER"))
}
ORDER BY DESC(?created)
LIMIT 20Given a session URI or sourceFile, return predicate mix + sample provenance facts (no JSONL required for a light summary).
SELECT ?pred (COUNT(?kt) AS ?n) WHERE {
?sess devkg:hasSourceFile ?sourceFile .
FILTER(CONTAINS(STR(?sourceFile), "SESSION_PATH_FRAGMENT"))
?kt a devkg:KnowledgeTriple ;
devkg:extractedInSession ?sess ;
devkg:triplePredicateLabel ?pred .
}
GROUP BY ?pred
ORDER BY DESC(?n)
LIMIT 24Follow with sample facts:
SELECT ?subj ?pred ?obj
(SUBSTR(REPLACE(STR(?content), "\n", " "), 1, 160) AS ?snippet)
WHERE {
?sess devkg:hasSourceFile ?sourceFile .
FILTER(CONTAINS(STR(?sourceFile), "SESSION_PATH_FRAGMENT"))
?kt a devkg:KnowledgeTriple ;
devkg:extractedInSession ?sess ;
devkg:tripleSubject ?s ;
devkg:tripleObject ?o ;
devkg:triplePredicateLabel ?pred ;
devkg:extractedFrom ?msg .
?s rdfs:label ?subj . ?o rdfs:label ?obj .
FILTER(LANG(?subj) = "" && LANG(?obj) = "")
OPTIONAL { ?msg sioc:content ?content }
}
LIMIT 15Traverses outbound edges from X, then follows outbound edges from each neighbor. Shows the subgraph reachable in 2 hops.
SELECT DISTINCT ?aLabel ?p1 ?bLabel ?p2 ?cLabel WHERE {
?t1 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?a ;
devkg:triplePredicateLabel ?p1 ;
devkg:tripleObject ?b .
?a rdfs:label ?aLabel .
?b rdfs:label ?bLabel .
FILTER(LANG(?aLabel) = "" && LANG(?bLabel) = "")
FILTER(CONTAINS(LCASE(?aLabel), "ENTITY_LOWER"))
OPTIONAL {
?t2 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?b ;
devkg:triplePredicateLabel ?p2 ;
devkg:tripleObject ?c .
?c rdfs:label ?cLabel .
FILTER(LANG(?cLabel) = "")
}
}
ORDER BY ?bLabel ?cLabel
LIMIT 40For bidirectional 2-hop (also follows inbound edges), add a second UNION branch that reverses subject/object in each hop.
SELECT ?label (COUNT(DISTINCT ?triple) AS ?degree) WHERE {
{
?triple a devkg:KnowledgeTriple ;
devkg:tripleSubject ?e .
?e rdfs:label ?label .
FILTER(LANG(?label) = "")
} UNION {
?triple a devkg:KnowledgeTriple ;
devkg:tripleObject ?e .
?e rdfs:label ?label .
FILTER(LANG(?label) = "")
}
}
GROUP BY ?label
ORDER BY DESC(?degree)
LIMIT 20SELECT ?s1File ?s2File
(COUNT(DISTINCT ?label) AS ?shared)
(GROUP_CONCAT(DISTINCT ?label; separator=", ") AS ?sharedEntities)
WHERE {
?t1 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?e1 ;
devkg:extractedInSession ?sess1 .
?t2 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?e2 ;
devkg:extractedInSession ?sess2 .
?e1 rdfs:label ?label .
?e2 rdfs:label ?label .
FILTER(LANG(?label) = "")
FILTER(STR(?sess1) < STR(?sess2))
OPTIONAL { ?sess1 devkg:hasSourceFile ?s1File }
OPTIONAL { ?sess2 devkg:hasSourceFile ?s2File }
}
GROUP BY ?s1File ?s2File
HAVING(COUNT(DISTINCT ?label) > 2)
ORDER BY DESC(?shared)
LIMIT 10SELECT DISTINCT ?p1 ?midLabel ?p2 WHERE {
{
?t1 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?a ;
devkg:triplePredicateLabel ?p1 ;
devkg:tripleObject ?mid .
?t2 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?mid ;
devkg:triplePredicateLabel ?p2 ;
devkg:tripleObject ?b .
} UNION {
?t1 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?mid ;
devkg:triplePredicateLabel ?p1 ;
devkg:tripleObject ?a .
?t2 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?mid ;
devkg:triplePredicateLabel ?p2 ;
devkg:tripleObject ?b .
} UNION {
?t1 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?a ;
devkg:triplePredicateLabel ?p1 ;
devkg:tripleObject ?mid .
?t2 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?b ;
devkg:triplePredicateLabel ?p2 ;
devkg:tripleObject ?mid .
}
?a rdfs:label ?aLabel .
?b rdfs:label ?bLabel .
?mid rdfs:label ?midLabel .
FILTER(LANG(?aLabel) = "" && LANG(?bLabel) = "" && LANG(?midLabel) = "")
FILTER(CONTAINS(LCASE(?aLabel), "ENTITY_X"))
FILTER(CONTAINS(LCASE(?bLabel), "ENTITY_Y"))
FILTER(?a != ?b && ?a != ?mid && ?mid != ?b)
}
LIMIT 20Present as: ENTITY_X --p1--> intermediate --p2--> ENTITY_Y
SELECT ?label (COUNT(DISTINCT ?triple) AS ?mentions) WHERE {
?session devkg:belongsToProject ?project .
?project rdfs:label ?projectLabel .
FILTER(CONTAINS(LCASE(?projectLabel), "PROJECT_LOWER"))
?triple a devkg:KnowledgeTriple ;
devkg:extractedInSession ?session .
{ ?triple devkg:tripleSubject ?e . ?e rdfs:label ?label . }
UNION
{ ?triple devkg:tripleObject ?e . ?e rdfs:label ?label . }
FILTER(LANG(?label) = "")
}
GROUP BY ?label
ORDER BY DESC(?mentions)
LIMIT 30SELECT DISTINCT ?siblingLabel ?predicate ?sharedLabel WHERE {
?t1 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?x ;
devkg:triplePredicateLabel ?predicate ;
devkg:tripleObject ?shared .
?t2 a devkg:KnowledgeTriple ;
devkg:tripleSubject ?sibling ;
devkg:triplePredicateLabel ?predicate ;
devkg:tripleObject ?shared .
?x rdfs:label ?xLabel .
?sibling rdfs:label ?siblingLabel .
?shared rdfs:label ?sharedLabel .
FILTER(LANG(?xLabel) = "" && LANG(?siblingLabel) = "" && LANG(?sharedLabel) = "")
FILTER(CONTAINS(LCASE(?xLabel), "ENTITY_LOWER"))
FILTER(?x != ?sibling)
}
ORDER BY ?predicate ?sharedLabel
LIMIT 40Many entities in the local graph have owl:sameAs links to Wikidata QIDs. You can cross into Wikidata's public SPARQL endpoint to discover knowledge that doesn't exist locally — drug classes, software ecosystems, related technologies, disambiguation, etc.
Wikidata endpoint: https://query.wikidata.org/sparql
Execution pattern (same as local, but different URL + requires User-Agent header):
curl -s -X POST 'https://query.wikidata.org/sparql' \
-H 'Accept: application/sparql-results+json' \
-H 'User-Agent: DevKG/1.0' \
--data-urlencode "query=YOUR_SPARQL_HERE" \
| jq -r '...'Rate limits: Wikidata allows ~60 requests/minute for anonymous users. Add 1-second delay between queries if doing batch lookups.
owl:sameAs Wikidata URIReturns all direct properties with human-readable labels. Use this first to understand what's available.
SELECT ?propLabel ?valLabel WHERE {
wd:QID ?p ?val .
?prop wikibase:directClaim ?p .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en" }
} LIMIT 30Key properties to look for:
instance of (P31) — what kind of thing it issubclass of (P279) — broader categoryhas use (P366) — what it's used forprogrammed in (P277) — implementation language (software)uses (P2283) — technologies it depends onpart of (P361) — larger system it belongs toATC code (P267) — drug classification (medications)route of administration (P636) — how a drug is takenGiven a QID, finds its instance of class, then finds all other instances of that class. Discovers alternatives and competitors.
SELECT ?peerLabel ?peerDescription WHERE {
wd:QID wdt:P31 ?class .
?peer wdt:P31 ?class .
FILTER(?peer != wd:QID)
SERVICE wikibase:label { bd:serviceParam wikibase:language "en" }
} LIMIT 20Examples:
instance of: graph database management system → finds ArangoDB, JanusGraph, Amazon Neptune, Dgraph, etc.instance of: type of chemical entity → (too broad, use P2868 "subject has role" or ATC code instead)When an entity label is ambiguous, fetch the Wikidata description to verify. Use this before trusting an owl:sameAs link.
SELECT ?label ?description WHERE {
wd:QID rdfs:label ?label .
wd:QID schema:description ?description .
FILTER(LANG(?label) = "en")
FILTER(LANG(?description) = "en")
}Traverses subclass of (P279) upward to find the classification hierarchy.
SELECT ?classLabel ?superClassLabel WHERE {
wd:QID wdt:P31 ?class .
?class wdt:P279* ?superClass .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en" }
} LIMIT 20When two local entities have Wikidata links but no direct local connection, check if Wikidata knows a relationship.
SELECT ?propLabel WHERE {
wd:QID_X ?p wd:QID_Y .
?prop wikibase:directClaim ?p .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en" }
}If no direct link, try 2-hop:
SELECT ?propLabel1 ?midLabel ?propLabel2 WHERE {
wd:QID_X ?p1 ?mid .
?mid ?p2 wd:QID_Y .
?prop1 wikibase:directClaim ?p1 .
?prop2 wikibase:directClaim ?p2 .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en" }
} LIMIT 10First get all QIDs from the local graph, then query Wikidata for their classes in one request.
Step 1 (local): Extract QIDs
SELECT ?label (REPLACE(STR(?wikidata), "http://www.wikidata.org/entity/", "") AS ?qid) WHERE {
?e a devkg:Entity ; rdfs:label ?label ; owl:sameAs ?wikidata .
FILTER(LANG(?label) = "")
FILTER(STRSTARTS(STR(?wikidata), "http://www.wikidata.org"))
}Step 2 (Wikidata): Get classes for multiple QIDs at once (use VALUES clause):
SELECT ?item ?itemLabel ?classLabel WHERE {
VALUES ?item { wd:Q1628290 wd:Q183554 wd:Q28865 }
?item wdt:P31 ?class .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en" }
}| Question | Local enough? | Use Wikidata? |
|---|---|---|
| "What does X integrate with?" | Yes (Template 1) | No |
| "What kind of thing is X?" | Maybe (if isTypeOf exists) | Yes (W1, W4) |
| "What are alternatives to X?" | Maybe (if alternativeTo exists) | Yes (W2) |
| "Is this the right entity?" | No | Yes (W3) |
| "How does X relate to Y globally?" | No | Yes (W5) |
| "What drug class is X in?" | No | Yes (W1 → ATC code, P2868) |
| "What language is X written in?" | Maybe | Yes (W1 → P277) |
DISTINCT — duplicate triples exist from lang-tagged vs untagged literals.FILTER(LANG(?label) = "") to avoid duplicate rows from lang-tagged literals.LCASE() in FILTER for safety.linkedin) and intent (profile, career, roberto).KnowledgeTriple nodes carry provenance: extractedFrom → source message, extractedInSession → session. Always project these when the user needs where.sioc:content is capped at ~2000 chars — enough to locate and lightly reason; open JSONL only for full fidelity.hasSourceFile, normalize the path first. If PRUNED, reconstruct from triples — do not grep.uses, dependsOn, solves, …) over treating the graph as a tag cloud of labels.© robertoshimizu, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .claude/skills/devkg-sparql of robertoshimizu/session-graph.
Open the folder on GitHubat commit 4adefaa
Devkg Sparql next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Devkg Sparql this skillrobertoshimizu/session-graph | 112 | — | ~7.6k | Automated safety check: Pass | Apache-2.0 | |
| Obsidian Canvas BoardsAgriciDaniel/claude-obsidian | 15k | — | ~1.4k | Automated safety check: Pass | MIT | |
| Ontology1mancompany/OneManCompany | 441 | 2 repos | ~1.5k | Automated safety check: Pass | Apache-2.0 | |
| Knowledge Graphgnomeria/usbtree | 691 | — | ~1.5k | Automated safety check: Pass | MIT | |
| Graphagenticnotetaking/arscontexta | 3.5k | — | ~4.9k | Automated safety check: Notes | MIT | |
| LLM Wiki Knowledge GraphEgonex-AI/Understand-Anything | 86k | — | ~1.5k | Automated safety check: Pass | MIT |
AgriciDaniel/claude-obsidian
Creates, inspects and updates Obsidian JSON Canvas boards in a vault, with text, file, link, group and edge nodes, using safe recoverable edits.
1mancompany/OneManCompany
Typed knowledge graph for structured agent memory and composable skills.
gnomeria/usbtree
Set up and maintain a lightweight, file-based knowledge graph of the repo — entities, typed relations, decisions, gotchas — so agents load context fast instead of re-exploring the codebase every…
agenticnotetaking/arscontexta
Interactive knowledge graph analysis. An agent skill from agenticnotetaking/arscontexta.
Egonex-AI/Understand-Anything
Detects a Karpathy-pattern LLM wiki and builds an interactive knowledge graph with entities, implicit relationships and topic clusters.
aws-samples/sample-kolya-br-proxy
A skill your agent uses when the user asks about GitNexus itself — available tools, how to query the knowledge graph, MCP resources, graph schema, or workflow reference.
Categories
Query the Dev Knowledge Graph via SPARQL instead of grepping raw session files. Devkg Sparql is an agent skill from robertoshimizu/session-graph. Query the Dev Knowledge Graph via SPARQL instead of grepping raw session files.
Devkg Sparql fits situations like: tasks that involve Knowledge graphs.
Run `npx skills add robertoshimizu/session-graph --skill devkg-sparql -a claude-code`. Or copy the skill folder (.claude/skills/devkg-sparql in robertoshimizu/session-graph) into .claude/skills/devkg-sparql in your project. Claude Code loads it when a task matches its description.
Run `npx skills add robertoshimizu/session-graph --skill devkg-sparql -a codex`. Or copy the skill folder (.claude/skills/devkg-sparql in robertoshimizu/session-graph) into .agents/skills/devkg-sparql in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add robertoshimizu/session-graph --skill devkg-sparql -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/devkg-sparql, .gemini/skills/devkg-sparql, .github/skills/devkg-sparql and .opencode/skills/devkg-sparql in your project.
Going by SKILL.md and its folder, Devkg Sparql needs the command-line tools its instructions call (curl and jq). Our summary lists: Python 3. Its frontmatter pre-approves these tools: Bash(curl:*), Bash(jq:*).
SKILL.md names 5 domains. In commands or code: w3.org, wikidata.org, query.wikidata.org, rdfs.org and purl.org; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Devkg Sparql is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 7.6k tokens (SKILL.md is roughly 30k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Devkg Sparql: Obsidian Canvas Boards (AgriciDaniel/claude-obsidian, 15k stars), Ontology (1mancompany/OneManCompany, 441 stars), Knowledge Graph (gnomeria/usbtree, 691 stars) and Graph (agenticnotetaking/arscontexta, 3.5k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
robertoshimizu (a GitHub user) maintains it in robertoshimizu/session-graph, which has 112 GitHub stars. The repository was last updated on July 29, 2026.
Source: robertoshimizu/session-graph on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.