diff --git a/README.md b/README.md index 4ebf53b..a9c5e0c 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ LLM + + Discord +

@@ -58,9 +61,9 @@ Four ways this changes what Claude Code can do for you: - đź’ˇ **Stop repeating the same mistakes:** Produces actionable playbooks Claude can follow next time; memory only records what happened. - > *Example:* you tell Claude to stop running `npm test` without `--run` because watch mode hangs.
- > **Memory:** “user was annoyed about npm test hanging”
- > **Learning:** “always pass `--run` to `npm test` in this repo — default watch mode blocks CI” + > *Example:* a deploy fails after Claude bumps `prisma` from 5.x to 6.0; you tell it to roll back because 6.0 breaks nested writes in your order flow.
+ > **Memory:** “deploy broke after prisma bump; user rolled back”
+ > **Learning:** “treat major-version bumps of ORMs/DB drivers as breaking — verify with integration tests, not just unit tests” - 🚀 **Start from the optimized path:** Preserves and optimizes execution paths so Claude can reuse what already works. > *Example:* Claude spends several iterations trying to start the local dev environment before discovering that this repo requires `pnpm dev:all` instead of the usual `npm run dev`.
diff --git a/assets/playbook_dashboard.png b/assets/playbook_dashboard.png index 846ce0d..8eb0c7c 100644 Binary files a/assets/playbook_dashboard.png and b/assets/playbook_dashboard.png differ diff --git a/assets/profile_dashboard.png b/assets/profile_dashboard.png index 2f7246a..034f39c 100644 Binary files a/assets/profile_dashboard.png and b/assets/profile_dashboard.png differ diff --git a/plugin/dashboard/app/configure/server/page.tsx b/plugin/dashboard/app/configure/server/page.tsx index 10d6df1..a16b079 100644 --- a/plugin/dashboard/app/configure/server/page.tsx +++ b/plugin/dashboard/app/configure/server/page.tsx @@ -162,11 +162,11 @@ export default function ConfigureServerPage() { { const n = Number(e.target.value); updateSrv( - "batch_size", + "window_size", Number.isFinite(n) && n > 0 ? n : undefined, ); }} @@ -184,11 +184,11 @@ export default function ConfigureServerPage() { { const n = Number(e.target.value); updateSrv( - "batch_interval", + "stride_size", Number.isFinite(n) && n > 0 ? n : undefined, ); }} diff --git a/plugin/dashboard/app/dashboard/page.tsx b/plugin/dashboard/app/dashboard/page.tsx index 41937dd..329a137 100644 --- a/plugin/dashboard/app/dashboard/page.tsx +++ b/plugin/dashboard/app/dashboard/page.tsx @@ -183,7 +183,7 @@ export default function DashboardPage() { )} diff --git a/plugin/dashboard/app/playbooks/page.tsx b/plugin/dashboard/app/playbooks/page.tsx index 6f90604..ac8c219 100644 --- a/plugin/dashboard/app/playbooks/page.tsx +++ b/plugin/dashboard/app/playbooks/page.tsx @@ -137,7 +137,7 @@ export default function PlaybooksPage() { ) : (

diff --git a/plugin/dashboard/app/profiles/page.tsx b/plugin/dashboard/app/profiles/page.tsx index 311c272..a3859a9 100644 --- a/plugin/dashboard/app/profiles/page.tsx +++ b/plugin/dashboard/app/profiles/page.tsx @@ -82,7 +82,7 @@ export default function ProfilesPage() { ) : (
diff --git a/plugin/dashboard/lib/types.ts b/plugin/dashboard/lib/types.ts index 41b85f1..cc64efc 100644 --- a/plugin/dashboard/lib/types.ts +++ b/plugin/dashboard/lib/types.ts @@ -112,8 +112,8 @@ export interface ReflexioExtractorConfig { export interface ReflexioConfig { agent_context_prompt?: string | null; - batch_size?: number; - batch_interval?: number; + window_size?: number; + stride_size?: number; profile_extractor_configs?: ReflexioExtractorConfig[] | null; user_playbook_extractor_configs?: ReflexioExtractorConfig[] | null; [k: string]: unknown; diff --git a/plugin/pyproject.toml b/plugin/pyproject.toml index 9e42280..b4241e3 100644 --- a/plugin/pyproject.toml +++ b/plugin/pyproject.toml @@ -5,7 +5,7 @@ description = "Self-improving Claude Code plugin — learns from corrections via readme = "README.md" requires-python = ">=3.12" dependencies = [ - "reflexio-ai>=0.2.15", + "reflexio-ai>=0.2.20", # Used by reflexio's local embedding provider (ONNXMiniLM_L6_V2). # Pulls in onnxruntime + tokenizers; the ~80 MB ONNX model itself is # downloaded on first use, not at install time. diff --git a/plugin/src/claude_smart/events/session_start.py b/plugin/src/claude_smart/events/session_start.py index c793600..f0a15e7 100644 --- a/plugin/src/claude_smart/events/session_start.py +++ b/plugin/src/claude_smart/events/session_start.py @@ -10,11 +10,11 @@ from claude_smart import context_format, cs_cite, hook, ids, state from claude_smart.reflexio_adapter import Adapter -# Claude-smart's preferred extraction cadence — more frequent, smaller batches +# Claude-smart's preferred extraction cadence — more frequent, smaller windows # than reflexio's out-of-box 10/5. Applied idempotently to the reflexio server -# on every SessionStart via Adapter.apply_batch_defaults. -_CLAUDE_SMART_BATCH_SIZE = 5 -_CLAUDE_SMART_BATCH_INTERVAL = 3 +# on every SessionStart via Adapter.apply_extraction_defaults. +_CLAUDE_SMART_WINDOW_SIZE = 5 +_CLAUDE_SMART_STRIDE_SIZE = 3 def handle(payload: dict[str, Any]) -> None: @@ -26,9 +26,9 @@ def handle(payload: dict[str, Any]) -> None: project_id = ids.resolve_project_id(cwd) adapter = Adapter() - adapter.apply_batch_defaults( - batch_size=_CLAUDE_SMART_BATCH_SIZE, - batch_interval=_CLAUDE_SMART_BATCH_INTERVAL, + adapter.apply_extraction_defaults( + window_size=_CLAUDE_SMART_WINDOW_SIZE, + stride_size=_CLAUDE_SMART_STRIDE_SIZE, ) playbooks, profiles = adapter.fetch_both( project_id=project_id, diff --git a/plugin/src/claude_smart/reflexio_adapter.py b/plugin/src/claude_smart/reflexio_adapter.py index 323b733..1beb55d 100644 --- a/plugin/src/claude_smart/reflexio_adapter.py +++ b/plugin/src/claude_smart/reflexio_adapter.py @@ -121,23 +121,23 @@ def delete_all(self) -> tuple[dict[str, int], list[tuple[str, str]]] | None: counts[name] = getattr(response, "deleted_count", 0) or 0 return counts, errors - def apply_batch_defaults(self, *, batch_size: int, batch_interval: int) -> bool: - """Push claude-smart's preferred batch defaults to the reflexio server. + def apply_extraction_defaults(self, *, window_size: int, stride_size: int) -> bool: + """Push claude-smart's preferred extraction defaults to the reflexio server. Reads the current ``Config`` and only issues a ``set_config`` when the server-side values differ, so steady state is a single cheap GET. Reflexio persists ``Config`` to disk, so once these values land they survive backend restarts. The flip side: if an operator customizes - ``batch_size``/``batch_interval`` via the dashboard, this call will + ``window_size``/``stride_size`` via the dashboard, this call will overwrite those values back to the claude-smart defaults on the next SessionStart. To change the defaults, edit the constants at the call site in ``events/session_start.py``. Args: - batch_size (int): Desired ``Config.batch_size`` on the server. - batch_interval (int): Desired ``Config.batch_interval`` on the - server. Must be ``<= batch_size`` (reflexio enforces this). + window_size (int): Desired ``Config.window_size`` on the server. + stride_size (int): Desired ``Config.stride_size`` on the + server. Must be ``<= window_size`` (reflexio enforces this). Returns: bool: True if the server is already at the target values or the @@ -150,16 +150,16 @@ def apply_batch_defaults(self, *, batch_size: int, batch_interval: int) -> bool: try: config = client.get_config() if ( - getattr(config, "batch_size", None) == batch_size - and getattr(config, "batch_interval", None) == batch_interval + getattr(config, "window_size", None) == window_size + and getattr(config, "stride_size", None) == stride_size ): return True - config.batch_size = batch_size - config.batch_interval = batch_interval + config.window_size = window_size + config.stride_size = stride_size client.set_config(config) return True except Exception as exc: # noqa: BLE001 — adapter must never raise. - _LOGGER.warning("apply_batch_defaults failed: %s", exc) + _LOGGER.warning("apply_extraction_defaults failed: %s", exc) return False # ----------------------------------------------------------------- diff --git a/plugin/uv.lock b/plugin/uv.lock index 7e8fddd..ed7613b 100644 --- a/plugin/uv.lock +++ b/plugin/uv.lock @@ -434,7 +434,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "chromadb", specifier = ">=0.5" }, - { name = "reflexio-ai", specifier = ">=0.2.15" }, + { name = "reflexio-ai", specifier = ">=0.2.20" }, ] [package.metadata.requires-dev] diff --git a/reflexio b/reflexio index 9da73f8..b8ac1b0 160000 --- a/reflexio +++ b/reflexio @@ -1 +1 @@ -Subproject commit 9da73f80456365abc6f756b3742059d4e50b019d +Subproject commit b8ac1b0e92b703f3776b93a9f4d240b332d7981e diff --git a/tests/test_adapter.py b/tests/test_adapter.py index 76622db..6fed522 100644 --- a/tests/test_adapter.py +++ b/tests/test_adapter.py @@ -229,17 +229,15 @@ def test_fetch_playbooks_default_top_k_is_tightened() -> None: # ----------------------------------------------------------------------------- -# apply_batch_defaults — push claude-smart's preferred cadence to reflexio +# apply_extraction_defaults — push claude-smart's preferred cadence to reflexio # ----------------------------------------------------------------------------- class _ConfigClient: """Captures get_config/set_config calls against a mutable config object.""" - def __init__(self, *, batch_size: int, batch_interval: int, get_raises=None): - self._config = SimpleNamespace( - batch_size=batch_size, batch_interval=batch_interval - ) + def __init__(self, *, window_size: int, stride_size: int, get_raises=None): + self._config = SimpleNamespace(window_size=window_size, stride_size=stride_size) self._get_raises = get_raises self.set_calls: list[SimpleNamespace] = [] @@ -251,39 +249,39 @@ def get_config(self): def set_config(self, config): self.set_calls.append( SimpleNamespace( - batch_size=config.batch_size, batch_interval=config.batch_interval + window_size=config.window_size, stride_size=config.stride_size ) ) self._config = config return {"ok": True} -def test_apply_batch_defaults_writes_when_values_differ() -> None: - client = _ConfigClient(batch_size=10, batch_interval=5) +def test_apply_extraction_defaults_writes_when_values_differ() -> None: + client = _ConfigClient(window_size=10, stride_size=5) a = _adapter_with(client) - assert a.apply_batch_defaults(batch_size=5, batch_interval=3) is True + assert a.apply_extraction_defaults(window_size=5, stride_size=3) is True assert len(client.set_calls) == 1 - assert client.set_calls[0].batch_size == 5 - assert client.set_calls[0].batch_interval == 3 + assert client.set_calls[0].window_size == 5 + assert client.set_calls[0].stride_size == 3 -def test_apply_batch_defaults_skips_set_when_values_match() -> None: - client = _ConfigClient(batch_size=5, batch_interval=3) +def test_apply_extraction_defaults_skips_set_when_values_match() -> None: + client = _ConfigClient(window_size=5, stride_size=3) a = _adapter_with(client) - assert a.apply_batch_defaults(batch_size=5, batch_interval=3) is True + assert a.apply_extraction_defaults(window_size=5, stride_size=3) is True assert client.set_calls == [] -def test_apply_batch_defaults_returns_false_when_client_unavailable( +def test_apply_extraction_defaults_returns_false_when_client_unavailable( monkeypatch, ) -> None: a = reflexio_adapter.Adapter() monkeypatch.setattr(a, "_get_client", lambda: None) - assert a.apply_batch_defaults(batch_size=5, batch_interval=3) is False + assert a.apply_extraction_defaults(window_size=5, stride_size=3) is False -def test_apply_batch_defaults_absorbs_get_config_errors() -> None: +def test_apply_extraction_defaults_absorbs_get_config_errors() -> None: a = _adapter_with( - _ConfigClient(batch_size=10, batch_interval=5, get_raises=RuntimeError("down")) + _ConfigClient(window_size=10, stride_size=5, get_raises=RuntimeError("down")) ) - assert a.apply_batch_defaults(batch_size=5, batch_interval=3) is False + assert a.apply_extraction_defaults(window_size=5, stride_size=3) is False diff --git a/tests/test_events.py b/tests/test_events.py index f99d9a4..dc3c2d4 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -1208,7 +1208,7 @@ def test_session_start_emits_continue_when_no_playbook_or_profile( session_dir, monkeypatch ) -> None: class StubAdapter: - def apply_batch_defaults(self, **_kw): + def apply_extraction_defaults(self, **_kw): return True def fetch_both(self, **_kw): @@ -1230,7 +1230,7 @@ def test_session_start_emits_additional_context_when_playbook_present( session_dir, monkeypatch ) -> None: class Stub: - def apply_batch_defaults(self, **_kw): + def apply_extraction_defaults(self, **_kw): return True def fetch_both(self, **_kw): @@ -1260,14 +1260,14 @@ def fetch_both(self, **_kw): assert "use pathlib" in payload["hookSpecificOutput"]["additionalContext"] -def test_session_start_applies_claude_smart_batch_defaults( +def test_session_start_applies_claude_smart_extraction_defaults( session_dir, monkeypatch ) -> None: - """SessionStart must push claude-smart's 5/3 batch defaults to reflexio.""" + """SessionStart must push claude-smart's 5/3 extraction defaults to reflexio.""" applied: list[dict[str, Any]] = [] class Stub: - def apply_batch_defaults(self, **kwargs): + def apply_extraction_defaults(self, **kwargs): applied.append(kwargs) return True @@ -1284,7 +1284,7 @@ def fetch_both(self, **_kw): buf = io.StringIO() monkeypatch.setattr(sys, "stdout", buf) session_start.handle({"session_id": "s1", "source": "startup"}) - assert applied == [{"batch_size": 5, "batch_interval": 3}] + assert applied == [{"window_size": 5, "stride_size": 3}] def test_session_start_fetches_both_on_every_source(session_dir, monkeypatch) -> None: @@ -1292,7 +1292,7 @@ def test_session_start_fetches_both_on_every_source(session_dir, monkeypatch) -> calls: list[dict[str, Any]] = [] class Stub: - def apply_batch_defaults(self, **_kw): + def apply_extraction_defaults(self, **_kw): return True def fetch_both(self, **kwargs):