Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ venv/
!.env.example

*.log
*.logs
.logs

# Docs
docs/

# QA / installer scratch (local-only)
QA-Logs/
Expand Down Expand Up @@ -47,11 +52,11 @@ config.toml.bak
CRUSH.md

# Tests are dev-only (not needed on consumer installs)
tests/
!tests/
tests/*
!tests/test_workspace_primer_hooks.py
!tests/test_scriptlib.py
!tests/test_env_probe_codex.py
!tests/test_plan_auditor_master.py

# Ruler outputs at repo root (no leading dot)
/AGENTS.md
Expand All @@ -66,7 +71,7 @@ tests/*
!/.gitattributes
!/.gitmodules
!/.env.example
# `.cursor/` stays fully ignored; ship agents/skills via config/templates/cursor-* (deploy: prime_workspace).
# `.cursor/` stays fully ignored; ship subagents from config/templates/agents/ and skills from config/templates/cursor-skills/ (deploy: prime_workspace).
# If your team commits other root dotdirs (e.g. .husky), add: !/.husky/
# `.devdocs/` is covered by `/.*` above — local-only; never add ! exceptions for it.
# END BRAINDRAIN GITIGNORE PROTOCOL
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,20 @@ The format is based on keeping a clear, user-facing history. Version in `VERSION
- **Scriptlib modularization**: scriptlib now treats project-local `.scriptlib/` and shared `~/.braindrain/scriptlib` as distinct layers, with promotion-only flow into the shared personal catalog.
- **New scriptlib MCP tools**: added promote, update discovery/application, maintenance, and catalog status flows for local/shared script operations.
- **Librarian-first routing**: freestanding reusable scripts are now expected to go through librarian decision flow (`reuse`, `fork`, or `new`) before a fresh script is created.
- **Model provenance controls**: added `provenance` config toggles for chat footer scope, plan metadata stamping, and subagent model tracing (`.braindrain/plan-reports/model-trace.jsonl`), plus audit report frontmatter fields for model/date/cursor mode attribution.
- **Cursor stop hook stability**: `on-stop-observe.sh` is now output-silent by default so Cursor stop-hook JSON parsing is not broken by plain-text stdout.

### For contributors

- **Cursor agents/skills**: librarian and `scriptlib-librarian` live under `config/templates/cursor-subagents/` and `config/templates/cursor-skills/`; `prime_workspace()` deploys both to `.cursor/agents/` and `.cursor/skills/`. The repo root gitignore no longer whitelists paths under `.cursor/` (Braindrain protocol: ship via templates only).
- **Subagent templates**: single source tree `config/templates/agents/` deploys to `.cursor/agents/` and/or `.codex/agents/` depending on the resolved IDE set; duplicate `cursor-subagents/` and `codex-subagents/` template dirs were removed. Skills remain under `config/templates/cursor-skills/`. Added `daily-plan-auditor` agent and planning close-out guidance in Ruler `RULES.md`. Planning audit script moves `archived` plans into `<ide>/plans/.plan.archives/`. Tests: `tests/test_plan_auditor_master.py`.
- Added provenance-aware runtime/tooling paths in `braindrain/server.py`, `braindrain/config.py`, `braindrain/types.py`, and `scripts/daily_plan_audit.py`, with tests in `tests/test_plan_auditor_master.py` and `tests/test_provenance_config.py`.

## [1.0.3] — 2026-04-10

### For users

- You can opt into **scriptlib**: harvest and search reusable scripts, run them through MCP, and keep guidance in agent rules when scriptlib is enabled for a workspace.
- **prime_workspace** now deploys **Cursor and Codex subagent templates** from `config/templates/cursor-subagents/` and `config/templates/codex-subagents/`, with optional `sync_subagents` and `codex_agent_targets` for Codex layout.
- **prime_workspace** deploys Cursor/Codex subagent markdown into `.cursor/agents/` and `.codex/agents/` with optional `sync_subagents` and `codex_agent_targets`. (Older docs referred to split template trees; the repo now uses a single canonical tree — see Unreleased.)
- **Token checkpoint protocol** and optional `.braindrain/token-metrics.jsonl` schema `1.0` are documented in templates and README for consistent observability.

### For contributors
Expand Down
219 changes: 145 additions & 74 deletions README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions TRADEMARKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This policy applies to the project name **Braindrain**, any official project log

### Code license vs. brand usage

The source code is licensed under `AGPL-3.0` in [`LICENSE`](LICENSE).
The source code is licensed under `AGPL-3.0` in `[LICENSE](LICENSE)`.
That license governs code use, modification, and distribution.
This policy is only about avoiding confusion around the official project identity.

Expand All @@ -29,4 +29,4 @@ If you ship a fork or service, please use distinct branding unless you have expl

### Permission requests

If you want to use the Braindrain name or branding in a way that may look official, open an issue in this repository with the intended use and context.
If you want to use the Braindrain name or branding in a way that may look official, open an issue in this repository with the intended use and context.
1 change: 1 addition & 0 deletions braindrain/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ def _parse_config(self, raw: dict) -> ConfigData:
lessons=raw.get("lessons", {}),
dreaming=raw.get("dreaming", {}),
provider_context=raw.get("provider_context", {}),
provenance=raw.get("provenance", {}),
)

def reload(self) -> None:
Expand Down
21 changes: 17 additions & 4 deletions braindrain/env_probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,10 @@

# ---------------------------------------------------------------------------
# IDE / Agent MCP config locations
# Checked by path existence; content parsed if JSON/JSONC.
# Checked by path existence; content parsed if JSON/JSONC, except Codex CLI
# which is TOML-first at ~/.codex/config.toml.
# Format: (app_key, display_name, config_path, mcp_key_path)
# mcp_key_path: dot-separated path into the JSON to find server map
# mcp_key_path: dot-separated path into the config to find server map
# e.g. "mcpServers" or "mcp" or "context_servers"
# ---------------------------------------------------------------------------

Expand Down Expand Up @@ -374,7 +375,7 @@
"~/.config/claude/claude_desktop_config.json",
"mcpServers",
),
("codex_cli", "Codex CLI", "~/.codex/config.json", "mcpServers"),
("codex_cli", "Codex CLI", "~/.codex/config.toml", "mcp_servers"),
("codex_openai", "Codex (OpenAI)", "~/.openai/mcp.json", "mcpServers"),
("continue", "Continue", "~/.continue/config.json", "mcpServers"),
("vscode", "VS Code", "~/.vscode/settings.json", "mcp.servers"),
Expand Down Expand Up @@ -423,6 +424,18 @@ def _read_json_file(path: Path) -> dict | None:
return None


def _read_toml_file(path: Path) -> dict | None:
"""Read a TOML file, return parsed dict or None."""
try:
import tomllib # Python 3.11+ stdlib

raw = path.read_text(encoding="utf-8", errors="ignore")
parsed = tomllib.loads(raw)
return parsed if isinstance(parsed, dict) else None
except Exception:
return None


def _get_nested(d: dict, dot_path: str) -> Any:
"""Walk a dot-separated key path into a nested dict."""
parts = dot_path.split(".")
Expand Down Expand Up @@ -456,7 +469,7 @@ def probe_app_configs() -> dict[str, Any]:
}
continue

parsed = _read_json_file(path)
parsed = _read_toml_file(path) if app_key == "codex_cli" else _read_json_file(path)
mcp_block = _get_nested(parsed, mcp_key) if parsed else None

server_names: list[str] = []
Expand Down
Loading