poppy-memory 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- poppy_memory-0.2.0/.github/workflows/ci.yml +69 -0
- poppy_memory-0.2.0/.github/workflows/publish.yml +36 -0
- poppy_memory-0.2.0/.gitignore +29 -0
- poppy_memory-0.2.0/CHANGELOG.md +87 -0
- poppy_memory-0.2.0/CLAUDE.md +99 -0
- poppy_memory-0.2.0/CONTRIBUTING.md +12 -0
- poppy_memory-0.2.0/LICENSE +662 -0
- poppy_memory-0.2.0/PKG-INFO +269 -0
- poppy_memory-0.2.0/README.md +238 -0
- poppy_memory-0.2.0/SECURITY.md +17 -0
- poppy_memory-0.2.0/benchmarks/BENCHMARKS.md +72 -0
- poppy_memory-0.2.0/docs/assets/readme-header-dark.svg +11 -0
- poppy_memory-0.2.0/docs/assets/readme-header-light.svg +11 -0
- poppy_memory-0.2.0/install.sh +42 -0
- poppy_memory-0.2.0/mcpb/icon.png +0 -0
- poppy_memory-0.2.0/mcpb/manifest.json +64 -0
- poppy_memory-0.2.0/mcpb/server/main.py +46 -0
- poppy_memory-0.2.0/pyproject.toml +88 -0
- poppy_memory-0.2.0/src/poppy/__init__.py +27 -0
- poppy_memory-0.2.0/src/poppy/build_mcpb.py +91 -0
- poppy_memory-0.2.0/src/poppy/capture/__init__.py +13 -0
- poppy_memory-0.2.0/src/poppy/capture/_state.py +46 -0
- poppy_memory-0.2.0/src/poppy/capture/banner.py +92 -0
- poppy_memory-0.2.0/src/poppy/capture/cadence.py +71 -0
- poppy_memory-0.2.0/src/poppy/capture/journal.py +126 -0
- poppy_memory-0.2.0/src/poppy/capture/lock.py +79 -0
- poppy_memory-0.2.0/src/poppy/capture/policy.py +163 -0
- poppy_memory-0.2.0/src/poppy/capture/reconciler.py +169 -0
- poppy_memory-0.2.0/src/poppy/capture/watermark.py +43 -0
- poppy_memory-0.2.0/src/poppy/capture/window.py +134 -0
- poppy_memory-0.2.0/src/poppy/cli/__init__.py +0 -0
- poppy_memory-0.2.0/src/poppy/cli/hooks.py +753 -0
- poppy_memory-0.2.0/src/poppy/cli/main.py +1539 -0
- poppy_memory-0.2.0/src/poppy/config.py +229 -0
- poppy_memory-0.2.0/src/poppy/conflict_detection.py +210 -0
- poppy_memory-0.2.0/src/poppy/consolidation.py +565 -0
- poppy_memory-0.2.0/src/poppy/db.py +43 -0
- poppy_memory-0.2.0/src/poppy/engine/__init__.py +3 -0
- poppy_memory-0.2.0/src/poppy/engine/_model_cache.py +55 -0
- poppy_memory-0.2.0/src/poppy/engine/_st_loader.py +68 -0
- poppy_memory-0.2.0/src/poppy/engine/bloom.py +588 -0
- poppy_memory-0.2.0/src/poppy/engine/interface.py +60 -0
- poppy_memory-0.2.0/src/poppy/engine/migration.py +273 -0
- poppy_memory-0.2.0/src/poppy/engine/registry.py +102 -0
- poppy_memory-0.2.0/src/poppy/engine/seed.py +321 -0
- poppy_memory-0.2.0/src/poppy/engine/sprout.py +374 -0
- poppy_memory-0.2.0/src/poppy/errors.py +14 -0
- poppy_memory-0.2.0/src/poppy/integrations/claude_memory_import.py +145 -0
- poppy_memory-0.2.0/src/poppy/integrations/hermes_memory_import.py +121 -0
- poppy_memory-0.2.0/src/poppy/lifecycle.py +210 -0
- poppy_memory-0.2.0/src/poppy/mcp_server/__init__.py +0 -0
- poppy_memory-0.2.0/src/poppy/mcp_server/server.py +518 -0
- poppy_memory-0.2.0/src/poppy/models.py +38 -0
- poppy_memory-0.2.0/src/poppy/runtime.py +75 -0
- poppy_memory-0.2.0/src/poppy/setup/__init__.py +0 -0
- poppy_memory-0.2.0/src/poppy/setup/claude_code.py +439 -0
- poppy_memory-0.2.0/src/poppy/setup/goose.py +184 -0
- poppy_memory-0.2.0/src/poppy/setup/hermes.py +558 -0
- poppy_memory-0.2.0/src/poppy/setup/trags.py +142 -0
- poppy_memory-0.2.0/src/poppy/sources.py +86 -0
- poppy_memory-0.2.0/src/poppy/sync/__init__.py +299 -0
- poppy_memory-0.2.0/src/poppy/sync/auto.py +226 -0
- poppy_memory-0.2.0/src/poppy/sync/client.py +143 -0
- poppy_memory-0.2.0/src/poppy/sync/serializer.py +94 -0
- poppy_memory-0.2.0/src/poppy/sync/state.py +76 -0
- poppy_memory-0.2.0/src/poppy/telemetry.py +217 -0
- poppy_memory-0.2.0/src/poppy/ui/__init__.py +6 -0
- poppy_memory-0.2.0/src/poppy/ui/server.py +489 -0
- poppy_memory-0.2.0/src/poppy/ui/static/app.js +944 -0
- poppy_memory-0.2.0/src/poppy/ui/static/fonts/Fraunces-Italic-latin.woff2 +0 -0
- poppy_memory-0.2.0/src/poppy/ui/static/fonts/Fraunces-latin.woff2 +0 -0
- poppy_memory-0.2.0/src/poppy/ui/static/fonts/JetBrainsMono-Italic-latin.woff2 +0 -0
- poppy_memory-0.2.0/src/poppy/ui/static/fonts/JetBrainsMono-latin.woff2 +0 -0
- poppy_memory-0.2.0/src/poppy/ui/static/fonts/OFL-Fraunces.txt +93 -0
- poppy_memory-0.2.0/src/poppy/ui/static/fonts/OFL-JetBrainsMono.txt +93 -0
- poppy_memory-0.2.0/src/poppy/ui/static/index.html +101 -0
- poppy_memory-0.2.0/src/poppy/ui/static/styles.css +809 -0
- poppy_memory-0.2.0/src/poppy/ui/tombstones.py +147 -0
- poppy_memory-0.2.0/tests/capture/__init__.py +0 -0
- poppy_memory-0.2.0/tests/capture/test_banner.py +112 -0
- poppy_memory-0.2.0/tests/capture/test_cadence.py +52 -0
- poppy_memory-0.2.0/tests/capture/test_capture_worker.py +249 -0
- poppy_memory-0.2.0/tests/capture/test_consent_cli.py +149 -0
- poppy_memory-0.2.0/tests/capture/test_lock.py +39 -0
- poppy_memory-0.2.0/tests/capture/test_policy.py +86 -0
- poppy_memory-0.2.0/tests/capture/test_reconciler.py +179 -0
- poppy_memory-0.2.0/tests/capture/test_sessionend_window.py +126 -0
- poppy_memory-0.2.0/tests/capture/test_watermark.py +34 -0
- poppy_memory-0.2.0/tests/capture/test_window.py +106 -0
- poppy_memory-0.2.0/tests/conftest.py +23 -0
- poppy_memory-0.2.0/tests/test_build_mcpb.py +119 -0
- poppy_memory-0.2.0/tests/test_claude_memory_import.py +178 -0
- poppy_memory-0.2.0/tests/test_cli.py +302 -0
- poppy_memory-0.2.0/tests/test_cli_since.py +218 -0
- poppy_memory-0.2.0/tests/test_cli_telemetry.py +173 -0
- poppy_memory-0.2.0/tests/test_config.py +136 -0
- poppy_memory-0.2.0/tests/test_conflict_detection.py +265 -0
- poppy_memory-0.2.0/tests/test_consolidation.py +167 -0
- poppy_memory-0.2.0/tests/test_db.py +41 -0
- poppy_memory-0.2.0/tests/test_engine_bloom.py +248 -0
- poppy_memory-0.2.0/tests/test_engine_migration.py +265 -0
- poppy_memory-0.2.0/tests/test_engine_registry.py +153 -0
- poppy_memory-0.2.0/tests/test_engine_seed.py +141 -0
- poppy_memory-0.2.0/tests/test_engine_sprout.py +156 -0
- poppy_memory-0.2.0/tests/test_engine_st_loader.py +117 -0
- poppy_memory-0.2.0/tests/test_hooks.py +571 -0
- poppy_memory-0.2.0/tests/test_lifecycle.py +274 -0
- poppy_memory-0.2.0/tests/test_mcp_server.py +303 -0
- poppy_memory-0.2.0/tests/test_models.py +69 -0
- poppy_memory-0.2.0/tests/test_setup_claude_code.py +309 -0
- poppy_memory-0.2.0/tests/test_setup_goose.py +171 -0
- poppy_memory-0.2.0/tests/test_setup_hermes.py +185 -0
- poppy_memory-0.2.0/tests/test_setup_trags.py +53 -0
- poppy_memory-0.2.0/tests/test_sources.py +97 -0
- poppy_memory-0.2.0/tests/test_telemetry.py +232 -0
- poppy_memory-0.2.0/tests/test_ui_lifecycle.py +220 -0
- poppy_memory-0.2.0/tests/test_version.py +57 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# push is restricted to main so PR branches run once (via pull_request),
|
|
4
|
+
# not twice (push + pull_request) — PR #3 review follow-up.
|
|
5
|
+
# pull_request filters on the BASE branch, so stacked PRs (base reveal-prep/*)
|
|
6
|
+
# must be listed too or they would get zero CI runs.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
pull_request:
|
|
11
|
+
branches:
|
|
12
|
+
- main
|
|
13
|
+
- "reveal-prep/**"
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
strategy:
|
|
19
|
+
matrix:
|
|
20
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@v4
|
|
27
|
+
|
|
28
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
29
|
+
run: uv python install ${{ matrix.python-version }}
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: uv sync
|
|
33
|
+
|
|
34
|
+
- name: Lint
|
|
35
|
+
run: |
|
|
36
|
+
uv run ruff check src/ tests/
|
|
37
|
+
uv run ruff format --check src/ tests/
|
|
38
|
+
|
|
39
|
+
- name: Test
|
|
40
|
+
run: uv run pytest -v
|
|
41
|
+
|
|
42
|
+
# Build-only release rehearsal on every push. Publishing to PyPI happens
|
|
43
|
+
# exclusively in publish.yml, which triggers only on v* tags.
|
|
44
|
+
build:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
|
|
50
|
+
- name: Set up Python
|
|
51
|
+
uses: actions/setup-python@v5
|
|
52
|
+
with:
|
|
53
|
+
python-version: "3.13"
|
|
54
|
+
|
|
55
|
+
- name: Install build tooling
|
|
56
|
+
run: python -m pip install --upgrade pip build twine
|
|
57
|
+
|
|
58
|
+
- name: Build wheel + sdist
|
|
59
|
+
run: python -m build
|
|
60
|
+
|
|
61
|
+
- name: Verify build metadata
|
|
62
|
+
run: python -m twine check dist/*
|
|
63
|
+
|
|
64
|
+
- name: Upload dist artifacts
|
|
65
|
+
uses: actions/upload-artifact@v4
|
|
66
|
+
with:
|
|
67
|
+
name: dist
|
|
68
|
+
path: dist/
|
|
69
|
+
retention-days: 7
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.13"
|
|
22
|
+
|
|
23
|
+
- name: Install build tooling
|
|
24
|
+
run: python -m pip install --upgrade pip build twine
|
|
25
|
+
|
|
26
|
+
- name: Build sdist + wheel
|
|
27
|
+
run: python -m build
|
|
28
|
+
|
|
29
|
+
- name: Verify build metadata
|
|
30
|
+
run: python -m twine check dist/*
|
|
31
|
+
|
|
32
|
+
- name: Publish to PyPI
|
|
33
|
+
env:
|
|
34
|
+
TWINE_USERNAME: __token__
|
|
35
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
36
|
+
run: python -m twine upload dist/*
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
.uv-cache/
|
|
10
|
+
uv.lock
|
|
11
|
+
|
|
12
|
+
# Editor / IDE
|
|
13
|
+
.vscode/
|
|
14
|
+
.idea/
|
|
15
|
+
*.swp
|
|
16
|
+
|
|
17
|
+
# OS
|
|
18
|
+
.DS_Store
|
|
19
|
+
Thumbs.db
|
|
20
|
+
|
|
21
|
+
# Pytest / coverage
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
htmlcov/
|
|
25
|
+
.tox/
|
|
26
|
+
|
|
27
|
+
# Built .mcpb bundles (the mcpb/ source dir with manifest.json + shim is checked in)
|
|
28
|
+
*.mcpb
|
|
29
|
+
mcpb-build/
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Poppy are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2026-06-14
|
|
9
|
+
|
|
10
|
+
First public release of `poppy-memory`.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Core memory CLI: `remember`, `recall`, `list`, `edit`, `forget`, `expire`,
|
|
15
|
+
`stats`, with types (fact, decision, preference, lesson), projects, TTLs,
|
|
16
|
+
supersede chains, and LLM conflict detection (`--check-conflicts`,
|
|
17
|
+
`--auto-supersede`).
|
|
18
|
+
- Three retrieval engines with a floating default tier: `bloom` (hybrid FTS5 +
|
|
19
|
+
bge-small embeddings + cross-encoder rerank), `sprout` (lighter bi-encoder),
|
|
20
|
+
`seed` (FTS5 only, zero ML deps). Switch with `poppy engines use`, re-embed
|
|
21
|
+
with `poppy migrate-engine`.
|
|
22
|
+
- MCP server (`poppy serve`) plus one-command setup for Claude Code (MCP,
|
|
23
|
+
lifecycle hooks, CLAUDE.md primer), Claude Desktop, Cursor, Windsurf, Codex,
|
|
24
|
+
Copilot CLI, Pi, Goose, and Hermes Agent. `poppy doctor` verifies the whole
|
|
25
|
+
installation.
|
|
26
|
+
- Claude Desktop Extension bundle builder (`poppy build mcpb`); the build
|
|
27
|
+
stamps the pyproject version into the bundle manifest.
|
|
28
|
+
- Optional Trags cloud sync: device-code onboarding (`poppy setup trags`),
|
|
29
|
+
`poppy sync push|pull|run|status`, and auto-sync after local writes.
|
|
30
|
+
- Imports from existing stores: `poppy import claude-memories` and
|
|
31
|
+
`poppy import hermes-memories`.
|
|
32
|
+
- Local web UI (`poppy ui`) for browsing, editing, and restoring memories.
|
|
33
|
+
- `--since` filter on `recall`/`list` accepting ISO dates or durations such as
|
|
34
|
+
`7d` and `1w3d`.
|
|
35
|
+
- Telemetry disclosure and first-class opt-out: `poppy telemetry
|
|
36
|
+
status|on|off` persisting to `~/.poppy/config.json`, a one-time first-run
|
|
37
|
+
notice on stderr, and a README section listing every event and property
|
|
38
|
+
sent. `POPPY_TELEMETRY_OFF=1` always wins.
|
|
39
|
+
- `poppy --version`, single-sourced from `pyproject.toml`.
|
|
40
|
+
- Zero-touch capture: consent-gated automatic memory extraction from coding
|
|
41
|
+
sessions. Mid-session captures fire every Nth prompt under a single-flight
|
|
42
|
+
lock with a per-session soft cap; a SessionEnd backstop and PostCompact
|
|
43
|
+
re-extraction cover whatever the cadence missed. An incremental transcript
|
|
44
|
+
window (watermark-based, compaction-safe) feeds extraction, and a
|
|
45
|
+
dedup-on-capture reconciler decides ADD / SUPERSEDE / SKIP before anything
|
|
46
|
+
is written. Extraction runs locally through the user's own host CLI
|
|
47
|
+
(claude/codex/gemini); a remote-only backend is never auto-spent.
|
|
48
|
+
- `poppy consent --enable/--disable/--status` plus a one-time consent prompt
|
|
49
|
+
in `poppy setup claude-code` (and a `--yes` flag): nothing is captured
|
|
50
|
+
until consent is granted, and an opt-out persists.
|
|
51
|
+
- SessionStart status banner (active / inactive / consent pending) and
|
|
52
|
+
expanded `poppy doctor` capture reporting: granular consent and backend
|
|
53
|
+
status, last-capture freshness, journal count, and watermark/lock state.
|
|
54
|
+
- Source-app provenance on MCP writes: memories record the real client app
|
|
55
|
+
(for example `claude-code` or `cursor`) from `poppy serve --source` or the
|
|
56
|
+
connecting client's `clientInfo`, never the bare transport string `mcp`.
|
|
57
|
+
- Legacy engine names in an existing `config.json` keep working:
|
|
58
|
+
`speaker_closet` and `best` map silently to `bloom`, `baseline` to `seed`.
|
|
59
|
+
- `since` filter on the MCP `recall` tool, accepting the same values as the
|
|
60
|
+
CLI `--since` (ISO date or a duration such as `7d`).
|
|
61
|
+
- Offline-safe retrieval model loading: already-downloaded models load with
|
|
62
|
+
`local_files_only`, so recall keeps working with the network down; a cold
|
|
63
|
+
cache with no network exits with a one-line actionable error instead of a
|
|
64
|
+
HuggingFace traceback; a one-time stderr notice announces the first-run
|
|
65
|
+
model download.
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- `--since` on `recall`/`list` was accepted but silently ignored; it now
|
|
70
|
+
filters and rejects invalid values with a usage error.
|
|
71
|
+
- `recall`/`list` no longer print the misleading "No memories found." when
|
|
72
|
+
memories exist but active `--since`/`--project`/`--type` filters excluded
|
|
73
|
+
them; they say the filters matched nothing instead.
|
|
74
|
+
- Recall telemetry reported a stale hardcoded engine name instead of the
|
|
75
|
+
engine that actually served the query.
|
|
76
|
+
- The `memory_write` telemetry event sent the raw project name; it now sends
|
|
77
|
+
only a `has_project` boolean.
|
|
78
|
+
|
|
79
|
+
### Security
|
|
80
|
+
|
|
81
|
+
- License set to AGPL-3.0-or-later.
|
|
82
|
+
- `~/.poppy/config.json` (holds API keys) is written 0600 inside a 0700
|
|
83
|
+
directory, atomically, so secrets never land world-readable.
|
|
84
|
+
- The `poppy setup trags` device-code flow encrypts the returned API key to an
|
|
85
|
+
ephemeral RSA keypair, so the key never crosses the wire in plaintext.
|
|
86
|
+
- Runtime SQLite connections enable WAL and a busy timeout, preventing
|
|
87
|
+
lock-related corruption under concurrent hook/CLI access.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
**Poppy** is a developer memory CLI and MCP server. "Remember what matters."
|
|
8
|
+
|
|
9
|
+
Agents store and recall memories via MCP tools; humans browse via the CLI and the
|
|
10
|
+
local web UI. Data lives locally at `~/.poppy/` (SQLite + FTS5 + embeddings).
|
|
11
|
+
This is the publish repo for the `poppy-memory` package; experimental engine work
|
|
12
|
+
lives elsewhere and is promoted here when it wins.
|
|
13
|
+
|
|
14
|
+
## Tech Stack
|
|
15
|
+
|
|
16
|
+
- **Language**: Python 3.11+
|
|
17
|
+
- **Package manager**: uv
|
|
18
|
+
- **CLI**: click
|
|
19
|
+
- **MCP server**: mcp SDK (FastMCP)
|
|
20
|
+
- **Storage**: SQLite + FTS5; embedding engines use sentence-transformers
|
|
21
|
+
- **Testing**: pytest + pytest-asyncio
|
|
22
|
+
|
|
23
|
+
## Development
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uv sync # install dependencies
|
|
27
|
+
uv run pytest -v # run tests
|
|
28
|
+
uv run ruff check src/ tests/
|
|
29
|
+
uv run ruff format src/ tests/
|
|
30
|
+
uv run poppy --help # CLI help
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Use the `POPPY_DIR` env var to avoid touching `~/.poppy/` during development.
|
|
34
|
+
The test suite isolates this automatically (see `tests/conftest.py`).
|
|
35
|
+
|
|
36
|
+
## Architecture
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
src/poppy/
|
|
40
|
+
├── models.py # Memory, Source, Filters dataclasses
|
|
41
|
+
├── config.py # Config load/save, consent tri-state, legacy migration
|
|
42
|
+
├── sources.py # Source-app provenance vocabulary
|
|
43
|
+
├── engine/
|
|
44
|
+
│ ├── interface.py # RetrievalEngine ABC (the stable boundary)
|
|
45
|
+
│ ├── registry.py # Engine catalog + legacy-name fallback mapping
|
|
46
|
+
│ ├── bloom.py # Default: hybrid FTS5+embeddings, cross-encoder rerank
|
|
47
|
+
│ ├── sprout.py # Mid tier: lighter bi-encoder, same architecture
|
|
48
|
+
│ └── seed.py # FTS5 only, no ML deps (universal fallback)
|
|
49
|
+
├── capture/ # Zero-touch capture stack (see below)
|
|
50
|
+
├── consolidation.py # LLM extraction: backstop, mid-session, post-compact
|
|
51
|
+
├── cli/
|
|
52
|
+
│ ├── main.py # click CLI commands (incl. consent, doctor)
|
|
53
|
+
│ └── hooks.py # Claude Code hook entrypoints + detached workers
|
|
54
|
+
├── mcp_server/ # FastMCP server + tool handlers
|
|
55
|
+
├── setup/ # Per-client install (claude-code, cursor, goose, ...)
|
|
56
|
+
├── sync/ # Optional Trags cloud sync
|
|
57
|
+
└── ui/ # Local web UI
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Zero-touch capture (`src/poppy/capture/`)
|
|
61
|
+
|
|
62
|
+
Automatic memory extraction from coding sessions. Consent-gated: enabled by
|
|
63
|
+
default but inert until `poppy consent --enable` (ADR-0002).
|
|
64
|
+
|
|
65
|
+
| Module | Role |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `policy.py` | ConsolidationPolicy: consent tri-state + backend detection. Single source of truth for "is capture on, and if not, why". |
|
|
68
|
+
| `_state.py` / `watermark.py` | Per-session JSON state; the watermark marks the last captured turn so no turn is extracted twice. Reset at SessionStart. |
|
|
69
|
+
| `cadence.py` | Fire every Nth user turn; per-session soft cap defers the rest to the SessionEnd backstop. |
|
|
70
|
+
| `lock.py` | Single-flight per-session lock; stale locks (crashed workers) are stolen after 5 minutes. |
|
|
71
|
+
| `window.py` | Incremental transcript reader for `(watermark, now]`; survives compaction by clamping. |
|
|
72
|
+
| `reconciler.py` | Dedup-on-capture before ingest: ADD / SUPERSEDE / SKIP, three tiers (lexical prefilter, LLM verdict, bias to ADD). |
|
|
73
|
+
| `journal.py` | Local JSONL record of what each capture stored. Never synced. |
|
|
74
|
+
| `banner.py` | SessionStart status banner (active / INACTIVE / consent pending). |
|
|
75
|
+
|
|
76
|
+
Flow: `UserPromptSubmit` hook counts turns and, on cadence, spawns a detached
|
|
77
|
+
`poppy hook _capture-worker` which runs `consolidation.consolidate_capture_event`
|
|
78
|
+
(lock, window, gates, LLM extract, reconcile, watermark advance, journal).
|
|
79
|
+
`SessionEnd` and `PostCompact` run the same extraction as backstops. Extraction
|
|
80
|
+
uses the host CLI (`claude` / `codex` / `gemini`) so it is free and local; a
|
|
81
|
+
remote-only backend is never auto-spent.
|
|
82
|
+
|
|
83
|
+
Capture state files (`capture_state.json`, `capture_journal.jsonl`,
|
|
84
|
+
`capture-*.lock`) are per-device by design and excluded from sync.
|
|
85
|
+
|
|
86
|
+
### Key boundary: RetrievalEngine ABC
|
|
87
|
+
|
|
88
|
+
Everything above `RetrievalEngine` is stable. New engines implement the ABC and
|
|
89
|
+
register in `engine/registry.py`. `interface.py` does not change without
|
|
90
|
+
discussion.
|
|
91
|
+
|
|
92
|
+
## Conventions
|
|
93
|
+
|
|
94
|
+
- Python 3.11+, formatted with ruff (line-length 120)
|
|
95
|
+
- Tests with pytest; async tests with pytest-asyncio
|
|
96
|
+
- Commits: conventional commits (`feat:`, `fix:`, `docs:`, `chore:`, `ci:`, `style:`)
|
|
97
|
+
- User-facing copy: no em dashes
|
|
98
|
+
- Releases are tag-driven (`v*` runs publish.yml); CI builds the wheel on every
|
|
99
|
+
push but never publishes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Poppy's source is published openly under the GNU Affero General Public License v3
|
|
4
|
+
(see [LICENSE](LICENSE)). You are free to read, run, self-host, fork, and modify
|
|
5
|
+
it under those terms.
|
|
6
|
+
|
|
7
|
+
We do not accept external pull requests. Poppy is developed in-house, and keeping
|
|
8
|
+
a single copyright holder lets us relicense or dual-license cleanly as the project
|
|
9
|
+
evolves. Bug reports are welcome via issues; code contributions will be politely
|
|
10
|
+
declined.
|
|
11
|
+
|
|
12
|
+
If you build something on top of Poppy, we would love to hear about it.
|