Skip to content
Closed
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
42 changes: 42 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@
.entire/
__pycache__/
*.pyc
dist/
*.egg-info/
build/
bench/results/tmp-*
16 changes: 9 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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 <topic>`
Expand Down
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

---

## Quick Start
## Install

```bash
# Install reflect
git clone https://github.com/codeyogi911/reflect.git
cd reflect && ./install.sh
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
Expand Down Expand Up @@ -180,9 +182,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 <topic>`
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 <topic>`
5. Submit a PR

## License

Expand Down
76 changes: 47 additions & 29 deletions install.sh
Original file line number Diff line number Diff line change
@@ -1,35 +1,53 @@
#!/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)"

# ── 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"

# ── 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")}"

SKILL_SRC="$SCRIPT_DIR/skill/SKILL.md"
SKILL_DST="$TARGET_REPO/.claude/skills/reflect"

mkdir -p "$SKILL_DST"
cp "$SKILL_SRC" "$SKILL_DST/SKILL.md"

# 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"
REPO="codeyogi911/reflect"
CLONE_DIR="${HOME}/.local/share/reflect"

info() { printf '\033[1;34m==>\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; }

# ── 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"
}
else
info "Cloning reflect..."
git clone "https://github.com/${REPO}.git" "$CLONE_DIR"
fi

echo "Skill installed: $SKILL_DST/SKILL.md"
# ── 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

# ── Summary ──────────────────────────────────────────────────────────
echo ""
echo "Make sure $BIN_DIR is on your PATH."
echo "Run 'reflect init' in any git repo to get started."
# ── 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
38 changes: 38 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -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"
18 changes: 9 additions & 9 deletions reflect
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
File renamed without changes.
6 changes: 6 additions & 0 deletions reflect_cli/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Allow running as: python -m reflect_cli"""

import sys
from reflect_cli.cli import main

sys.exit(main() or 0)
File renamed without changes.
103 changes: 103 additions & 0 deletions reflect_cli/cli.py
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 1 addition & 1 deletion lib/context.py → reflect_cli/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


# ---------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion lib/evidence.py → reflect_cli/evidence.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Loading
Loading