brainpick 0.3.2__tar.gz → 0.4.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.
- {brainpick-0.3.2 → brainpick-0.4.0}/PKG-INFO +1 -1
- {brainpick-0.3.2 → brainpick-0.4.0}/pyproject.toml +1 -1
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/__init__.py +1 -1
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/cli.py +1 -1
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/integrate.py +21 -2
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/mcp_server.py +34 -1
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/scaffold.py +32 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick.egg-info/PKG-INFO +1 -1
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_e2e_serve.py +1 -1
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_integrate.py +15 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_mcp_tools.py +16 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/README.md +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/setup.cfg +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/__main__.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_skill/SKILL.md +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/assets/Editor-x4G_0KV0.js +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/assets/index-CTcBQZsl.js +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/assets/index-Dn6TTKMy.css +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/assets/virtual_pwa-register-Cb81Y-Xh.js +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/assets/workbox-window.prod.es5-BBnX5xw4.js +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/assets/worker-CVB4_RMu.js +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/icons/apple-touch-icon.png +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/icons/favicon.svg +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/icons/icon-192.png +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/icons/icon-512.png +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/icons/icon-maskable-512.png +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/index.html +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/manifest.webmanifest +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/sw.js +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/workbox-a3c94b52.js +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/auth.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/compile/__init__.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/compile/pipeline.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/compile/similarity_gaps.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/compile/t1.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/compile/t2.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/compile/t3.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/config.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/core/__init__.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/core/bundle.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/core/canonical.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/core/frontmatter.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/core/fs.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/core/links.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/deltas.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/detect.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/embed.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/federation.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/kg.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/kgadapt/__init__.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/kgadapt/algorithmic.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/kgadapt/protocol.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/llm.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/merge.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/query/__init__.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/query/keyword.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/query/present.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/query/router.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/query/vectors.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/serve/__init__.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/serve/app.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/serve/live.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/serve/rest.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/serve/state.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/serve/watcher.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/timeline.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/vectorstore.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick.egg-info/SOURCES.txt +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick.egg-info/dependency_links.txt +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick.egg-info/entry_points.txt +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick.egg-info/requires.txt +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick.egg-info/top_level.txt +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_auth.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_bundle.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_cli.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_config.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_conformance.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_deltas.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_detect.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_e2e_mcp.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_federation.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_frontmatter.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_keyword.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_kg.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_links.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_llm.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_merge.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_pipeline.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_present.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_report.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_router.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_scaffold.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_serve_state.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_similarity_gaps.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_similarity_gaps_stage.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_t1.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_t2_chunker.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_t2_embed.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_t2_stage.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_t2_store.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_t3_algorithmic.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_t3_backend.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_t3_export.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_t3_stage.py +0 -0
- {brainpick-0.3.2 → brainpick-0.4.0}/tests/test_timeline.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "brainpick"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "A turn-key brain stack for agents: compile an OKF markdown bundle into a queryable, servable, visualizable brain."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -500,7 +500,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
500
500
|
|
|
501
501
|
p_integrate = sub.add_parser("integrate", help="install brainpick into an agent harness (skill, MCP, report)")
|
|
502
502
|
p_integrate.add_argument("target", metavar="<target>",
|
|
503
|
-
help="the harness to wire up: claude-code | opencode | agents-md")
|
|
503
|
+
help="the harness to wire up: claude-code | opencode | agents-md | dsh")
|
|
504
504
|
p_integrate.add_argument("--root", default=".", help="bundle root (default: current directory)")
|
|
505
505
|
p_integrate.add_argument("--dry-run", action="store_true",
|
|
506
506
|
help="print what integrate would do without writing anything")
|
|
@@ -9,6 +9,9 @@ Three targets, one family voice (mirrors henxels' `integrate`):
|
|
|
9
9
|
opencode.json MCP snippet.
|
|
10
10
|
- `agents-md` — ensure an AGENTS.md exists (the one place integrate may create a
|
|
11
11
|
file), install the brain-report markers if absent, and compile so the block fills.
|
|
12
|
+
- `dsh` — write the Agent Skill (the `.claude/skills` convention dsh shares),
|
|
13
|
+
then PRINT the `cordis.patch.yml` insert that mounts brainpick through
|
|
14
|
+
`@deepseek-ai/dsh-mcp-client` (dsh has no `claude mcp add`; a server is a config row).
|
|
12
15
|
|
|
13
16
|
The shipped Agent Skill (integrations/skill/SKILL.md, canonical) rides inside the
|
|
14
17
|
package; the parity test asserts the shipped copy is byte-identical to the canonical.
|
|
@@ -22,15 +25,17 @@ from pathlib import Path
|
|
|
22
25
|
from brainpick.compile.pipeline import run_compile
|
|
23
26
|
from brainpick.compile.t1 import REPORT_BEGIN_PREFIX, REPORT_END_MARKER
|
|
24
27
|
from brainpick.detect import find_repo_root
|
|
25
|
-
from brainpick.scaffold import _Voice, mcp_snippets
|
|
28
|
+
from brainpick.scaffold import _Voice, dsh_snippet, mcp_snippets
|
|
26
29
|
|
|
27
|
-
TARGETS = ("claude-code", "opencode", "agents-md")
|
|
30
|
+
TARGETS = ("claude-code", "opencode", "agents-md", "dsh")
|
|
28
31
|
HENXELS_BEGIN = "<!-- henxels:begin -->"
|
|
29
32
|
|
|
30
33
|
# harness -> where its Agent Skill lands, relative to the repo root
|
|
31
34
|
SKILL_DESTINATIONS = {
|
|
32
35
|
"claude-code": Path(".claude") / "skills" / "brainpick" / "SKILL.md",
|
|
33
36
|
"opencode": Path(".opencode") / "skills" / "brainpick" / "SKILL.md",
|
|
37
|
+
# dsh loads Agent Skills from the same .claude/skills convention as Claude Code.
|
|
38
|
+
"dsh": Path(".claude") / "skills" / "brainpick" / "SKILL.md",
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
_MINIMAL_AGENTS = "# AGENTS.md\n\nWorking notes for agents in this repository.\n"
|
|
@@ -128,6 +133,18 @@ def _integrate_opencode(voice: _Voice, root: Path, repo: Path, dry_run: bool) ->
|
|
|
128
133
|
return 0
|
|
129
134
|
|
|
130
135
|
|
|
136
|
+
def _integrate_dsh(voice: _Voice, root: Path, repo: Path, dry_run: bool) -> int:
|
|
137
|
+
dest, existed = _write_skill(repo, "dsh", dry_run)
|
|
138
|
+
verb = "would write" if dry_run else ("updated" if existed else "wrote")
|
|
139
|
+
voice.line("✓", f"skill: {verb} {dest}")
|
|
140
|
+
if dry_run:
|
|
141
|
+
voice.step("• print the cordis.patch.yml insert for @deepseek-ai/dsh-mcp-client")
|
|
142
|
+
return 0
|
|
143
|
+
voice.raw()
|
|
144
|
+
voice.raw(dsh_snippet(root))
|
|
145
|
+
return 0
|
|
146
|
+
|
|
147
|
+
|
|
131
148
|
def _integrate_agents_md(voice: _Voice, root: Path, repo: Path, dry_run: bool) -> int:
|
|
132
149
|
agents = repo / "AGENTS.md"
|
|
133
150
|
existed = agents.is_file()
|
|
@@ -177,4 +194,6 @@ def run_integrate(target: str, root: str | Path, dry_run: bool = False) -> int:
|
|
|
177
194
|
return _integrate_claude_code(voice, root, repo, dry_run)
|
|
178
195
|
if target == "opencode":
|
|
179
196
|
return _integrate_opencode(voice, root, repo, dry_run)
|
|
197
|
+
if target == "dsh":
|
|
198
|
+
return _integrate_dsh(voice, root, repo, dry_run)
|
|
180
199
|
return _integrate_agents_md(voice, root, repo, dry_run)
|
|
@@ -26,6 +26,7 @@ from brainpick.federation import (
|
|
|
26
26
|
)
|
|
27
27
|
from brainpick.llm import make_chat
|
|
28
28
|
from brainpick.merge import find_base, resolve
|
|
29
|
+
from brainpick.query.keyword import tokenize
|
|
29
30
|
from brainpick.query.router import KNOWN_MODES, run_search
|
|
30
31
|
from brainpick.serve.state import ServeState, bfs_neighborhood, jsonable, resolve_doc
|
|
31
32
|
from brainpick.serve.watcher import recompile_and_broadcast
|
|
@@ -106,6 +107,25 @@ def _single_overview(state: ServeState, budget_tokens: int | None = None) -> dic
|
|
|
106
107
|
# -- brain_search ------------------------------------------------------------------
|
|
107
108
|
|
|
108
109
|
|
|
110
|
+
def _matched_terms(query: str, *fields: str) -> list[str]:
|
|
111
|
+
"""The query tokens that actually occur in the given fields, in query order,
|
|
112
|
+
de-duplicated. Uses the same tokenizer keyword search uses, so 'why' reports
|
|
113
|
+
what really matched instead of echoing the whole query string (issue #2)."""
|
|
114
|
+
field_tokens = set()
|
|
115
|
+
for field in fields:
|
|
116
|
+
if field:
|
|
117
|
+
field_tokens.update(tokenize(field))
|
|
118
|
+
seen: dict[str, None] = {}
|
|
119
|
+
for token in tokenize(query):
|
|
120
|
+
if token in field_tokens and token not in seen:
|
|
121
|
+
seen[token] = None
|
|
122
|
+
return list(seen)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _quote_terms(terms: list[str]) -> str:
|
|
126
|
+
return ", ".join(f"'{t}'" for t in terms)
|
|
127
|
+
|
|
128
|
+
|
|
109
129
|
def _why(hit: dict, query: str) -> str:
|
|
110
130
|
lowered = query.lower()
|
|
111
131
|
if lowered in str(hit["title"]).lower():
|
|
@@ -116,7 +136,20 @@ def _why(hit: dict, query: str) -> str:
|
|
|
116
136
|
return f"semantically close to '{query}'"
|
|
117
137
|
if hit.get("source") == "graph":
|
|
118
138
|
return f"connected in the entity graph to '{query}'"
|
|
119
|
-
|
|
139
|
+
# Keyword-ish hit: name the query tokens that actually occur, and where, rather
|
|
140
|
+
# than claiming the whole query appears verbatim (issue #2).
|
|
141
|
+
total = len(dict.fromkeys(tokenize(query)))
|
|
142
|
+
title_terms = _matched_terms(query, str(hit.get("title") or ""))
|
|
143
|
+
desc_terms = _matched_terms(query, hit.get("description") or "")
|
|
144
|
+
body_terms = _matched_terms(query, hit.get("snippet") or "")
|
|
145
|
+
if title_terms:
|
|
146
|
+
return f"title mentions {_quote_terms(title_terms)}"
|
|
147
|
+
if desc_terms:
|
|
148
|
+
return f"description mentions {_quote_terms(desc_terms)}"
|
|
149
|
+
if body_terms:
|
|
150
|
+
suffix = f" ({len(body_terms)}/{total} terms)" if total > 1 else ""
|
|
151
|
+
return f"body mentions {_quote_terms(body_terms)}{suffix}"
|
|
152
|
+
return "keyword match"
|
|
120
153
|
|
|
121
154
|
|
|
122
155
|
def _single_search(state: ServeState, query: str, mode: str = "auto", limit: int = 8,
|
|
@@ -212,6 +212,38 @@ def mcp_snippets(bundle: Path) -> str:
|
|
|
212
212
|
return "\n".join(parts)
|
|
213
213
|
|
|
214
214
|
|
|
215
|
+
def dsh_snippet(bundle: Path) -> str:
|
|
216
|
+
"""The DeepSeek Harness (dsh) wiring: an insert entry for the web profile's
|
|
217
|
+
`cordis.patch.yml` that mounts brainpick through `@deepseek-ai/dsh-mcp-client`
|
|
218
|
+
as a stdio server. dsh has no `claude mcp add` equivalent — a server is a
|
|
219
|
+
config row, so we print the exact row with the resolved absolute root."""
|
|
220
|
+
command = brainpick_command() + ["mcp", "--root", str(bundle)]
|
|
221
|
+
entry = (
|
|
222
|
+
" - id: mcp-brainpick\n"
|
|
223
|
+
" name: '@deepseek-ai/dsh-mcp-client'\n"
|
|
224
|
+
" config:\n"
|
|
225
|
+
" serverName: brainpick\n"
|
|
226
|
+
" transport: stdio\n"
|
|
227
|
+
f" command: {command[0]}\n"
|
|
228
|
+
f" args: {json.dumps(command[1:])}\n"
|
|
229
|
+
" failOnStartupError: false"
|
|
230
|
+
)
|
|
231
|
+
parts = [
|
|
232
|
+
"Wire brainpick into DeepSeek Harness (dsh):",
|
|
233
|
+
"",
|
|
234
|
+
" Add this insert entry to your web profile's cordis.patch.yml",
|
|
235
|
+
" (~/.dsh/profiles/web/cordis.patch.yml), then restart the service",
|
|
236
|
+
" (e.g. systemctl --user restart dsh.service) and hard-refresh:",
|
|
237
|
+
"",
|
|
238
|
+
" - insert:",
|
|
239
|
+
entry,
|
|
240
|
+
"",
|
|
241
|
+
" Tools then surface as mcp__brainpick__brain_* — brain_overview first,",
|
|
242
|
+
" then brain_search / brain_read / brain_neighbors.",
|
|
243
|
+
]
|
|
244
|
+
return "\n".join(parts)
|
|
245
|
+
|
|
246
|
+
|
|
215
247
|
def henxels_fragment(contract: Path, bundle: Path) -> str:
|
|
216
248
|
"""The freshness gate, paste-able into an existing contract — never applied for you."""
|
|
217
249
|
root = os.path.relpath(bundle, contract.parent)
|
|
@@ -107,7 +107,7 @@ def test_sse_frame_format():
|
|
|
107
107
|
def test_health(kotiaurinko):
|
|
108
108
|
with TestClient(make_app(kotiaurinko)) as client:
|
|
109
109
|
assert client.get("/api/health").json() == {
|
|
110
|
-
"impl": "python", "name": "brainpick", "spec_version": "0.1", "version": "0.
|
|
110
|
+
"impl": "python", "name": "brainpick", "spec_version": "0.1", "version": "0.4.0",
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
|
|
@@ -53,10 +53,25 @@ def test_integrate_opencode_writes_skill_under_its_convention(repo, capsys):
|
|
|
53
53
|
assert "opencode" in capsys.readouterr().out.lower()
|
|
54
54
|
|
|
55
55
|
|
|
56
|
+
def test_integrate_dsh_writes_skill_and_prints_cordis_insert(repo, capsys):
|
|
57
|
+
root, bundle = repo
|
|
58
|
+
assert run_integrate("dsh", bundle) == 0
|
|
59
|
+
skill = root / SKILL_DESTINATIONS["dsh"]
|
|
60
|
+
assert skill.read_text(encoding="utf-8") == CANONICAL.read_text(encoding="utf-8")
|
|
61
|
+
out = capsys.readouterr().out
|
|
62
|
+
assert "@deepseek-ai/dsh-mcp-client" in out # the mcp-client bundle
|
|
63
|
+
assert "cordis.patch.yml" in out # where the row goes
|
|
64
|
+
assert "serverName: brainpick" in out
|
|
65
|
+
assert str(bundle) in out # resolved absolute root, not a stale path
|
|
66
|
+
assert "claude mcp add" not in out # dsh has no such command
|
|
67
|
+
|
|
68
|
+
|
|
56
69
|
def test_integrate_dry_run_is_inert(repo, capsys):
|
|
57
70
|
root, bundle = repo
|
|
58
71
|
assert run_integrate("claude-code", bundle, dry_run=True) == 0
|
|
59
72
|
assert not (root / ".claude").exists()
|
|
73
|
+
assert run_integrate("dsh", bundle, dry_run=True) == 0
|
|
74
|
+
assert not (root / ".claude").exists()
|
|
60
75
|
assert run_integrate("agents-md", bundle, dry_run=True) == 0
|
|
61
76
|
assert not (root / "AGENTS.md").exists()
|
|
62
77
|
|
|
@@ -102,6 +102,22 @@ def test_search_hits_have_why_not_bodies(kotiaurinko):
|
|
|
102
102
|
assert result["truncated"] is False
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
def test_search_why_names_matched_terms_not_whole_query(kotiaurinko):
|
|
106
|
+
"""issue #2: a multi-word query must not claim the WHOLE query appears in a
|
|
107
|
+
doc that only matched some of its terms. The 'why' names the tokens that
|
|
108
|
+
actually occur, never a term (like 'banana') that appears nowhere."""
|
|
109
|
+
result = search_payload(make_state(kotiaurinko), "aurinko komeetta banana")
|
|
110
|
+
assert result["hits"], "expected keyword hits for a partially-matching query"
|
|
111
|
+
for hit in result["hits"]:
|
|
112
|
+
why = hit["why"]
|
|
113
|
+
# never echo the full query, and never claim a non-existent term matched
|
|
114
|
+
assert "aurinko komeetta banana" not in why
|
|
115
|
+
assert "banana" not in why
|
|
116
|
+
# a keyword-ish 'mentions' reason must quote the actual matched token(s)
|
|
117
|
+
if "mentions" in why:
|
|
118
|
+
assert "aurinko" in why or "komeetta" in why
|
|
119
|
+
|
|
120
|
+
|
|
105
121
|
def test_search_budget_trims_hits(kotiaurinko):
|
|
106
122
|
state = make_state(kotiaurinko)
|
|
107
123
|
full = search_payload(state, "aurinko")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/assets/virtual_pwa-register-Cb81Y-Xh.js
RENAMED
|
File without changes
|
{brainpick-0.3.2 → brainpick-0.4.0}/src/brainpick/_static/assets/workbox-window.prod.es5-BBnX5xw4.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|