superlocalmemory 3.6.16 → 3.6.18
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.
- package/CHANGELOG.md +25 -0
- package/README.md +3 -2
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/CLAUDE.md +3 -3
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-recall/SKILL.md +1 -1
- package/plugin/skills/slm-remember/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/agents/slm-memory-advisor.md +1 -1
- package/plugin-src/agents/slm-optimize-advisor.md +1 -1
- package/plugin-src/manifest.json +1 -1
- package/plugin-src/requirements.txt +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/plugin-src/rules/CLAUDE.md.fragment +3 -3
- package/plugin-src/skills/slm-cache/SKILL.md +1 -1
- package/plugin-src/skills/slm-compress/SKILL.md +1 -1
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-recall/SKILL.md +1 -1
- package/plugin-src/skills/slm-remember/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/skills/slm-status/SKILL.md +1 -1
- package/pyproject.toml +3 -2
- package/scripts/build-plugin.js +1 -1
- package/scripts/postinstall-interactive.js +94 -7
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/setup_wizard.py +34 -0
- package/src/superlocalmemory/core/embeddings.py +5 -0
- package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +4 -0
- package/src/superlocalmemory/hooks/adapter_base.py +10 -3
- package/src/superlocalmemory/hooks/claude_code_hooks.py +40 -4
- package/src/superlocalmemory/hooks/copilot_adapter.py +78 -9
- package/src/superlocalmemory/hooks/hook_handlers.py +59 -1
- package/src/superlocalmemory/hooks/memory_protocol.py +102 -0
- package/src/superlocalmemory/hooks/post_tool_async_hook.py +23 -5
- package/src/superlocalmemory/infra/event_bus.py +4 -0
- package/src/superlocalmemory/learning/feedback.py +59 -0
- package/src/superlocalmemory/learning/outcome_queue.py +10 -2
- package/src/superlocalmemory/llm/backbone.py +8 -1
- package/src/superlocalmemory/retrieval/reranker.py +5 -0
- package/src/superlocalmemory/server/routes/learning.py +2 -0
- package/src/superlocalmemory/server/routes/v3_api.py +11 -0
- package/src/superlocalmemory/server/unified_daemon.py +78 -0
- package/src/superlocalmemory/storage/database.py +34 -7
- package/src/superlocalmemory/storage/migrations/M017_ccq_scope_column.py +79 -0
- package/src/superlocalmemory.egg-info/PKG-INFO +4 -3
- package/src/superlocalmemory.egg-info/SOURCES.txt +2 -0
- package/plugin-src/commands/slm-optimize.md +0 -22
- package/plugin-src/commands/slm-recall.md +0 -16
- package/plugin-src/commands/slm-remember.md +0 -16
- package/plugin-src/commands/slm-status.md +0 -15
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ All notable changes to SuperLocalMemory V3 will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.6.17] - 2026-06-21 — Community PR round + dashboard-feedback fix + SQLite tuning
|
|
9
|
+
|
|
10
|
+
Eight community pull requests merged after line-by-line review, plus fixes for the open issues. Every change was validated against the full test suite under the real 3.12 runtime; default single-machine behavior is unchanged.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **HTTP write-path observability** (PR #52, @barrygfox). The HTTP fast paths (`/observe`, `/remember`, the AutoCapture pipeline, the materializer) now emit `EventBus` events tagged `source_protocol="http"`, so the dashboard event stream is no longer structurally empty. New event types: `memory.observed`, `memory.captured`, `memory.dropped`, `memory.queued`. Emission is best-effort and never affects the caller's response.
|
|
15
|
+
- **Marker-bounded adapter writes** (PR #54, @barrygfox). `CopilotAdapter` now wraps its content in `<!-- SLM-START -->` / `<!-- SLM-END -->` markers and merges into `.github/copilot-instructions.md` instead of overwriting it, preserving user- and agent-curated content. `disable()` strips the SLM block instead of deleting the file. New `hooks/memory_protocol.py` is the shared single source of truth for the marker contract.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **Dashboard feedback was completely broken** (issues #53/#59). The dashboard thumbs-up/down/pin and dwell handlers called `FeedbackCollector.record_dashboard_feedback()` — a method that did not exist, so every write raised `AttributeError` (caught by the route, so no lock leak, but the feature was dead). Implemented the method, mapping the dashboard vocabulary onto stored `(signal_type, value)` pairs; the raw query is hashed, never stored.
|
|
20
|
+
- **NULL columns reloaded as `[]` instead of `None`** (PR #50, @barrygfox). `_jl()` collapsed "no default" and explicit `default=None`, defeating downstream `is None` guards and causing `Mean of empty slice` warnings in the Fisher–Langevin coupling. Fixed with a `_MISSING` sentinel + an empty-array guard.
|
|
21
|
+
- **Lifecycle hooks hard-coded daemon port `:8765`** (PR #51, @barrygfox). Hooks now resolve the port from the per-user `~/.superlocalmemory/daemon.port` file; the non-loopback SSRF guard on `SLM_HOOK_DAEMON_URL` is preserved.
|
|
22
|
+
- **`atomic_write` honored a stale sync-log skip** (PR #55, @barrygfox). The on-disk file is re-hashed before a durable skip, so an out-of-band edit (`git restore`, manual edit) is no longer silently ignored.
|
|
23
|
+
- **Embedding/reranker workers ran single-threaded** (PR #56, @barrygfox). `OMP_NUM_THREADS` is restored in those subprocess workers (which load torch but never lightgbm, so the libomp SIGSEGV cannot occur).
|
|
24
|
+
- **Anthropic provider ignored `api_base`** (PR #57, @barrygfox). The Anthropic backbone now honors a configured base URL (Anthropic-compatible proxy), mirroring the OpenAI provider.
|
|
25
|
+
- **Dashboard screenshot committed as a raw binary** (PR #58, @MelleKoning). Converted to a Git LFS pointer per the existing `.gitattributes` rule.
|
|
26
|
+
- **"Test Connection" blocked for remote dashboards** (issue #40 residue). In `SLM_REMOTE` mode, an allowlisted LAN client may probe its own LAN LLM endpoint, exactly like the loopback dashboard. The SSRF guard is not relaxed for any non-allowlisted caller.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- **SQLite endurance knobs are env-tunable** (issue #53). `SLM_DB_BUSY_TIMEOUT_MS`, `SLM_DB_MAX_RETRIES`, and `SLM_DB_RETRY_BASE_DELAY` override the defaults for operators on slow/contended I/O. Unset env is byte-identical to the prior hard-coded constants.
|
|
31
|
+
- **`outcome_queue` polling backs off when idle** (issue #53). The drain worker now relaxes its 0.25s poll (doubling, capped at 2s) when the queue drains empty and snaps back to 0.25s the instant there is work, reducing idle contention on the shared SQLite file.
|
|
32
|
+
|
|
8
33
|
## [3.6.14] - 2026-06-18 — Audit-hardened: memory bounds, cross-tenant cache isolation, atomic credentials
|
|
9
34
|
|
|
10
35
|
Shipped through two adversarial audit passes (Qualixar Iron Pattern Stages 8–9), validated against a green 5933-test suite under the real 3.12 runtime. Default single-machine behavior is unchanged.
|
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
<img src="https://superlocalmemory.com/assets/logo-mark.png" alt="SuperLocalMemory" width="200"/>
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<h1 align="center">SuperLocalMemory V3.6.
|
|
5
|
+
<h1 align="center">SuperLocalMemory V3.6.18</h1>
|
|
6
6
|
<p align="center"><strong>Cache. Compress. Remember. Three surfaces — proxy, MCP tools, or skill. Every setup covered.</strong><br/>
|
|
7
7
|
<em>To the best of our knowledge, the only zero-cloud agent memory that beats Mem0's zero-LLM score on LoCoMo. Mode A: 74.8% vs Mem0 64.2% — no GPU, no API key, on CPU.</em></p>
|
|
8
|
-
<p align="center"><code>v3.6.
|
|
8
|
+
<p align="center"><code>v3.6.18</code> — <strong>Plugin-native. Profile-aware. Distributed-ready.</strong><br/>
|
|
9
9
|
Proxy: <code>slm wrap claude</code> · MCP: add <code>slm_compress</code> to your config · Skill: zero-config</p>
|
|
10
10
|
<p align="center"><strong>3 published research papers</strong> (arXiv preprints + Zenodo-archived) · <a href="https://arxiv.org/abs/2603.02240">arXiv:2603.02240</a> · <a href="https://arxiv.org/abs/2603.14588">arXiv:2603.14588</a> · <a href="https://arxiv.org/abs/2604.04514">arXiv:2604.04514</a></p>
|
|
11
11
|
|
|
@@ -307,6 +307,7 @@ slm dashboard # Opens at http://localhost:8765
|
|
|
307
307
|
|
|
308
308
|
| Version | Codename | Key Features |
|
|
309
309
|
|---|---|---|
|
|
310
|
+
| **v3.6.17** | Community | 8 contributor PRs (observability events, marker-bounded adapter writes, daemon port discovery, anthropic `api_base`, OpenMP workers, atomic-write rehash, `_jl` sentinel, LFS pointer); dashboard-feedback fix (#53/#59); env-tunable SQLite knobs + idle backoff; remote LLM test-probe (#40) |
|
|
310
311
|
| **v3.6.16** | Docs | Corrected Claude Code plugin install — adds the required `/plugin marketplace add` step; clarifies plugin vs pip/npm delivery |
|
|
311
312
|
| **v3.6.15** | Multi-scope | **Opt-in [shared memory](docs/shared-memory.md)** (personal/shared/global, off by default), default-deny scope at every read path, recall scope-race fix, contributor PRs #42/#43/#44, fixes #46–#49 |
|
|
312
313
|
| **v3.6.14** | Plugin-native | Claude Code Plugin (WP-06), MCP profiles (WP-01), IDE connect (WP-08), asset consolidation, UI polish (WP-12) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superlocalmemory",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.18",
|
|
4
4
|
"description": "Information-geometric agent memory with mathematical guarantees. 4-channel retrieval, Fisher-Rao similarity, zero-LLM mode, EU AI Act compliant. Works with Claude, Cursor, Windsurf, and 17+ AI tools.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-memory",
|
package/plugin/CLAUDE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- BEGIN SuperLocalMemory v3.6.
|
|
1
|
+
<!-- BEGIN SuperLocalMemory v3.6.18 -->
|
|
2
2
|
|
|
3
3
|
## SuperLocalMemory (SLM) — Agent Rules
|
|
4
4
|
|
|
@@ -39,6 +39,6 @@ slm-recall · slm-remember · slm-session · slm-status · slm-cache · slm-comp
|
|
|
39
39
|
### Subagents
|
|
40
40
|
slm-memory-advisor (memory decisions, session hygiene) · slm-optimize-advisor (context compression + KV cache)
|
|
41
41
|
|
|
42
|
-
<!-- END SuperLocalMemory v3.6.
|
|
42
|
+
<!-- END SuperLocalMemory v3.6.18 -->
|
|
43
43
|
|
|
44
|
-
SuperLocalMemory v3.6.
|
|
44
|
+
SuperLocalMemory v3.6.18 · Qualixar · AGPL-3.0-or-later
|
|
@@ -41,4 +41,4 @@ recall→`slm recall "<q>" --limit N` (add `--include-global`/`--include-shared`
|
|
|
41
41
|
# What NOT to do
|
|
42
42
|
Never session_init twice; never forget dry_run=False without reporting preview; never dump a whole file into remember; never invent a memory; never claim "saved" without success:true / clean CLI exit.
|
|
43
43
|
|
|
44
|
-
SuperLocalMemory v3.6.
|
|
44
|
+
SuperLocalMemory v3.6.18 · Qualixar · AGPL-3.0-or-later
|
|
@@ -35,4 +35,4 @@ stats→`slm optimize status`/`savings` · compress→`slm compress` · cache→
|
|
|
35
35
|
# What NOT to do
|
|
36
36
|
Never compress code-for-edit/JSON-to-parse/<500 chars; never store secrets/ccr_ids; never let optimize failure block/alter the task; never claim a specific savings %.
|
|
37
37
|
|
|
38
|
-
SuperLocalMemory v3.6.
|
|
38
|
+
SuperLocalMemory v3.6.18 · Qualixar · AGPL-3.0-or-later
|
package/plugin/requirements.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
superlocalmemory==3.6.
|
|
1
|
+
superlocalmemory==3.6.18
|
|
@@ -41,4 +41,4 @@ recall→`slm recall "<q>" --limit N` (add `--include-global`/`--include-shared`
|
|
|
41
41
|
# What NOT to do
|
|
42
42
|
Never session_init twice; never forget dry_run=False without reporting preview; never dump a whole file into remember; never invent a memory; never claim "saved" without success:true / clean CLI exit.
|
|
43
43
|
|
|
44
|
-
SuperLocalMemory v3.6.
|
|
44
|
+
SuperLocalMemory v3.6.18 · Qualixar · AGPL-3.0-or-later
|
|
@@ -35,4 +35,4 @@ stats→`slm optimize status`/`savings` · compress→`slm compress` · cache→
|
|
|
35
35
|
# What NOT to do
|
|
36
36
|
Never compress code-for-edit/JSON-to-parse/<500 chars; never store secrets/ccr_ids; never let optimize failure block/alter the task; never claim a specific savings %.
|
|
37
37
|
|
|
38
|
-
SuperLocalMemory v3.6.
|
|
38
|
+
SuperLocalMemory v3.6.18 · Qualixar · AGPL-3.0-or-later
|
package/plugin-src/manifest.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
superlocalmemory==3.6.
|
|
1
|
+
superlocalmemory==3.6.18
|
|
@@ -88,4 +88,4 @@ When the SLM MCP server is unavailable, use these CLI equivalents:
|
|
|
88
88
|
| `slm_cache_get` | `key` | KV cache get; returns hit, value |
|
|
89
89
|
| `slm_optimize_stats` | `()` | Returns compress_runs, tokens_saved_compress, cache_kv_hits |
|
|
90
90
|
|
|
91
|
-
SuperLocalMemory v3.6.
|
|
91
|
+
SuperLocalMemory v3.6.18 · Qualixar · AGPL-3.0-or-later
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- BEGIN SuperLocalMemory v3.6.
|
|
1
|
+
<!-- BEGIN SuperLocalMemory v3.6.18 -->
|
|
2
2
|
|
|
3
3
|
## SuperLocalMemory (SLM) — Agent Rules
|
|
4
4
|
|
|
@@ -39,6 +39,6 @@ slm-recall · slm-remember · slm-session · slm-status · slm-cache · slm-comp
|
|
|
39
39
|
### Subagents
|
|
40
40
|
slm-memory-advisor (memory decisions, session hygiene) · slm-optimize-advisor (context compression + KV cache)
|
|
41
41
|
|
|
42
|
-
<!-- END SuperLocalMemory v3.6.
|
|
42
|
+
<!-- END SuperLocalMemory v3.6.18 -->
|
|
43
43
|
|
|
44
|
-
SuperLocalMemory v3.6.
|
|
44
|
+
SuperLocalMemory v3.6.18 · Qualixar · AGPL-3.0-or-later
|
package/pyproject.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "superlocalmemory"
|
|
3
|
-
version = "3.6.
|
|
3
|
+
version = "3.6.18"
|
|
4
4
|
description = "Information-geometric agent memory with mathematical guarantees"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {text = "AGPL-3.0-or-later"}
|
|
@@ -146,8 +146,9 @@ superlocalmemory = ["ui/**/*"]
|
|
|
146
146
|
[tool.pytest.ini_options]
|
|
147
147
|
testpaths = ["tests"]
|
|
148
148
|
pythonpath = ["src"]
|
|
149
|
-
addopts = "-m 'not slow and not ollama and not benchmark'"
|
|
149
|
+
addopts = "-p no:cacheprovider -p no:subtests -m 'not slow and not ollama and not benchmark'"
|
|
150
150
|
asyncio_mode = "auto"
|
|
151
|
+
faulthandler_timeout = 0
|
|
151
152
|
markers = [
|
|
152
153
|
"slow: marks tests as slow — real engine/model loading (run with: pytest -m slow)",
|
|
153
154
|
"ollama: marks tests that require a running Ollama instance",
|
package/scripts/build-plugin.js
CHANGED
|
@@ -30,7 +30,7 @@ import process from 'node:process';
|
|
|
30
30
|
// ---------------------------------------------------------------------------
|
|
31
31
|
// Constants
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
|
-
const VERSION = '3.6.
|
|
33
|
+
const VERSION = '3.6.18';
|
|
34
34
|
const MANIFEST_REL = 'plugin-src/manifest.json';
|
|
35
35
|
const GENERATED_BANNER = `# _GENERATED — DO NOT HAND-EDIT
|
|
36
36
|
|
|
@@ -489,19 +489,99 @@ async function runInteractiveFlow(rl, recommendedProfile) {
|
|
|
489
489
|
// under 60 LOC per Stage-8 G2 scope.
|
|
490
490
|
function printLivingBrainDelta() {
|
|
491
491
|
console.log('');
|
|
492
|
-
console.log('What\'s new in v3.
|
|
493
|
-
console.log(' +
|
|
494
|
-
console.log(' +
|
|
495
|
-
console.log(' +
|
|
496
|
-
console.log(' +
|
|
497
|
-
console.log(' + Opt-in skill evolution (Haiku 4.5 default)');
|
|
498
|
-
console.log(' + Evo-Memory public benchmark');
|
|
492
|
+
console.log('What\'s new in v3.6.18:');
|
|
493
|
+
console.log(' + session_init mandate hook — Claude calls ToolSearch→session_init first, every session');
|
|
494
|
+
console.log(' + Plugin auto-install on npm/pip install — skills, agents, hooks wired automatically');
|
|
495
|
+
console.log(' + M017 migration — ccq_consolidated_blocks gets scope column (no more silent CCQ scope drop)');
|
|
496
|
+
console.log(' + GC-safe test flags baked into pyproject.toml + Makefile (no more macOS ARM SIGSEGV)');
|
|
499
497
|
console.log('What\'s unchanged:');
|
|
500
498
|
console.log(' * Your memory.db — zero deletes, zero rewrites');
|
|
501
499
|
console.log(' * Your profile settings');
|
|
502
500
|
console.log(' * All CLI commands you already use');
|
|
503
501
|
}
|
|
504
502
|
|
|
503
|
+
// T1-B: Auto-install the Claude Code plugin after pip/npm install.
|
|
504
|
+
// Best-effort: never fails the installer, never blocks the main flow.
|
|
505
|
+
// Checks for `claude` CLI, then runs:
|
|
506
|
+
// 1. claude plugin marketplace add qualixar/superlocalmemory
|
|
507
|
+
// 2. claude plugin install superlocalmemory@qualixar
|
|
508
|
+
// 3. slm hooks install
|
|
509
|
+
async function tryInstallClaudePlugin() {
|
|
510
|
+
const { execFile } = require('child_process');
|
|
511
|
+
const { promisify } = require('util');
|
|
512
|
+
const execFileAsync = promisify(execFile);
|
|
513
|
+
|
|
514
|
+
// Find claude binary — try PATH first, then common install locations.
|
|
515
|
+
const claudeCandidates = ['claude'];
|
|
516
|
+
if (process.platform !== 'win32') {
|
|
517
|
+
claudeCandidates.push(
|
|
518
|
+
'/usr/local/bin/claude',
|
|
519
|
+
process.env.HOME + '/.npm-global/bin/claude',
|
|
520
|
+
process.env.HOME + '/.local/bin/claude',
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
let claudeBin = null;
|
|
525
|
+
for (const candidate of claudeCandidates) {
|
|
526
|
+
try {
|
|
527
|
+
await execFileAsync(candidate, ['--version'], { timeout: 5000 });
|
|
528
|
+
claudeBin = candidate;
|
|
529
|
+
break;
|
|
530
|
+
} catch (_e) { /* keep looking */ }
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (!claudeBin) {
|
|
534
|
+
// Claude Code not installed — print guidance and skip.
|
|
535
|
+
console.log('SLM: Claude Code CLI not found — skipping plugin auto-install.');
|
|
536
|
+
console.log('SLM: To install the plugin manually after installing Claude Code:');
|
|
537
|
+
console.log('SLM: claude plugin marketplace add qualixar/superlocalmemory');
|
|
538
|
+
console.log('SLM: claude plugin install superlocalmemory@qualixar');
|
|
539
|
+
console.log('SLM: slm hooks install');
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
console.log('SLM: Claude Code found — installing SLM plugin...');
|
|
544
|
+
|
|
545
|
+
// Step 1: Add marketplace
|
|
546
|
+
try {
|
|
547
|
+
await execFileAsync(claudeBin,
|
|
548
|
+
['plugin', 'marketplace', 'add', 'qualixar/superlocalmemory'],
|
|
549
|
+
{ timeout: 30000 });
|
|
550
|
+
console.log('SLM: marketplace added (qualixar/superlocalmemory)');
|
|
551
|
+
} catch (e) {
|
|
552
|
+
// "already exists" or network error — not fatal
|
|
553
|
+
console.log('SLM: marketplace add note: ' + (e.stderr || e.message || String(e)).trim().split('\n')[0]);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Step 2: Install plugin
|
|
557
|
+
try {
|
|
558
|
+
await execFileAsync(claudeBin,
|
|
559
|
+
['plugin', 'install', 'superlocalmemory@qualixar'],
|
|
560
|
+
{ timeout: 30000 });
|
|
561
|
+
console.log('SLM: plugin installed (superlocalmemory@qualixar)');
|
|
562
|
+
} catch (e) {
|
|
563
|
+
console.log('SLM: plugin install note: ' + (e.stderr || e.message || String(e)).trim().split('\n')[0]);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Step 3: Install hooks (only if not already current — avoids needless writes)
|
|
567
|
+
// `slm hooks install` uses atomic tmp-then-rename and only touches the SLM
|
|
568
|
+
// section of settings.json; all other Claude Code settings are preserved.
|
|
569
|
+
try {
|
|
570
|
+
const statusResult = await execFileAsync('slm', ['hooks', 'status', '--json'],
|
|
571
|
+
{ timeout: 10000 }).catch(() => null);
|
|
572
|
+
const alreadyCurrent = statusResult &&
|
|
573
|
+
(() => { try { const s = JSON.parse(statusResult.stdout); return s.installed && !s.needs_upgrade; } catch { return false; } })();
|
|
574
|
+
if (!alreadyCurrent) {
|
|
575
|
+
await execFileAsync('slm', ['hooks', 'install'], { timeout: 15000 });
|
|
576
|
+
console.log('SLM: hooks installed into Claude Code settings');
|
|
577
|
+
} else {
|
|
578
|
+
console.log('SLM: hooks already current — skipped');
|
|
579
|
+
}
|
|
580
|
+
} catch (e) {
|
|
581
|
+
console.log('SLM: hooks install note: ' + (e.stderr || e.message || String(e)).trim().split('\n')[0]);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
505
585
|
function printFirstRunChecklist(config) {
|
|
506
586
|
console.log('');
|
|
507
587
|
console.log('SuperLocalMemory is configured.');
|
|
@@ -692,6 +772,13 @@ async function main() {
|
|
|
692
772
|
|
|
693
773
|
// UX-G2: show the one-screen delta banner so upgraders see what shipped.
|
|
694
774
|
printLivingBrainDelta();
|
|
775
|
+
|
|
776
|
+
// T1-B: Auto-install Claude Code plugin + hooks. Best-effort, non-blocking.
|
|
777
|
+
// Only runs on npm install (not --dry-run), when claude CLI is present.
|
|
778
|
+
if (!args.dryRun) {
|
|
779
|
+
await tryInstallClaudePlugin();
|
|
780
|
+
}
|
|
781
|
+
|
|
695
782
|
printFirstRunChecklist(config);
|
|
696
783
|
return 0;
|
|
697
784
|
}
|
|
@@ -32,7 +32,7 @@ if "OMP_NUM_THREADS" not in os.environ:
|
|
|
32
32
|
os.environ["OMP_NUM_THREADS"] = "2"
|
|
33
33
|
# ---------------------------------------------------------------------------
|
|
34
34
|
|
|
35
|
-
__version__ = "3.6.
|
|
35
|
+
__version__ = "3.6.18"
|
|
36
36
|
|
|
37
37
|
_REQUIRED_VERSIONS = {
|
|
38
38
|
"sentence_transformers": "5.3.0",
|
|
@@ -789,6 +789,40 @@ def _maybe_install_hooks_on_first_use() -> None:
|
|
|
789
789
|
# Best-effort: parity-fallback, never block CLI.
|
|
790
790
|
pass
|
|
791
791
|
|
|
792
|
+
# T1-B: Auto-install Claude Code plugin (skills, agents, hooks).
|
|
793
|
+
# Runs after hooks install, best-effort — never blocks CLI startup.
|
|
794
|
+
_try_install_claude_plugin()
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
def _try_install_claude_plugin() -> None:
|
|
798
|
+
"""Auto-install the Claude Code plugin on first pip/uvx SLM install.
|
|
799
|
+
|
|
800
|
+
Runs ``claude plugin marketplace add qualixar/superlocalmemory`` then
|
|
801
|
+
``claude plugin install superlocalmemory@qualixar`` if the ``claude``
|
|
802
|
+
CLI is found in PATH. Silent on any error — plugin install is a
|
|
803
|
+
convenience, not a hard requirement for SLM to function.
|
|
804
|
+
"""
|
|
805
|
+
import shutil
|
|
806
|
+
import subprocess
|
|
807
|
+
|
|
808
|
+
claude = shutil.which("claude")
|
|
809
|
+
if not claude:
|
|
810
|
+
return # Claude Code not in PATH — skip silently
|
|
811
|
+
|
|
812
|
+
_run = lambda cmd: subprocess.run( # noqa: E731
|
|
813
|
+
cmd, capture_output=True, timeout=30, check=False
|
|
814
|
+
)
|
|
815
|
+
|
|
816
|
+
try:
|
|
817
|
+
_run([claude, "plugin", "marketplace", "add", "qualixar/superlocalmemory"])
|
|
818
|
+
except Exception:
|
|
819
|
+
pass
|
|
820
|
+
|
|
821
|
+
try:
|
|
822
|
+
_run([claude, "plugin", "install", "superlocalmemory@qualixar"])
|
|
823
|
+
except Exception:
|
|
824
|
+
pass
|
|
825
|
+
|
|
792
826
|
|
|
793
827
|
# ---------------------------------------------------------------------------
|
|
794
828
|
# Mode C provider config (preserved from original)
|
|
@@ -473,6 +473,11 @@ class EmbeddingService:
|
|
|
473
473
|
"TOKENIZERS_PARALLELISM": "false",
|
|
474
474
|
"TORCH_DEVICE": "cpu",
|
|
475
475
|
"ORT_DISABLE_COREML": "1",
|
|
476
|
+
# Restore parallel OpenMP. The package caps OMP_NUM_THREADS
|
|
477
|
+
# globally to avoid a torch+lightgbm libomp SIGSEGV in the
|
|
478
|
+
# main process. This worker loads torch but never lightgbm,
|
|
479
|
+
# so there is no collision risk and full parallelism is safe.
|
|
480
|
+
"OMP_NUM_THREADS": str(os.cpu_count() or 4),
|
|
476
481
|
}
|
|
477
482
|
from superlocalmemory.core.platform_utils import popen_platform_kwargs
|
|
478
483
|
self._worker_proc = subprocess.Popen(
|
|
@@ -111,6 +111,8 @@ class FisherLangevinCoupling:
|
|
|
111
111
|
CouplingState with derived temperature, direction, and weight.
|
|
112
112
|
"""
|
|
113
113
|
var_arr = np.asarray(fisher_variance, dtype=np.float64)
|
|
114
|
+
if var_arr.size == 0:
|
|
115
|
+
return CouplingState()
|
|
114
116
|
|
|
115
117
|
# Step 1: Fisher confidence from variance
|
|
116
118
|
# Low variance = high confidence (memory is well-characterized)
|
|
@@ -203,6 +205,8 @@ class FisherLangevinCoupling:
|
|
|
203
205
|
return self._base_temp
|
|
204
206
|
|
|
205
207
|
var_arr = np.asarray(fisher_variance, dtype=np.float64)
|
|
208
|
+
if var_arr.size == 0:
|
|
209
|
+
return self._base_temp
|
|
206
210
|
avg_var = float(np.mean(np.clip(var_arr, 1e-8, None)))
|
|
207
211
|
fisher_conf = min(1.0, 1.0 / (1.0 + avg_var) + min(access_count * 0.02, 0.2))
|
|
208
212
|
return self._base_temp / (fisher_conf + self._epsilon)
|
|
@@ -217,9 +217,16 @@ def atomic_write(
|
|
|
217
217
|
prev = sync_log_last_content_sha256(sync_log_db, adapter_name, target_sha)
|
|
218
218
|
|
|
219
219
|
if prev == new_hash and resolved_path.exists():
|
|
220
|
-
# Durable skip
|
|
221
|
-
#
|
|
222
|
-
|
|
220
|
+
# Durable skip only if the on-disk content also matches the new hash.
|
|
221
|
+
# The sync-log row alone is not authoritative: the file may have been
|
|
222
|
+
# mutated out-of-band (e.g. ``git restore``, manual edit) since the
|
|
223
|
+
# last sync. Re-hash the file and re-write if it diverges.
|
|
224
|
+
try:
|
|
225
|
+
disk_hash = hashlib.sha256(resolved_path.read_bytes()).hexdigest()
|
|
226
|
+
except OSError:
|
|
227
|
+
disk_hash = None
|
|
228
|
+
if disk_hash == new_hash:
|
|
229
|
+
return WriteResult(wrote=False, bytes_written=0, content_sha256=new_hash)
|
|
223
230
|
|
|
224
231
|
resolved_path.parent.mkdir(parents=True, exist_ok=True)
|
|
225
232
|
tmp = resolved_path.with_suffix(resolved_path.suffix + ".slm-tmp")
|
|
@@ -21,6 +21,7 @@ from __future__ import annotations
|
|
|
21
21
|
|
|
22
22
|
import json
|
|
23
23
|
import logging
|
|
24
|
+
import os
|
|
24
25
|
import sys
|
|
25
26
|
import tempfile
|
|
26
27
|
from pathlib import Path
|
|
@@ -31,7 +32,7 @@ CLAUDE_SETTINGS = Path.home() / ".claude" / "settings.json"
|
|
|
31
32
|
VERSION_DIR = Path.home() / ".superlocalmemory" / "hooks"
|
|
32
33
|
VERSION_FILE = VERSION_DIR / ".version"
|
|
33
34
|
DISABLED_FILE = VERSION_DIR / ".hooks-disabled"
|
|
34
|
-
HOOKS_VERSION = "3.
|
|
35
|
+
HOOKS_VERSION = "3.6.18"
|
|
35
36
|
|
|
36
37
|
# Cross-platform temp dir and marker paths
|
|
37
38
|
_TMP = tempfile.gettempdir()
|
|
@@ -92,6 +93,19 @@ def _hook_definitions(include_gate: bool = False) -> dict[str, list]:
|
|
|
92
93
|
"""
|
|
93
94
|
defs: dict[str, list] = {
|
|
94
95
|
"SessionStart": [
|
|
96
|
+
# v3.6.18: mandate fires FIRST so session_init is never skipped.
|
|
97
|
+
# mcp__superlocalmemory__session_init is DEFERRED — Claude must call
|
|
98
|
+
# ToolSearch before it can invoke the tool. Without this hook,
|
|
99
|
+
# Claude responds before loading the schema → no 6-channel memory.
|
|
100
|
+
{
|
|
101
|
+
"hooks": [
|
|
102
|
+
{
|
|
103
|
+
"type": "command",
|
|
104
|
+
"command": _wrap_python_cmd("mandate"),
|
|
105
|
+
"timeout": 5000,
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
},
|
|
95
109
|
{
|
|
96
110
|
"hooks": [
|
|
97
111
|
{
|
|
@@ -100,7 +114,7 @@ def _hook_definitions(include_gate: bool = False) -> dict[str, list]:
|
|
|
100
114
|
"timeout": 15000,
|
|
101
115
|
}
|
|
102
116
|
]
|
|
103
|
-
}
|
|
117
|
+
},
|
|
104
118
|
],
|
|
105
119
|
"PostToolUse": [
|
|
106
120
|
{
|
|
@@ -278,9 +292,31 @@ def _read_settings() -> dict:
|
|
|
278
292
|
|
|
279
293
|
|
|
280
294
|
def _write_settings(settings: dict) -> None:
|
|
281
|
-
"""Write settings.json
|
|
295
|
+
"""Write settings.json atomically — tmp file + rename.
|
|
296
|
+
|
|
297
|
+
Direct .write_text() would truncate the file on a crash mid-write,
|
|
298
|
+
destroying the user's entire Claude Code configuration. The tmp-then-rename
|
|
299
|
+
pattern is atomic on POSIX (os.replace) and near-atomic on Windows: either
|
|
300
|
+
the full new content lands or the original file is untouched.
|
|
301
|
+
|
|
302
|
+
Never overwrites non-SLM settings — _merge_hooks() guarantees that only
|
|
303
|
+
the SLM hooks entries change; all other keys are preserved from the read.
|
|
304
|
+
"""
|
|
282
305
|
CLAUDE_SETTINGS.parent.mkdir(parents=True, exist_ok=True)
|
|
283
|
-
|
|
306
|
+
content = json.dumps(settings, indent=2) + "\n"
|
|
307
|
+
# Write to a sibling tmp file in the same directory so os.replace is atomic
|
|
308
|
+
# (cross-device rename would fail; same-dir rename is guaranteed atomic).
|
|
309
|
+
tmp_path = CLAUDE_SETTINGS.with_suffix(".json.slm_tmp")
|
|
310
|
+
try:
|
|
311
|
+
tmp_path.write_text(content, encoding="utf-8")
|
|
312
|
+
os.replace(tmp_path, CLAUDE_SETTINGS)
|
|
313
|
+
except Exception:
|
|
314
|
+
# Best-effort cleanup of the tmp file on failure
|
|
315
|
+
try:
|
|
316
|
+
tmp_path.unlink(missing_ok=True)
|
|
317
|
+
except Exception:
|
|
318
|
+
pass
|
|
319
|
+
raise
|
|
284
320
|
|
|
285
321
|
|
|
286
322
|
# ---------------------------------------------------------------------------
|