Skip to content

Commit 5827d07

Browse files
committed
feat(status): show KB path so agents can locate the wiki from anywhere
The skill in skills/openkb/ implicitly assumed cwd == KB root, but openkb's own resolution (`_find_kb_dir`) walks up from cwd and falls back to the global default set by `openkb use` — so a user running Claude Code from their home directory still has an "active" KB, but neither the agent nor the user has any obvious way to discover where it lives. The first smoke test of the skill exposed this: Claude in the source repo correctly ran `openkb list` first but had no way to find the wiki. Fix is one line in `print_status` plus a documentation update — status now leads with: Knowledge base: /absolute/path/to/kb Agents parse this from the first line; humans see it as natural context. No new command, no breaking change to existing fields. Updates the openkb skill (SKILL.md + references/commands.md) to make `openkb status` the mandatory first step, with the captured path used for every subsequent Read / Grep / jq call.
1 parent 8c17904 commit 5827d07

4 files changed

Lines changed: 93 additions & 19 deletions

File tree

‎openkb/cli.py‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,10 @@ def print_status(kb_dir: Path) -> None:
10191019
wiki_dir = kb_dir / "wiki"
10201020
subdirs = ["sources", "summaries", "concepts", "reports"]
10211021

1022+
# Print the active KB path as the first line. Agents and scripts
1023+
# parse this to locate the wiki without assuming cwd == KB root.
1024+
click.echo(f"Knowledge base: {kb_dir}")
1025+
click.echo("")
10221026
click.echo("Knowledge Base Status:")
10231027
click.echo(f" {'Directory':<20} {'Files':<10}")
10241028
click.echo(f" {'-'*20} {'-'*10}")
@@ -1068,7 +1072,11 @@ def print_status(kb_dir: Path) -> None:
10681072
@cli.command()
10691073
@click.pass_context
10701074
def status(ctx):
1071-
"""Show the current status of the knowledge base."""
1075+
"""Show the current status of the knowledge base.
1076+
1077+
Output starts with a ``Knowledge base: <path>`` line so agents and
1078+
scripts can locate the wiki without assuming cwd == KB root.
1079+
"""
10721080
kb_dir = _find_kb_dir(ctx.obj.get("kb_dir_override"))
10731081
if kb_dir is None:
10741082
click.echo("No knowledge base found. Run `openkb init` first.")

‎skills/openkb/SKILL.md‎

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,62 @@ The wiki holds three kinds of pages:
2424
- **Source files** at `wiki/sources/*.{md,json}` — full text for short
2525
docs (`.md`) or a paginated content array for long PDFs (`.json`).
2626

27+
## First: find where the KB lives
28+
29+
The user may invoke you from anywhere — the active knowledge base is
30+
not necessarily in your current working directory. Run `openkb status`
31+
to discover the KB root and a summary in one call:
32+
33+
```
34+
$ openkb status
35+
Knowledge base: /Users/.../my-kb
36+
37+
Knowledge Base Status:
38+
Directory Files
39+
-------------------- ----------
40+
sources 5
41+
summaries 5
42+
concepts 12
43+
...
44+
```
45+
46+
The first line — `Knowledge base: <path>` — is the absolute path you
47+
should use for every `Read` / `Grep` / `jq` call below. The same
48+
resolution rules `openkb` itself uses apply: walks up from cwd looking
49+
for `.openkb/`, then falls back to the global default set by
50+
`openkb use`.
51+
52+
If `openkb status` says "No knowledge base found", tell the user to
53+
`cd` into their KB or run `openkb init` to create one — don't proceed.
54+
2755
## See what's available
2856

29-
Use any of these to discover the catalog before drilling in:
57+
After capturing the KB path from `openkb status`, drill in via:
3058

3159
- `openkb list` — table of ingested documents (name, type, page count)
3260
plus the concept list.
33-
- `openkb status` — overall stats (doc count, concept count).
34-
- `Read wiki/index.md` — the compiled table of contents. Every
61+
- `Read <kb>/wiki/index.md` — the compiled table of contents. Every
3562
document and concept has a one-line `brief`. Scan this and pick the
3663
slugs that semantically match the user's question.
3764

3865
## Read content
3966

67+
(Paths shown relative to the KB root from `openkb where`. Prepend it
68+
in real calls.)
69+
4070
| Goal | How |
4171
|---|---|
42-
| Read a concept page | `Read wiki/concepts/<slug>.md` |
43-
| Read a document's summary | `Read wiki/summaries/<doc>.md` |
44-
| Read a short doc's full text | `Read wiki/sources/<doc>.md` |
45-
| Read a long doc's specific page | `jq '.[N]' wiki/sources/<doc>.json` (page N, 0-indexed) |
72+
| Read a concept page | `Read <kb>/wiki/concepts/<slug>.md` |
73+
| Read a document's summary | `Read <kb>/wiki/summaries/<doc>.md` |
74+
| Read a short doc's full text | `Read <kb>/wiki/sources/<doc>.md` |
75+
| Read a long doc's specific page | `jq '.[N]' <kb>/wiki/sources/<doc>.json` (page N, 0-indexed) |
4676
| Get a synthesized answer across sources | `openkb query "<question>"` |
47-
| Find an exact phrase | `Grep -r "<phrase>" wiki/` |
48-
| Follow a `[[wikilink]]` | `Read` the linked path |
77+
| Find an exact phrase | `Grep -r "<phrase>" <kb>/wiki/` |
78+
| Follow a `[[wikilink]]` | `Read` the linked path under `<kb>/wiki/` |
4979

5080
Concept and summary bodies use `[[concepts/<slug>]]` and
51-
`[[summaries/<doc>]]` wikilinks. They are relative paths — follow them
52-
by Reading the corresponding file.
81+
`[[summaries/<doc>]]` wikilinks. They are wiki-relative paths — follow
82+
them by Reading `<kb>/wiki/<target>.md`.
5383

5484
## Frontmatter
5585

‎skills/openkb/references/commands.md‎

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,35 @@ Concepts (5):
3737

3838
## `openkb status`
3939

40-
Knowledge base overview (run from inside a KB directory).
40+
Knowledge base overview. **Always run this first** when working with
41+
an OpenKB KB — its first line tells you where the KB lives, which is
42+
what you need for every `Read` / `Grep` / `jq` call afterwards.
4143

4244
```
4345
$ openkb status
44-
Knowledge base at /path/to/kb
45-
Documents: 2 (long_pdf: 1, short: 1)
46-
Concepts: 5
47-
Last ingest: 2026-05-16 12:14:12 (paper.pdf)
46+
Knowledge base: /path/to/kb
47+
48+
Knowledge Base Status:
49+
Directory Files
50+
-------------------- ----------
51+
sources 5
52+
summaries 5
53+
concepts 12
54+
reports 2
55+
raw 5
56+
57+
Total indexed: 5 document(s)
58+
Last compile: 2026-05-16 12:14:12
59+
Last lint: 2026-05-16 12:16:31
4860
```
4961

50-
Use this as a first read when the user asks "what does your KB look
51-
like?" or "how big is the KB?".
62+
- The `Knowledge base: <path>` line is parseable: it's the absolute
63+
path of the active KB. The user may have invoked you from anywhere
64+
— never assume cwd is the KB root; use this path.
65+
- Resolution: walks up from cwd looking for `.openkb/`, then falls
66+
back to the global default set by `openkb use`.
67+
- Empty case: prints "No knowledge base found. Run `openkb init`
68+
first." Tell the user this and stop — don't try to read files.
5269

5370
## `openkb query "<question>"`
5471

‎tests/test_list_status.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,22 @@ def test_status_exit_code_zero(self, tmp_path):
148148
result = runner.invoke(cli, ["status"])
149149

150150
assert result.exit_code == 0
151+
152+
153+
class TestStatusKbPath:
154+
"""Status output must lead with the active KB path so agents and
155+
scripts can locate the wiki when invoked from outside the KB root."""
156+
157+
def test_status_prints_kb_path_first(self, tmp_path):
158+
kb_dir = _setup_kb(tmp_path)
159+
160+
runner = CliRunner()
161+
with patch("openkb.cli._find_kb_dir", return_value=kb_dir):
162+
result = runner.invoke(cli, ["status"])
163+
164+
assert result.exit_code == 0
165+
# First non-empty line carries the path in a parseable form:
166+
# "Knowledge base: /path/to/kb"
167+
first_line = result.output.splitlines()[0]
168+
assert first_line.startswith("Knowledge base: ")
169+
assert first_line.split(": ", 1)[1] == str(kb_dir)

0 commit comments

Comments
 (0)