From 4d268a6e97b2d8b8aa6e3f8bf5d75557f2acf908 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Apr 2026 08:09:13 +0000 Subject: [PATCH 1/3] feat: ship CLI via pip/pipx/Homebrew like Entire ships MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename lib/ → reflect_cli/ as a proper Python package. Add pyproject.toml with console_scripts entry point so `pip install reflect-cli` and `pipx install reflect-cli` work out of the box. Distribution channels: - PyPI: pip/pipx install reflect-cli - Homebrew: brew tap codeyogi911/tap && brew install reflect-cli - Curl installer: curl -fsSL .../install.sh | bash (auto-detects best method) Adds publish.yml workflow (triggers on git tags) for PyPI trusted publishing, GitHub releases, and Homebrew tap updates. Includes Homebrew formula template. https://claude.ai/code/session_015u199PXPEv5cyD8UjcRQkd --- .github/workflows/publish.yml | 54 +++++++++++++++ CLAUDE.md | 16 +++-- README.md | 27 ++++++-- homebrew/reflect-cli.rb | 19 ++++++ install.sh | 89 ++++++++++++++++++------- pyproject.toml | 38 +++++++++++ reflect | 18 ++--- {lib => reflect_cli}/__init__.py | 0 reflect_cli/__main__.py | 6 ++ {lib => reflect_cli}/aggregates.py | 0 reflect_cli/cli.py | 103 +++++++++++++++++++++++++++++ {lib => reflect_cli}/context.py | 2 +- {lib => reflect_cli}/evidence.py | 2 +- {lib => reflect_cli}/improve.py | 4 +- {lib => reflect_cli}/init.py | 0 {lib => reflect_cli}/metrics.py | 0 {lib => reflect_cli}/search.py | 0 {lib => reflect_cli}/sessions.py | 0 {lib => reflect_cli}/sources.py | 0 {lib => reflect_cli}/status.py | 0 {lib => reflect_cli}/synthesize.py | 0 {lib => reflect_cli}/timeline.py | 0 {lib => reflect_cli}/why.py | 0 scripts/smoke.sh | 2 +- 24 files changed, 327 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/publish.yml create mode 100644 homebrew/reflect-cli.rb create mode 100644 pyproject.toml rename {lib => reflect_cli}/__init__.py (100%) create mode 100644 reflect_cli/__main__.py rename {lib => reflect_cli}/aggregates.py (100%) create mode 100644 reflect_cli/cli.py rename {lib => reflect_cli}/context.py (99%) rename {lib => reflect_cli}/evidence.py (99%) rename {lib => reflect_cli}/improve.py (98%) rename {lib => reflect_cli}/init.py (100%) rename {lib => reflect_cli}/metrics.py (100%) rename {lib => reflect_cli}/search.py (100%) rename {lib => reflect_cli}/sessions.py (100%) rename {lib => reflect_cli}/sources.py (100%) rename {lib => reflect_cli}/status.py (100%) rename {lib => reflect_cli}/synthesize.py (100%) rename {lib => reflect_cli}/timeline.py (100%) rename {lib => reflect_cli}/why.py (100%) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..0cea126 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,54 @@ +name: Publish + +on: + push: + tags: ["v*"] + +permissions: + contents: write # GitHub release + id-token: write # PyPI trusted publishing + +jobs: + pypi: + runs-on: ubuntu-latest + environment: release + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # setuptools-scm needs full history + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Build + run: | + pip install build + python -m build + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + github-release: + needs: pypi + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Create GitHub release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release create "${{ github.ref_name }}" --generate-notes + + homebrew: + needs: pypi + runs-on: ubuntu-latest + steps: + - name: Update Homebrew formula + uses: mislav/bump-homebrew-formula-action@v3 + with: + formula-name: reflect-cli + homebrew-tap: codeyogi911/homebrew-tap + download-url: https://github.com/codeyogi911/reflect/archive/refs/tags/${{ github.ref_name }}.tar.gz + env: + COMMITTER_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} diff --git a/CLAUDE.md b/CLAUDE.md index 6acfe9b..4733fd9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,22 +8,24 @@ Repo-owned memory for AI coding agents. Reads raw evidence from Entire CLI sessi ## Structure -- `reflect` — CLI entry point (Python) -- `lib/` — CLI modules (evidence, context, init, why, search, status, improve) -- `lib/evidence.py` — fixed evidence gathering pipeline (Entire CLI + git) +- `reflect` — CLI entry point (legacy dev symlink, kept for backwards compat) +- `reflect_cli/` — Python package (evidence, context, init, why, search, status, improve) +- `reflect_cli/cli.py` — main entry point (used by pyproject.toml console_scripts) +- `reflect_cli/evidence.py` — fixed evidence gathering pipeline (Entire CLI + git) +- `pyproject.toml` — package config for pip/pipx/Homebrew distribution - `skill/SKILL.md` — skill source (dev copy; install copies to `.claude/skills/reflect/`) - `SPEC.md` — specification for `.reflect/` directory format - `hooks/session-start.sh` — SessionStart hook for context freshness (also linked from the skill dir) -- `install.sh` — installer (symlinks CLI to `~/.local/bin`) +- `install.sh` — curl-friendly installer (auto-detects pipx/pip/git) - `README.md` — user-facing docs - `ROADMAP.md` — future phases - `CLAUDE.md` — this file ## Development -- Edit `lib/evidence.py` to change evidence gathering -- Edit `lib/context.py` to change synthesis pipeline, system prompt, or validation -- Edit `lib/` to change CLI commands +- Edit `reflect_cli/evidence.py` to change evidence gathering +- Edit `reflect_cli/context.py` to change synthesis pipeline, system prompt, or validation +- Edit `reflect_cli/` to change CLI commands - Edit `.reflect/format.yaml` (in any repo) to customize context sections - Edit `skill/SKILL.md` to change the Claude Code skill (source of truth) - Test locally: `python3 reflect context` or `python3 reflect why ` diff --git a/README.md b/README.md index 7a814ca..e285113 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,25 @@ --- -## Quick Start +## Install ```bash -# Install reflect -git clone https://github.com/codeyogi911/reflect.git -cd reflect && ./install.sh +# Recommended: pipx (isolated install, like `go install`) +pipx install reflect-cli + +# Or: pip +pip install reflect-cli + +# Or: Homebrew +brew tap codeyogi911/tap && brew install reflect-cli + +# Or: curl installer (auto-detects best method) +curl -fsSL https://raw.githubusercontent.com/codeyogi911/reflect/main/install.sh | bash +``` + +## Quick Start +```bash # Set up any repo cd ~/your-project reflect init # installs Entire CLI, creates .reflect/, wires into CLAUDE.md @@ -180,9 +192,10 @@ Claude's memory lives in `~/.claude/projects/` on your laptop — it doesn't tra ## Contributing 1. Fork the repo -2. Edit `lib/` — changes take effect immediately via symlinks -3. Test: `python3 reflect context` or `python3 reflect why ` -4. Submit a PR +2. `pip install -e .` for an editable install +3. Edit `reflect_cli/` — changes take effect immediately +4. Test: `reflect context` or `reflect why ` +5. Submit a PR ## License diff --git a/homebrew/reflect-cli.rb b/homebrew/reflect-cli.rb new file mode 100644 index 0000000..dbe997a --- /dev/null +++ b/homebrew/reflect-cli.rb @@ -0,0 +1,19 @@ +class ReflectCli < Formula + include Language::Python::Virtualenv + + desc "Repo-owned memory for AI coding agents" + homepage "https://github.com/codeyogi911/reflect" + url "https://github.com/codeyogi911/reflect/archive/refs/tags/v0.1.0.tar.gz" + # sha256 will be filled automatically by the tap update workflow + license "MIT" + + depends_on "python@3.12" + + def install + virtualenv_install_with_resources + end + + test do + assert_match "Repo-owned memory", shell_output("#{bin}/reflect --help") + end +end diff --git a/install.sh b/install.sh index 911ba36..1d97666 100755 --- a/install.sh +++ b/install.sh @@ -1,35 +1,74 @@ #!/usr/bin/env bash +# reflect installer — curl -fsSL https://raw.githubusercontent.com/codeyogi911/reflect/main/install.sh | bash set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO="codeyogi911/reflect" +INSTALL_DIR="${HOME}/.local/bin" -# ── CLI: symlink to ~/.local/bin ────────────────────────────────────── -BIN_DIR="${HOME}/.local/bin" -mkdir -p "$BIN_DIR" -ln -sf "$SCRIPT_DIR/reflect" "$BIN_DIR/reflect" -echo "CLI installed: $BIN_DIR/reflect" +# ── Helpers ───────────────────────────────────────────────────────── +info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } +warn() { printf '\033[1;33mWarning:\033[0m %s\n' "$*"; } +err() { printf '\033[1;31mError:\033[0m %s\n' "$*" >&2; exit 1; } -# ── Skill: install into target repo ────────────────────────────────── -# If run from within a git repo, install the skill there. -# Otherwise install into the reflect repo itself. -TARGET_REPO="${1:-$(git rev-parse --show-toplevel 2>/dev/null || echo "$SCRIPT_DIR")}" +command_exists() { command -v "$1" >/dev/null 2>&1; } -SKILL_SRC="$SCRIPT_DIR/skill/SKILL.md" -SKILL_DST="$TARGET_REPO/.claude/skills/reflect" +# ── Detect install method ────────────────────────────────────────── +install_via_pipx() { + info "Installing via pipx..." + pipx install reflect-cli +} -mkdir -p "$SKILL_DST" -cp "$SKILL_SRC" "$SKILL_DST/SKILL.md" +install_via_pip() { + info "Installing via pip..." + pip install --user reflect-cli +} -# Copy hooks if they exist -HOOKS_DIR="$SCRIPT_DIR/hooks" -if [ -d "$HOOKS_DIR" ]; then - rm -rf "$SKILL_DST/hooks" - cp -R "$HOOKS_DIR" "$SKILL_DST/hooks" -fi +install_via_source() { + info "Installing from source..." + local clone_dir="${HOME}/.local/share/reflect" -echo "Skill installed: $SKILL_DST/SKILL.md" + if [ -d "$clone_dir" ]; then + info "Updating existing clone..." + git -C "$clone_dir" pull --ff-only origin main + else + git clone "https://github.com/${REPO}.git" "$clone_dir" + fi -# ── Summary ────────────────────────────────────────────────────────── -echo "" -echo "Make sure $BIN_DIR is on your PATH." -echo "Run 'reflect init' in any git repo to get started." + mkdir -p "$INSTALL_DIR" + cd "$clone_dir" + pip install --user -e . 2>/dev/null || { + # Fallback: symlink for systems without pip + ln -sf "$clone_dir/reflect" "$INSTALL_DIR/reflect" + info "Installed via symlink (pip unavailable)" + } +} + +# ── Main ─────────────────────────────────────────────────────────── +main() { + info "Installing reflect..." + + if command_exists pipx; then + install_via_pipx + elif command_exists pip; then + install_via_pip + elif command_exists git; then + install_via_source + else + err "No package manager found. Install pipx, pip, or git and try again." + fi + + # Verify installation + if command_exists reflect; then + info "reflect installed successfully!" + reflect --help | head -3 + else + warn "reflect was installed but is not on your PATH." + echo "" + echo "Add this to your shell profile:" + echo ' export PATH="$HOME/.local/bin:$PATH"' + echo "" + echo "Then restart your shell and run: reflect --help" + fi +} + +main "$@" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..65d6dbf --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,38 @@ +[build-system] +requires = ["setuptools>=68.0", "setuptools-scm>=8.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "reflect-cli" +dynamic = ["version"] +description = "Repo-owned memory for AI coding agents" +readme = "README.md" +license = "MIT" +requires-python = ">=3.11" +authors = [{ name = "codeyogi911" }] +keywords = ["ai", "agents", "memory", "context", "cli"] +classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Software Development :: Version Control :: Git", +] + +[project.urls] +Homepage = "https://github.com/codeyogi911/reflect" +Repository = "https://github.com/codeyogi911/reflect" +Issues = "https://github.com/codeyogi911/reflect/issues" + +[project.scripts] +reflect = "reflect_cli.cli:main" + +[tool.setuptools.packages.find] +include = ["reflect_cli*"] + +[tool.setuptools_scm] +# Version derived from git tags (e.g. v0.1.0) +fallback_version = "0.0.0" diff --git a/reflect b/reflect index f663f8a..ba19f1c 100755 --- a/reflect +++ b/reflect @@ -13,15 +13,15 @@ import os # Use realpath to resolve symlinks (install.sh symlinks into ~/.local/bin) sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) -from lib.init import cmd_init -from lib.context import cmd_context -from lib.why import cmd_why -from lib.search import cmd_search -from lib.status import cmd_status -from lib.improve import cmd_improve -from lib.sessions import cmd_sessions -from lib.timeline import cmd_timeline -from lib.metrics import cmd_metrics +from reflect_cli.init import cmd_init +from reflect_cli.context import cmd_context +from reflect_cli.why import cmd_why +from reflect_cli.search import cmd_search +from reflect_cli.status import cmd_status +from reflect_cli.improve import cmd_improve +from reflect_cli.sessions import cmd_sessions +from reflect_cli.timeline import cmd_timeline +from reflect_cli.metrics import cmd_metrics def main(): diff --git a/lib/__init__.py b/reflect_cli/__init__.py similarity index 100% rename from lib/__init__.py rename to reflect_cli/__init__.py diff --git a/reflect_cli/__main__.py b/reflect_cli/__main__.py new file mode 100644 index 0000000..0605340 --- /dev/null +++ b/reflect_cli/__main__.py @@ -0,0 +1,6 @@ +"""Allow running as: python -m reflect_cli""" + +import sys +from reflect_cli.cli import main + +sys.exit(main() or 0) diff --git a/lib/aggregates.py b/reflect_cli/aggregates.py similarity index 100% rename from lib/aggregates.py rename to reflect_cli/aggregates.py diff --git a/reflect_cli/cli.py b/reflect_cli/cli.py new file mode 100644 index 0000000..361e4e5 --- /dev/null +++ b/reflect_cli/cli.py @@ -0,0 +1,103 @@ +"""reflect CLI entry point for installed package.""" + +import argparse +import sys + +from reflect_cli.init import cmd_init +from reflect_cli.context import cmd_context +from reflect_cli.why import cmd_why +from reflect_cli.search import cmd_search +from reflect_cli.status import cmd_status +from reflect_cli.improve import cmd_improve +from reflect_cli.sessions import cmd_sessions +from reflect_cli.timeline import cmd_timeline +from reflect_cli.metrics import cmd_metrics + + +def main(): + parser = argparse.ArgumentParser( + prog="reflect", + description="Repo-owned memory for AI coding agents.", + ) + subparsers = parser.add_subparsers(dest="command") + + # reflect init + init_parser = subparsers.add_parser("init", help="Initialize .reflect/ with default format") + init_parser.add_argument("--migrate", action="store_true", help="Migrate from legacy harness to format.yaml") + + # reflect context + ctx = subparsers.add_parser("context", help="Generate context.md via subagent synthesis") + ctx.add_argument("--max-lines", type=int, default=None, help="Line budget override") + + # reflect why + why = subparsers.add_parser("why", help="Answer questions about project history") + why.add_argument("query", nargs="+", help="File path or topic to search for") + why.add_argument("--raw", action="store_true", help="Dump raw evidence without synthesis") + why.add_argument("--verbose", "-v", action="store_true", help="Show maker-checker progress and raw evidence") + + # reflect search + search = subparsers.add_parser("search", help="Grep across all evidence sources") + search.add_argument("query", nargs="+", help="Search query") + + # reflect status + subparsers.add_parser("status", help="Show evidence source availability") + + # reflect sessions + sess = subparsers.add_parser("sessions", help="List and inspect Entire CLI sessions") + sess.add_argument("session_id", nargs="?", default=None, help="Session ID for detail view") + sess.add_argument("--limit", type=int, default=15, help="Number of sessions to show (default: 15)") + + # reflect timeline + tl = subparsers.add_parser("timeline", help="Date-grouped view of sessions and checkpoints") + tl.add_argument("--days", type=int, default=7, help="Number of days to show (default: 7)") + tl.add_argument("--json", action="store_true", help="Output as JSON") + + # reflect improve + subparsers.add_parser("improve", help="Analyze context quality, suggest format.yaml changes") + + # reflect metrics + met = subparsers.add_parser( + "metrics", + help="Print metrics JSON and/or export shields.io badge endpoint files", + ) + met.add_argument( + "--export", + metavar="DIR", + dest="export_dir", + default=None, + help="Write shields endpoint JSON files into DIR", + ) + met.add_argument( + "--no-json", + action="store_true", + help="Do not print JSON to stdout (use with --export)", + ) + met.add_argument( + "--generate-summaries", + action="store_true", + help="Allow Entire to generate missing summaries (slow; default off)", + ) + + args = parser.parse_args() + + if args.command is None: + parser.print_help() + return 0 + + commands = { + "init": cmd_init, + "context": cmd_context, + "why": cmd_why, + "search": cmd_search, + "status": cmd_status, + "sessions": cmd_sessions, + "timeline": cmd_timeline, + "improve": cmd_improve, + "metrics": cmd_metrics, + } + + return commands[args.command](args) + + +if __name__ == "__main__": + sys.exit(main() or 0) diff --git a/lib/context.py b/reflect_cli/context.py similarity index 99% rename from lib/context.py rename to reflect_cli/context.py index 3743578..9978d9f 100644 --- a/lib/context.py +++ b/reflect_cli/context.py @@ -13,7 +13,7 @@ from datetime import datetime from pathlib import Path -from lib.evidence import gather_evidence, build_evidence_document, truncate_evidence +from reflect_cli.evidence import gather_evidence, build_evidence_document, truncate_evidence # --------------------------------------------------------------------------- diff --git a/lib/evidence.py b/reflect_cli/evidence.py similarity index 99% rename from lib/evidence.py rename to reflect_cli/evidence.py index e953517..d0b29ea 100644 --- a/lib/evidence.py +++ b/reflect_cli/evidence.py @@ -11,7 +11,7 @@ from collections import defaultdict from pathlib import Path -from lib.sources import run, has_entire, has_git +from reflect_cli.sources import run, has_entire, has_git def gather_evidence(max_checkpoints=12, auto_generate=True): diff --git a/lib/improve.py b/reflect_cli/improve.py similarity index 98% rename from lib/improve.py rename to reflect_cli/improve.py index d69a051..154456d 100644 --- a/lib/improve.py +++ b/reflect_cli/improve.py @@ -9,8 +9,8 @@ import sys from pathlib import Path -from lib.evidence import gather_evidence -from lib.context import load_format +from reflect_cli.evidence import gather_evidence +from reflect_cli.context import load_format def analyze_context_quality(context_md): diff --git a/lib/init.py b/reflect_cli/init.py similarity index 100% rename from lib/init.py rename to reflect_cli/init.py diff --git a/lib/metrics.py b/reflect_cli/metrics.py similarity index 100% rename from lib/metrics.py rename to reflect_cli/metrics.py diff --git a/lib/search.py b/reflect_cli/search.py similarity index 100% rename from lib/search.py rename to reflect_cli/search.py diff --git a/lib/sessions.py b/reflect_cli/sessions.py similarity index 100% rename from lib/sessions.py rename to reflect_cli/sessions.py diff --git a/lib/sources.py b/reflect_cli/sources.py similarity index 100% rename from lib/sources.py rename to reflect_cli/sources.py diff --git a/lib/status.py b/reflect_cli/status.py similarity index 100% rename from lib/status.py rename to reflect_cli/status.py diff --git a/lib/synthesize.py b/reflect_cli/synthesize.py similarity index 100% rename from lib/synthesize.py rename to reflect_cli/synthesize.py diff --git a/lib/timeline.py b/reflect_cli/timeline.py similarity index 100% rename from lib/timeline.py rename to reflect_cli/timeline.py diff --git a/lib/why.py b/reflect_cli/why.py similarity index 100% rename from lib/why.py rename to reflect_cli/why.py diff --git a/scripts/smoke.sh b/scripts/smoke.sh index 11ac154..d4a68fd 100755 --- a/scripts/smoke.sh +++ b/scripts/smoke.sh @@ -8,7 +8,7 @@ trap 'rm -rf "$TMP"' EXIT cp "$ROOT/reflect" "$TMP/reflect" chmod +x "$TMP/reflect" -cp -R "$ROOT/lib" "$TMP/lib" +cp -R "$ROOT/reflect_cli" "$TMP/reflect_cli" cd "$TMP" git init -q From bd39eb54713ee8dd9a0dd92819a034a5e47cae4a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Apr 2026 08:09:58 +0000 Subject: [PATCH 2/3] chore: gitignore Python build artifacts (dist/, egg-info/, build/) https://claude.ai/code/session_015u199PXPEv5cyD8UjcRQkd --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 6295979..2c80c10 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ .entire/ __pycache__/ *.pyc +dist/ +*.egg-info/ +build/ bench/results/tmp-* From 4d117d99eed0a5934906234845ee42d5412993d1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Apr 2026 08:18:47 +0000 Subject: [PATCH 3/3] simplify: curl-only install, remove Homebrew formula Single install method: curl -fsSL .../install.sh | bash Removes Homebrew formula and tap workflow. Keeps PyPI publish for the curl installer to pip-install from the local clone. https://claude.ai/code/session_015u199PXPEv5cyD8UjcRQkd --- .github/workflows/publish.yml | 12 ---- CLAUDE.md | 2 +- README.md | 10 ---- homebrew/reflect-cli.rb | 19 ------- install.sh | 103 ++++++++++++++-------------------- 5 files changed, 42 insertions(+), 104 deletions(-) delete mode 100644 homebrew/reflect-cli.rb diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0cea126..a6b3102 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -40,15 +40,3 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: gh release create "${{ github.ref_name }}" --generate-notes - homebrew: - needs: pypi - runs-on: ubuntu-latest - steps: - - name: Update Homebrew formula - uses: mislav/bump-homebrew-formula-action@v3 - with: - formula-name: reflect-cli - homebrew-tap: codeyogi911/homebrew-tap - download-url: https://github.com/codeyogi911/reflect/archive/refs/tags/${{ github.ref_name }}.tar.gz - env: - COMMITTER_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} diff --git a/CLAUDE.md b/CLAUDE.md index 4733fd9..57eebdb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,7 +12,7 @@ Repo-owned memory for AI coding agents. Reads raw evidence from Entire CLI sessi - `reflect_cli/` — Python package (evidence, context, init, why, search, status, improve) - `reflect_cli/cli.py` — main entry point (used by pyproject.toml console_scripts) - `reflect_cli/evidence.py` — fixed evidence gathering pipeline (Entire CLI + git) -- `pyproject.toml` — package config for pip/pipx/Homebrew distribution +- `pyproject.toml` — package config for pip/pipx distribution - `skill/SKILL.md` — skill source (dev copy; install copies to `.claude/skills/reflect/`) - `SPEC.md` — specification for `.reflect/` directory format - `hooks/session-start.sh` — SessionStart hook for context freshness (also linked from the skill dir) diff --git a/README.md b/README.md index e285113..5f285c4 100644 --- a/README.md +++ b/README.md @@ -19,16 +19,6 @@ ## Install ```bash -# Recommended: pipx (isolated install, like `go install`) -pipx install reflect-cli - -# Or: pip -pip install reflect-cli - -# Or: Homebrew -brew tap codeyogi911/tap && brew install reflect-cli - -# Or: curl installer (auto-detects best method) curl -fsSL https://raw.githubusercontent.com/codeyogi911/reflect/main/install.sh | bash ``` diff --git a/homebrew/reflect-cli.rb b/homebrew/reflect-cli.rb deleted file mode 100644 index dbe997a..0000000 --- a/homebrew/reflect-cli.rb +++ /dev/null @@ -1,19 +0,0 @@ -class ReflectCli < Formula - include Language::Python::Virtualenv - - desc "Repo-owned memory for AI coding agents" - homepage "https://github.com/codeyogi911/reflect" - url "https://github.com/codeyogi911/reflect/archive/refs/tags/v0.1.0.tar.gz" - # sha256 will be filled automatically by the tap update workflow - license "MIT" - - depends_on "python@3.12" - - def install - virtualenv_install_with_resources - end - - test do - assert_match "Repo-owned memory", shell_output("#{bin}/reflect --help") - end -end diff --git a/install.sh b/install.sh index 1d97666..acda8ec 100755 --- a/install.sh +++ b/install.sh @@ -3,72 +3,51 @@ set -euo pipefail REPO="codeyogi911/reflect" -INSTALL_DIR="${HOME}/.local/bin" +CLONE_DIR="${HOME}/.local/share/reflect" -# ── Helpers ───────────────────────────────────────────────────────── info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } -warn() { printf '\033[1;33mWarning:\033[0m %s\n' "$*"; } err() { printf '\033[1;31mError:\033[0m %s\n' "$*" >&2; exit 1; } command_exists() { command -v "$1" >/dev/null 2>&1; } -# ── Detect install method ────────────────────────────────────────── -install_via_pipx() { - info "Installing via pipx..." - pipx install reflect-cli -} - -install_via_pip() { - info "Installing via pip..." - pip install --user reflect-cli -} - -install_via_source() { - info "Installing from source..." - local clone_dir="${HOME}/.local/share/reflect" - - if [ -d "$clone_dir" ]; then - info "Updating existing clone..." - git -C "$clone_dir" pull --ff-only origin main - else - git clone "https://github.com/${REPO}.git" "$clone_dir" - fi - - mkdir -p "$INSTALL_DIR" - cd "$clone_dir" - pip install --user -e . 2>/dev/null || { - # Fallback: symlink for systems without pip - ln -sf "$clone_dir/reflect" "$INSTALL_DIR/reflect" - info "Installed via symlink (pip unavailable)" +# ── Preflight ────────────────────────────────────────────────────── +command_exists git || err "git is required. Install it and try again." +command_exists python3 || err "python3 is required (3.11+). Install it and try again." + +# ── Clone or update ──────────────────────────────────────────────── +if [ -d "$CLONE_DIR" ]; then + info "Updating existing install..." + git -C "$CLONE_DIR" pull --ff-only origin main 2>/dev/null || { + info "Pull failed, re-cloning..." + rm -rf "$CLONE_DIR" + git clone "https://github.com/${REPO}.git" "$CLONE_DIR" } -} - -# ── Main ─────────────────────────────────────────────────────────── -main() { - info "Installing reflect..." - - if command_exists pipx; then - install_via_pipx - elif command_exists pip; then - install_via_pip - elif command_exists git; then - install_via_source - else - err "No package manager found. Install pipx, pip, or git and try again." - fi - - # Verify installation - if command_exists reflect; then - info "reflect installed successfully!" - reflect --help | head -3 - else - warn "reflect was installed but is not on your PATH." - echo "" - echo "Add this to your shell profile:" - echo ' export PATH="$HOME/.local/bin:$PATH"' - echo "" - echo "Then restart your shell and run: reflect --help" - fi -} - -main "$@" +else + info "Cloning reflect..." + git clone "https://github.com/${REPO}.git" "$CLONE_DIR" +fi + +# ── Install via pip ──────────────────────────────────────────────── +info "Installing reflect CLI..." +if command_exists pipx; then + pipx install --force "$CLONE_DIR" +elif command_exists pip; then + pip install --user "$CLONE_DIR" +else + # Last resort: install pip via ensurepip, then install + python3 -m ensurepip --default-pip 2>/dev/null || true + python3 -m pip install --user "$CLONE_DIR" || err "Could not install. Please install pip or pipx and try again." +fi + +# ── Verify ───────────────────────────────────────────────────────── +if command_exists reflect; then + info "reflect installed successfully!" + echo "" + reflect --help | head -3 +else + info "Install complete. Add ~/.local/bin to your PATH:" + echo "" + echo ' export PATH="$HOME/.local/bin:$PATH"' + echo "" + echo "Then restart your shell and run: reflect --help" +fi