Metadata-Version: 2.4
Name: foam-wiki
Version: 0.5.0
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Rust
Classifier: Typing :: Typed
Requires-Dist: pyyaml>=6
Requires-Dist: pathspec>=0.12
Requires-Dist: json5>=0.9
Requires-Dist: rapidfuzz>=3,<4
Requires-Dist: tomli>=2 ; python_full_version < '3.11'
Requires-Dist: typed-agent-hooks>=0.1.1,<0.2 ; extra == 'agent-hooks'
Requires-Dist: filelock>=3.16,<4 ; extra == 'agent-hooks'
Requires-Dist: platformdirs>=4,<5 ; extra == 'agent-hooks'
Requires-Dist: pandas>=2 ; extra == 'all'
Requires-Dist: networkx>=3 ; extra == 'all'
Requires-Dist: networkx>=3 ; extra == 'graph'
Requires-Dist: pandas>=2 ; extra == 'pandas'
Provides-Extra: agent-hooks
Provides-Extra: all
Provides-Extra: graph
Provides-Extra: pandas
License-File: LICENSE
Summary: A Pythonic, notebook-first library + CLI for Foam-style markdown wikis.
Keywords: foam,wikilink,markdown,knowledge-base,notebook
Author-email: Nima Shoghi <nimashoghi@gmail.com>
License-Expression: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://github.com/nimashoghi/foampy
Project-URL: Issues, https://github.com/nimashoghi/foampy/issues
Project-URL: Repository, https://github.com/nimashoghi/foampy

# foamwiki

A Rust-backed, notebook-first Python library and thin CLI for Foam-style
Markdown wikis (folders of notes connected by `[[wikilinks]]`).

The native core reads and parses notes in parallel, maintains the identity and
resolution indexes, and stores the link graph. The Python layer preserves the
convenient data-oriented API, PyYAML frontmatter values, templates, and
filesystem mutation receipts.

## Installation

The distribution is named `foam-wiki`; the Python package and command are both
named `foamwiki`.

Install the stable release:

```bash
uv add foam-wiki
# or: python -m pip install foam-wiki
```

Until the first stable release, opt into the published prerelease:

```bash
uv add --prerelease allow foam-wiki
# or: python -m pip install --pre foam-wiki
```

Run the CLI without adding a project dependency:

```bash
uvx --from foam-wiki foamwiki --help
```

To install the current branch directly from Git:

```text
foam-wiki @ git+https://github.com/nimashoghi/foampy.git
```

Git installations build the extension from source and require Rust 1.88 or
newer. CI produces CPython stable-ABI wheels for manylinux2014 x86_64 and
AArch64 (glibc-based Linux), macOS x86_64 and Apple silicon, and Windows x86_64.
Each wheel supports Python 3.10 and newer.

## Core API

```python
import foamwiki

ws = foamwiki.load()                 # walk up to .foam/ ; parse + index once
note = ws["mean-flow"]             # lookup by shortest id or path
note.title, note.tags, note.outline
note.backlinks()                   # who links here

ws.resolve("flow-matching")        # -> the target Note (falsy Unresolved if broken)
hits = ws.search("TM-align")       # compact immutable Sequence[Hit]
hits[:20]                          # materialize only the rows being inspected
hits.materialize()                 # explicit eager Rows[Hit], when required
foamwiki.orphans(ws)                 # whole-graph census (free functions)
foamwiki.check(ws)                   # strict link diagnostics
foamwiki.rename(ws, note, "papers/meanflow.md")   # dry-run preview; pass dry_run=False to apply
```

Resolution validates `#heading` and `#^block` fragments. Ambiguous attachment
suffixes return a falsy `Unresolved(reason="ambiguous")` rather than silently
choosing one file. Percent-encode reserved characters in attachment paths, for
example `![[fig%23draft%5D.png]]` for `fig#draft].png`; diagnostic suggestions
do this automatically.

### Full paths from the vault root

Set `link_format = "absolute"` in `foamwiki.toml` to use full vault-relative note IDs and links such as `[[users/nima/model]]`, `[[users/nima/model.md#Setup|Configuration]]`, and `![[users/nima/model#^result]]`. Folder-qualified wikilinks resolve only from the vault root in this mode; a missing `users/nima/model` does not fall back to `archive/users/nima/model`. Basename links still resolve when unambiguous, and `fix` expands them to the preferred full path. Directory-index links remain available when `directory_mode` is enabled. Explicit `./` and `../` paths keep their source-relative meaning, and ordinary Markdown links keep their existing path semantics.

This uses [Obsidian's documented folder-path syntax](https://obsidian.md/help/links). In Obsidian, select **Settings > Files and links > New link format > Absolute path in vault** to generate that spelling. Foampy's strict missing-path behavior is an explicit workspace policy; it is not a claim about undocumented Obsidian fallback behavior. Foampy does not read or change Obsidian settings.

The default `link_format = "shortest"` retains global shortest-ID and suffix lookup. In either mode, validation, `fix`, and coordinated note, heading, and block renames preserve intentional full vault-relative links, including an explicitly written note extension. Meaningful display text and embed markers survive renames. Redundant display text is still reported and removable by `fix`.

Workspace-wide operations verify that the effective configuration, indexed
file census, and every indexed note still match the loaded snapshot. Applied
mutations also reject symlink-backed write sources, stage all output before
changing the workspace, and roll back on write or reload failure. A dry run
therefore remains safe to review even while another editor is active:
replanning or applying it fails loudly instead of overwriting newer content.

### Selective indexing below an excluded directory

Exclusions use ordered Gitignore syntax. A rooted negation can re-include a
narrow subtree without disabling pruning for unrelated excluded directories:

```json
{
  "foam.files.exclude": ["!.agents/skills/**/*.md"]
}
```

`foamwiki` descends only through `.agents/skills` for this pattern; excluded
siblings such as `.agents/sessions` remain pruned. Negations without a safe
rooted prefix, such as `!**/skills/**/*.md`, retain conservative full-tree
traversal so their matching behavior remains correct.

## Compact notebook output

Public records use compact, payload-free representations while keeping
identifiers and paths intact. Potentially large text fields are bounded. Search
hits center an 88-character excerpt on the match instead of dumping the complete
source line and nested `Note`/`Pos` records:

```text
<Hit alphafold-2/paper L272:550 '...colaboratory). TM-align v. 20190822 (https://zhanglab.dcmb.med.umich.edu/...'>
```

`Rows`, `SearchResults`, tag indexes, document outlines, and node child menus
display at most 20 entries and report how many remain. Slice them to continue.
Literal `SearchResults` also materialize public hit records only on access;
call `.materialize()` for an eager `Rows[Hit]`. Automatic mutation diff
previews are bounded; call `ChangeSet.diff()` for the complete diff. These
limits affect display only: `Hit.text` and `Match.line` retain the exact source
line (including indentation), while record fields, iteration, and `.to_df()`
retain complete data. `Passage` remains intentionally unbounded because
evaluating `.body` or `.read()` is an explicit request for content.

## Template authoring

Markdown templates can declare `foam_template.filepath` and use Foam variables
in both their content and destination. Preview first, then repeat the reviewed
call with `dry_run=False`:

```python
draft = foamwiki.create(
    ws,
    template="meeting-scratchpad",
    when="2026-07-15T14:30:00+01:00",
    dry_run=True,
)
draft.path, draft.frontmatter, draft.text

note = foamwiki.create(
    ws,
    template="meeting-scratchpad",
    when="2026-07-15T14:30:00+01:00",
    dry_run=False,
)
```

`foamwiki.daily(ws, "2026-07-15", dry_run=True)` uses `daily-note.md` and prefers
its template filepath. Explicit destinations always override template metadata.

## Structure-aware reading (for notebook agents)

Read a *large* markdown file without dumping it into context. `foamwiki.read`
returns a priced, navigable map of the file's sections — orient cheaply, drill
by stable numeric path, search to a section, then read only what you choose:

```python
doc = foamwiki.read("design.md")     # (or note.doc for a vault note — no reparse)
doc                                # repr = a priced table-of-contents (~150 tok for a 40k-tok file)
doc["3.2"]                         # drill by numeric path (or doc["Method"] / doc[3]); prints a menu, never dumps
doc.search("KL")                   # term -> the sections that contain it (Rows[Match])
doc["3.2"].body                    # read just this section's prose (a priced Passage); .read() for the whole subtree
doc["3.2"].children                # tree nav as properties: .parent .children .siblings .next .prev
```

Every view quotes exact line counts + `~`token estimates, so you budget before
you spend. Works standalone on any `.md` file (no workspace needed).

See [`AGENTS.md`](AGENTS.md) for the design philosophy and the placement rule.
pandas and networkx are optional extras used lazily by `.to_df()` and
`to_networkx()`; request the `all` extra in the Git dependency when needed:
`foam-wiki[all] @ git+https://github.com/nimashoghi/foampy.git`.

## Development

```bash
uv sync
uv run pytest -q
uv run ruff check .
uv run basedpyright src scripts
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targets --all-features
```

See [`PERFORMANCE.md`](PERFORMANCE.md) for the real-workspace baseline and
reproducible benchmark command. [`MIGRATION.md`](MIGRATION.md) lists the
intentional behavior and naming changes. [`RELEASING.md`](RELEASING.md)
documents local and CI release validation.

MIT licensed.

## Agent hooks (optional)

Install the `agent-hooks` extra to use the `foamwiki-hooks` console application. It registers project-scoped hooks for Codex and Claude Code through typed-agent-hooks, preserving unrelated settings. In a Foam workspace, run:

```console
foamwiki-hooks install --provider all --root /path/to/wiki
```

Use `--provider codex` or `--provider claude_code` for one host. `foamwiki-hooks uninstall` removes only this application's managed entries. Restart the host and follow its normal project and hook trust flow. The executable path is specific to this installation; reinstall registrations after moving the environment or cloning a wiki onto another machine.

Hooks resolve prompt and tool-output wikilinks, expand explicit transclusions, advise about graph mutations, and report newly relevant graph diagnostics. Discovery uses `.foam/` or `foamwiki.toml` through `foamwiki.discover`; Git is not required. Runtime state lives in the platform user cache, with file locks coordinating hook processes. `FOAMWIKI_HOOK_STATE_ROOT` can override that location for isolated tests.

Validation observes the whole workspace, including opaque notebook, shell, and background writes. Notifications are advisory and scoped to paths explicitly selected through resolved prompt wikilinks or recognized tool path arguments, plus affected inbound links to those paths. Selection establishes relevance, not authorship: two sessions can read or edit the same note. Unrelated changes produce neither diagnostics nor link-conversion advice in that session. New findings observed silently remain available when their notes become relevant, and repairs remove stale findings before delivery. The first snapshot quietly baselines existing diagnostics.

Stop callbacks only update observation state; they never block completion or inject notifications, including on validation failure. Late background findings can be delivered on the next context-capable event when relevant. Opaque code without an explicit selected path does not assign its output files to the session merely because they changed during execution. For an explicit audit, use `foamwiki check`; automatic silence does not certify all task outputs. The package does not provide process-level write attribution.

This extra contains generic Foam behavior only. Source-corpus evidence policies, wiki authoring rules, and Git synchronization belong to the consuming wiki or setup package. The initial generic extraction comes from the author's wiki hooks; executable behavior is covered by `tests/test_agent_hooks.py` using a non-Git vault and both provider schemas.

