Skip to content

Commit ba2795e

Browse files
Release 0.11.1: add Pi Coding Agent as a 6th platform
Integrates PR #83 (by @Gepetdo) onto post-0.11.0 main. bash scripts/build.sh --platform pi emits dist/pi/: a native Pi package (package.json with pi.prompts/pi.skills, one prompt template per command under .pi/prompts/, and a discovery skill at .pi/skills/obsidian-second-brain/SKILL.md). Purely additive - the adapter is auto-discovered by build.sh, so the existing five platforms are untouched. Re-counted to the current 44 commands (the adapter reads commands/ dynamically, so the build was already correct; only static docs needed it). Smoke test added; all 6 platforms build, 27 tests pass, substitution check clean. Closes #83 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e8029cd commit ba2795e

8 files changed

Lines changed: 340 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66

77
## [Unreleased]
88

9+
## [0.11.1] - 2026-06-28
10+
11+
### Added
12+
13+
- **Pi Coding Agent as a 6th build target (#83, by @Gepetdo).** `bash scripts/build.sh --platform pi` emits `dist/pi/`, a native [Pi](https://pi.dev) package: a `package.json` declaring `pi.prompts`/`pi.skills`, one prompt template per command under `.pi/prompts/` (invoke `/obsidian-save` etc.), and a discovery skill at `.pi/skills/obsidian-second-brain/SKILL.md` (`/skill:obsidian-second-brain`) with the AI-first rules + Python helpers alongside. Tool-name and path references are neutralized for the Pi layout (no `~/.claude/...` leakage). Purely additive - the adapter is auto-discovered by `build.sh`, so the existing five platforms are untouched; guarded by its own smoke test. Pi has no background-agent equivalent, so `/obsidian-nightly` is run manually or via cron.
14+
915
## [0.11.0] - 2026-06-28 - The Retriever
1016

1117
### Added

CITATION.cff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type: software
2121
license: MIT
2222
repository-code: "https://github.com/eugeniughelbur/obsidian-second-brain"
2323
url: "https://github.com/eugeniughelbur/obsidian-second-brain"
24-
version: "0.11.0"
24+
version: "0.11.1"
2525
date-released: "2026-06-28"
2626
keywords:
2727
- claude-code

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ vault/
565565

566566
## Install
567567

568-
> **One codebase, four platforms.** Pick yours below. The vault behavior is identical across all four; only the install path and the dispatcher file (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md`) differ.
568+
> **One codebase, six platforms.** Pick yours below. The vault behavior is identical across all of them; only the install path and the dispatcher file (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md` / `.pi/`) differ.
569569
570570
### Claude Code (default)
571571

@@ -597,7 +597,18 @@ Then start your CLI from the vault root.
597597

598598
The **Codex build emits native [Codex Agent Skills](https://developers.openai.com/codex/skills)**: one skill per command under `.agents/skills/<name>/SKILL.md`. Codex discovers them automatically with progressive disclosure (only each skill's name + description load until it's selected), and they run **in your current session** - invoke one with `$<name>`, pick it from `/skills`, or just describe the task and let Codex match it implicitly. `AGENTS.md` stays as a thin always-on manual (vault conventions + the AI-first rule); there is no routing table to maintain because the skill list is the router. The Gemini / OpenCode builds still emit a `GEMINI.md` / `AGENTS.md` dispatcher with an auto-generated routing table to command files under `.gemini/` / `.opencode/`.
599599

600-
Run `bash scripts/build.sh` with no arguments to build all four platforms at once. See [`dist/<platform>/INSTALL.md`](scripts/build.sh) after building for platform-specific notes.
600+
Run `bash scripts/build.sh` with no arguments to build every platform at once. See [`dist/<platform>/INSTALL.md`](scripts/build.sh) after building for platform-specific notes.
601+
602+
### Pi Coding Agent
603+
604+
```bash
605+
git clone https://github.com/eugeniughelbur/obsidian-second-brain
606+
cd obsidian-second-brain
607+
bash scripts/build.sh --platform pi
608+
pi install ./dist/pi # or: cp -R dist/pi/.pi/ /path/to/your/vault/
609+
```
610+
611+
The **Pi build emits a native [Pi](https://pi.dev) package**: prompt templates under `.pi/prompts/` (invoke as `/obsidian-save`, `/obsidian-daily`, etc.) plus a discovery skill under `.pi/skills/obsidian-second-brain/` (load with `/skill:obsidian-second-brain`). Pi reads the same `~/.config/obsidian-second-brain/.env` keys as the other platforms. It has no background-agent equivalent - run `/obsidian-nightly` manually or via cron. (Contributed by @Gepetdo.)
601612

602613
### Run on Hermes / open models
603614

adapters/pi/adapter.sh

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
#!/usr/bin/env bash
2+
# =============================================================================
3+
# adapters/pi/adapter.sh - Pi Coding Agent platform adapter
4+
# =============================================================================
5+
# Pi loads prompt templates from .pi/prompts/ and skills from
6+
# .pi/skills/<name>/SKILL.md. This adapter emits a Pi package under dist/pi/
7+
# that can be installed with:
8+
# pi install ./dist/pi
9+
# or copied into a vault root:
10+
# cp -R dist/pi/.pi/ /path/to/your/vault/
11+
# =============================================================================
12+
13+
PI_PLATFORM="pi"
14+
PI_DIR="pi"
15+
16+
adapter_build() {
17+
local src="$1" dst="$2"
18+
19+
_pi_emit_package_json "$src" "$dst"
20+
_pi_emit_prompts "$src/commands" "$dst/.pi/prompts"
21+
_pi_emit_skill "$src" "$dst/.pi/skills/obsidian-second-brain"
22+
_pi_copy_references "$src/references" "$dst/.pi/skills/obsidian-second-brain/references"
23+
_pi_copy_scripts "$src/scripts" "$dst/.pi/skills/obsidian-second-brain/scripts"
24+
_pi_emit_install_hint "$dst"
25+
}
26+
27+
_pi_emit_package_json() {
28+
local src="$1" dst="$2"
29+
local version
30+
version="$(grep -m1 '^version' "$src/pyproject.toml" 2>/dev/null | sed 's/.*=[[:space:]]*"//; s/".*//')"
31+
[[ -n "$version" ]] || version="0.0.0"
32+
33+
mkdir -p "$dst"
34+
cat > "$dst/package.json" <<EOF
35+
{
36+
"name": "obsidian-second-brain-pi",
37+
"version": "${version}",
38+
"description": "Pi package for obsidian-second-brain: prompt templates and skill to operate an Obsidian vault as a self-rewriting second brain.",
39+
"keywords": ["pi-package", "obsidian", "second-brain", "pkm"],
40+
"license": "MIT",
41+
"author": "Eugeniu Ghelbur",
42+
"repository": {
43+
"type": "git",
44+
"url": "https://github.com/eugeniughelbur/obsidian-second-brain.git"
45+
},
46+
"pi": {
47+
"prompts": [".pi/prompts"],
48+
"skills": [".pi/skills"]
49+
}
50+
}
51+
EOF
52+
}
53+
54+
_pi_emit_prompts() {
55+
local src="$1" dst="$2"
56+
[[ -d "$src" ]] || return 0
57+
mkdir -p "$dst"
58+
59+
local f name desc out
60+
for f in "$src"/*.md; do
61+
[[ -f "$f" ]] || continue
62+
should_include "$f" "$PI_PLATFORM" || continue
63+
64+
name="$(basename "$f" .md)"
65+
desc="$(parse_frontmatter "$f" description)"
66+
[[ -z "$desc" ]] && desc="Run the /$name command of the obsidian-second-brain skill."
67+
desc="${desc#\"}"; desc="${desc%\"}"
68+
69+
out="$dst/$name.md"
70+
{
71+
echo "---"
72+
printf 'description: "%s"\n' "${desc//\"/\\\"}"
73+
if _pi_command_takes_args "$f"; then
74+
printf 'argument-hint: "[args]"\n'
75+
fi
76+
echo "---"
77+
echo
78+
echo "Run the obsidian-second-brain /$name command:"
79+
echo
80+
command_body "$f"
81+
} > "$out"
82+
83+
_pi_rewrite_paths_for_prompt "$out"
84+
rewrite_tool_neutral "$out"
85+
done
86+
}
87+
88+
_pi_command_takes_args() {
89+
local file="$1"
90+
command_body "$file" | grep -qE '<[^>]+>' \
91+
|| parse_frontmatter "$file" description | grep -qE '<[^>]+>'
92+
}
93+
94+
_pi_rewrite_paths_for_prompt() {
95+
local file="$1"
96+
[[ -f "$file" ]] || return 0
97+
perl -i -pe '
98+
s|~/.claude/skills/obsidian-second-brain/|.pi/skills/obsidian-second-brain/|g;
99+
s~(^|[^A-Za-z0-9_./-])\.?/?references/~$1.pi/skills/obsidian-second-brain/references/~g;
100+
s~(^|[^A-Za-z0-9_./-])\.?/?scripts/(?!build\.sh)~$1.pi/skills/obsidian-second-brain/scripts/~g;
101+
' "$file"
102+
}
103+
104+
_pi_emit_skill() {
105+
local src="$1" dst="$2"
106+
mkdir -p "$dst"
107+
local out="$dst/SKILL.md"
108+
109+
{
110+
cat <<'EOF'
111+
---
112+
name: obsidian-second-brain
113+
description: >
114+
Operate any Obsidian vault as a living, self-rewriting second brain.
115+
Use this skill when the user asks to save, search, update, or manage their
116+
Obsidian vault, or when they invoke any /obsidian-* or /research command.
117+
---
118+
119+
# Obsidian Second Brain - Pi Operating Manual
120+
121+
This vault can run the **obsidian-second-brain** skill. The skill ships prompt
122+
templates under `.pi/prompts/` (type `/obsidian-save`, `/obsidian-daily`, etc.)
123+
and this discovery skill (`/skill:obsidian-second-brain`).
124+
125+
## How to operate
126+
127+
1. Read `_CLAUDE.md` in the vault root, if it exists, to learn the user's vault
128+
conventions.
129+
2. When the user types a prompt template like `/obsidian-save`, follow its
130+
instructions step by step.
131+
3. Treat the AI-first vault rule
132+
(`./references/ai-first-rules.md`) as non-negotiable for every note you
133+
write: `## For future Claude` preamble, rich frontmatter
134+
(`type`, `date`, `tags`, `ai-first: true`), `[[wikilinks]]` for every
135+
person/project/concept, recency markers per external claim, sources verbatim,
136+
confidence levels where applicable.
137+
4. If `_CLAUDE.md` is missing, run `/obsidian-init` to generate it.
138+
139+
## Command routing table
140+
EOF
141+
142+
emit_routing_table_grouped "$src/commands" "$PI_PLATFORM" ".pi/prompts"
143+
emit_trigger_reference "$src/commands" "$PI_PLATFORM"
144+
145+
cat <<'EOF'
146+
147+
---
148+
149+
*Generated by adapters/pi/adapter.sh - do not edit manually.*
150+
EOF
151+
} > "$out"
152+
153+
rewrite_tool_neutral "$out"
154+
perl -i -pe '
155+
s|~/.claude/skills/obsidian-second-brain/||g;
156+
s|\.pi/skills/obsidian-second-brain/||g;
157+
s~(^|[^A-Za-z0-9_./-])\.?/?references/~$1./references/~g;
158+
s~(^|[^A-Za-z0-9_./-])\.?/?scripts/(?!build\.sh)~$1./scripts/~g;
159+
' "$out"
160+
}
161+
162+
_pi_copy_references() {
163+
local src="$1" dst="$2"
164+
[[ -d "$src" ]] || return 0
165+
mkdir -p "$dst"
166+
cp -R "$src/." "$dst/"
167+
find "$dst" -type f -name '*.md' -print0 | while IFS= read -r -d '' f; do
168+
rewrite_tool_neutral "$f"
169+
rewrite_platform_paths "$f" "$PI_DIR"
170+
done
171+
}
172+
173+
_pi_copy_scripts() {
174+
local src="$1" dst="$2"
175+
[[ -d "$src" ]] || return 0
176+
mkdir -p "$dst"
177+
cp -R "$src/." "$dst/"
178+
}
179+
180+
_pi_emit_install_hint() {
181+
local dst="$1"
182+
cat > "$dst/INSTALL.md" <<'EOF'
183+
# Install on Pi
184+
185+
Build the Pi package from the repo:
186+
187+
```bash
188+
git clone https://github.com/eugeniughelbur/obsidian-second-brain.git
189+
cd obsidian-second-brain
190+
bash scripts/build.sh --platform pi
191+
```
192+
193+
Then either install the package into Pi:
194+
195+
```bash
196+
pi install ./dist/pi
197+
```
198+
199+
Or copy the built `.pi/` tree into your vault so Pi discovers the prompt
200+
templates and skill from the project root:
201+
202+
```bash
203+
cp -R dist/pi/.pi/ /path/to/your/vault/
204+
```
205+
206+
Then start Pi from your vault root. The `/obsidian-*` and `/research` prompt
207+
templates will be available, and the discovery skill loads via
208+
`/skill:obsidian-second-brain`.
209+
210+
## Research toolkit
211+
212+
The research commands (`/research`, `/research-deep`, `/x-read`, `/x-pulse`,
213+
`/youtube`, `/notebooklm`, `/podcast`) need the same API keys as the other
214+
platforms. Copy `.env.example` to `~/.config/obsidian-second-brain/.env`, set
215+
permissions to `600`, and paste in the required keys. Pi reads those same
216+
environment variables, or you can export them before running Pi.
217+
218+
## Background agents
219+
220+
Pi has no background-bash or PostCompact-hook equivalent. Run `/obsidian-nightly`
221+
manually or schedule it with cron outside of Pi.
222+
EOF
223+
}

docs/pi-testing.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Testing the Pi adapter
2+
3+
This doc covers testing the Pi adapter only. It does not cover the internal
4+
vault logic (that is exercised by the broader `tests/` suite and CLI smoke
5+
tests).
6+
7+
## Run the Pi build
8+
9+
```bash
10+
bash scripts/build.sh --platform pi
11+
```
12+
13+
Inspect the output:
14+
15+
```bash
16+
find dist/pi -maxdepth 4 -type f | sort
17+
```
18+
19+
You should see:
20+
21+
- `dist/pi/package.json`
22+
- `dist/pi/.pi/prompts/*.md`
23+
- `dist/pi/.pi/skills/obsidian-second-brain/SKILL.md`
24+
- `dist/pi/.pi/skills/obsidian-second-brain/references/*.md`
25+
- `dist/pi/.pi/skills/obsidian-second-brain/scripts/*.py`
26+
27+
## Run the Pi smoke test
28+
29+
```bash
30+
pytest -q tests/test_smoke.py::test_pi_build_generates_package -v
31+
```
32+
33+
## Research toolkit setup
34+
35+
If you run any `/research`, `/x-read`, `/youtube`, `/notebooklm`, or `/podcast`
36+
prompt template, copy `.env.example` to `~/.config/obsidian-second-brain/.env`,
37+
set permissions to `600`, and fill in the required API keys. Pi reads the same
38+
environment variables as the other platforms.
39+
40+
## Coverage note
41+
42+
The adapter tests are smoke tests that run the build script as a subprocess.
43+
Because pytest-cov only traces in-process Python, these tests do not appear in
44+
the Python coverage report. Coverage for the Pi adapter is measured by the
45+
assertions in `test_pi_build_generates_package`, which validate:
46+
47+
- `package.json` exists and has the correct `pi` manifest
48+
- every command produces a prompt template with frontmatter
49+
- the discovery skill has valid Agent Skills frontmatter
50+
- Claude-specific paths are rewritten to the Pi layout
51+
52+
To run the Python-coverage report for the importable modules:
53+
54+
```bash
55+
pytest -q --cov=scripts --cov=integrations --cov-report=term-missing
56+
```

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "obsidian-second-brain-research"
3-
version = "0.11.0"
3+
version = "0.11.1"
44
description = "Research toolkit for obsidian-second-brain - Grok, Perplexity, Gemini, and YouTube research that saves to your vault"
55
requires-python = ">=3.10"
66
dependencies = [

scripts/build.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Available platforms:
3737
gemini-cli - Gemini CLI (GEMINI.md + .gemini/commands/)
3838
opencode - OpenCode (AGENTS.md + .opencode/commands/)
3939
hermes - Nous Research Hermes Agent (native skills, skills/<category>/)
40+
pi - Pi Coding Agent (package.json + .pi/prompts/ + .pi/skills/)
4041
EOF
4142
exit 0
4243
;;

tests/test_smoke.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,45 @@ def test_hermes_build_generates_native_skills():
8787
assert "on_session_end" in hooks_doc.read_text(encoding="utf-8")
8888

8989

90+
def test_pi_build_generates_package():
91+
"""The pi adapter must emit a valid Pi package: package.json with pi
92+
prompts/skills entries, prompt templates with frontmatter, and a discovery
93+
skill with valid Agent Skills frontmatter."""
94+
result = subprocess.run(
95+
["bash", "scripts/build.sh", "--platform", "pi"],
96+
cwd=REPO_ROOT,
97+
check=False,
98+
capture_output=True,
99+
text=True,
100+
)
101+
102+
assert result.returncode == 0, result.stderr
103+
104+
package_json = REPO_ROOT / "dist/pi/package.json"
105+
assert package_json.is_file()
106+
manifest = json.loads(package_json.read_text(encoding="utf-8"))
107+
assert manifest["name"] == "obsidian-second-brain-pi"
108+
assert ".pi/prompts" in manifest.get("pi", {}).get("prompts", [])
109+
assert ".pi/skills" in manifest.get("pi", {}).get("skills", [])
110+
111+
prompt = REPO_ROOT / "dist/pi/.pi/prompts/obsidian-save.md"
112+
assert prompt.is_file()
113+
head = prompt.read_text(encoding="utf-8")[:300]
114+
assert "---" in head
115+
assert "description:" in head
116+
117+
skill = REPO_ROOT / "dist/pi/.pi/skills/obsidian-second-brain/SKILL.md"
118+
assert skill.is_file()
119+
skill_head = skill.read_text(encoding="utf-8")[:400]
120+
assert "name: obsidian-second-brain" in skill_head
121+
assert "description:" in skill_head
122+
123+
# Paths should be rewritten for the Pi layout, not left pointing at Claude.
124+
prompt_body = prompt.read_text(encoding="utf-8")
125+
assert "~/.claude/skills/obsidian-second-brain" not in prompt_body
126+
assert ".pi/skills/obsidian-second-brain" in prompt_body
127+
128+
90129
def test_vault_health_json_reports_clean_linked_vault(tmp_path):
91130
"""A minimal two-note vault with reciprocal wikilinks should report zero
92131
issues: no orphans, no broken links, no missing frontmatter."""

0 commit comments

Comments
 (0)