silica-core 0.7.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.
- silica_core-0.7.0/.agents/plugins/marketplace.json +20 -0
- silica_core-0.7.0/.claude-plugin/marketplace.json +14 -0
- silica_core-0.7.0/.claude-plugin/plugin.json +17 -0
- silica_core-0.7.0/.codex-plugin/plugin.json +22 -0
- silica_core-0.7.0/.dockerignore +15 -0
- silica_core-0.7.0/.env.example +77 -0
- silica_core-0.7.0/.github/FUNDING.yml +4 -0
- silica_core-0.7.0/.github/ISSUE_TEMPLATE/bug_report.yml +26 -0
- silica_core-0.7.0/.github/ISSUE_TEMPLATE/config.yml +15 -0
- silica_core-0.7.0/.github/ISSUE_TEMPLATE/feature_request.yml +18 -0
- silica_core-0.7.0/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- silica_core-0.7.0/.github/dependabot.yml +31 -0
- silica_core-0.7.0/.github/workflows/ci.yml +83 -0
- silica_core-0.7.0/.github/workflows/release-shim.yml +67 -0
- silica_core-0.7.0/.github/workflows/release.yml +87 -0
- silica_core-0.7.0/.gitignore +126 -0
- silica_core-0.7.0/CODE_OF_CONDUCT.md +133 -0
- silica_core-0.7.0/CONTRIBUTING.md +48 -0
- silica_core-0.7.0/Dockerfile +35 -0
- silica_core-0.7.0/LICENSE +661 -0
- silica_core-0.7.0/LICENSE-COMMERCIAL.md +62 -0
- silica_core-0.7.0/PKG-INFO +220 -0
- silica_core-0.7.0/README.md +173 -0
- silica_core-0.7.0/SECURITY.md +34 -0
- silica_core-0.7.0/TOOLS.md +208 -0
- silica_core-0.7.0/assets/banner-light.svg +13 -0
- silica_core-0.7.0/assets/banner.svg +13 -0
- silica_core-0.7.0/assets/build_banners.py +231 -0
- silica_core-0.7.0/assets/sili_mascot.png +0 -0
- silica_core-0.7.0/assets/silica-mark-favicon.svg +1 -0
- silica_core-0.7.0/assets/silica-mark.svg +1 -0
- silica_core-0.7.0/cliff.toml +46 -0
- silica_core-0.7.0/mcp.codex.json +8 -0
- silica_core-0.7.0/mcp.json +16 -0
- silica_core-0.7.0/packaging/silica-agent/README.md +13 -0
- silica_core-0.7.0/packaging/silica-agent/pyproject.toml +53 -0
- silica_core-0.7.0/packaging/silica-harness/README.md +13 -0
- silica_core-0.7.0/packaging/silica-harness/pyproject.toml +52 -0
- silica_core-0.7.0/pyproject.toml +138 -0
- silica_core-0.7.0/setup.cfg +4 -0
- silica_core-0.7.0/silica/.env.example +77 -0
- silica_core-0.7.0/silica/__init__.py +39 -0
- silica_core-0.7.0/silica/_version.py +24 -0
- silica_core-0.7.0/silica/cli.py +190 -0
- silica_core-0.7.0/silica/config.py +113 -0
- silica_core-0.7.0/silica/core.py +728 -0
- silica_core-0.7.0/silica/driver/__init__.py +127 -0
- silica_core-0.7.0/silica/driver/base.py +347 -0
- silica_core-0.7.0/silica/driver/fs_backend.py +1198 -0
- silica_core-0.7.0/silica/driver/ws_backend.py +582 -0
- silica_core-0.7.0/silica/embeddings.py +98 -0
- silica_core-0.7.0/silica/entry.py +10 -0
- silica_core-0.7.0/silica/kernel/__init__.py +9 -0
- silica_core-0.7.0/silica/kernel/code/__init__.py +4 -0
- silica_core-0.7.0/silica/kernel/code/codeast/__init__.py +31 -0
- silica_core-0.7.0/silica/kernel/code/codeast/base.py +230 -0
- silica_core-0.7.0/silica/kernel/code/codeast/c.py +215 -0
- silica_core-0.7.0/silica/kernel/code/codeast/java.py +190 -0
- silica_core-0.7.0/silica/kernel/code/codeast/python.py +317 -0
- silica_core-0.7.0/silica/kernel/code/codeast/ts.py +262 -0
- silica_core-0.7.0/silica/kernel/code/codegraph.py +516 -0
- silica_core-0.7.0/silica/kernel/code/codepack.py +510 -0
- silica_core-0.7.0/silica/kernel/code/gitstate.py +310 -0
- silica_core-0.7.0/silica/kernel/code/ipynb.py +71 -0
- silica_core-0.7.0/silica/kernel/link/__init__.py +4 -0
- silica_core-0.7.0/silica/kernel/link/ast.py +400 -0
- silica_core-0.7.0/silica/kernel/link/ofm.py +159 -0
- silica_core-0.7.0/silica/kernel/recall/__init__.py +4 -0
- silica_core-0.7.0/silica/kernel/recall/lexical.py +326 -0
- silica_core-0.7.0/silica/kernel/recall/paths.py +865 -0
- silica_core-0.7.0/silica/kernel/recall/rerank.py +140 -0
- silica_core-0.7.0/silica/kernel/text/__init__.py +4 -0
- silica_core-0.7.0/silica/kernel/text/language.py +111 -0
- silica_core-0.7.0/silica/kernel/text/media.py +148 -0
- silica_core-0.7.0/silica/kernel/text/payload.py +160 -0
- silica_core-0.7.0/silica/kernel/text/sanitize.py +431 -0
- silica_core-0.7.0/silica/kernel/text/text.py +186 -0
- silica_core-0.7.0/silica/kernel/vault_manifest.py +626 -0
- silica_core-0.7.0/silica/kernel/write/__init__.py +4 -0
- silica_core-0.7.0/silica/kernel/write/frontmatter.py +158 -0
- silica_core-0.7.0/silica/kernel/write/ops.py +220 -0
- silica_core-0.7.0/silica/onboarding/__init__.py +3 -0
- silica_core-0.7.0/silica/onboarding/adopt.py +135 -0
- silica_core-0.7.0/silica/onboarding/checks.py +123 -0
- silica_core-0.7.0/silica/onboarding/setup_client.py +301 -0
- silica_core-0.7.0/silica/repl.py +199 -0
- silica_core-0.7.0/silica/skills/silica/SKILL.md +58 -0
- silica_core-0.7.0/silica/sources/__init__.py +4 -0
- silica_core-0.7.0/silica/sources/convert.py +2374 -0
- silica_core-0.7.0/silica/tools/__init__.py +189 -0
- silica_core-0.7.0/silica/tools/atomic.py +40 -0
- silica_core-0.7.0/silica/tools/tabular.py +470 -0
- silica_core-0.7.0/silica/ui/__init__.py +4 -0
- silica_core-0.7.0/silica/ui/connect.py +273 -0
- silica_core-0.7.0/silica/ui/mcp.py +137 -0
- silica_core-0.7.0/silica/update.py +226 -0
- silica_core-0.7.0/silica_core.egg-info/PKG-INFO +220 -0
- silica_core-0.7.0/silica_core.egg-info/SOURCES.txt +168 -0
- silica_core-0.7.0/silica_core.egg-info/dependency_links.txt +1 -0
- silica_core-0.7.0/silica_core.egg-info/entry_points.txt +2 -0
- silica_core-0.7.0/silica_core.egg-info/requires.txt +32 -0
- silica_core-0.7.0/silica_core.egg-info/scm_file_list.json +163 -0
- silica_core-0.7.0/silica_core.egg-info/scm_version.json +8 -0
- silica_core-0.7.0/silica_core.egg-info/top_level.txt +1 -0
- silica_core-0.7.0/tests/__init__.py +1 -0
- silica_core-0.7.0/tests/conftest.py +93 -0
- silica_core-0.7.0/tests/doc_factory.py +259 -0
- silica_core-0.7.0/tests/fixtures/__init__.py +1 -0
- silica_core-0.7.0/tests/fixtures/vault_factory.py +293 -0
- silica_core-0.7.0/tests/golden/__init__.py +2 -0
- silica_core-0.7.0/tests/golden/autolink_cases.json +58 -0
- silica_core-0.7.0/tests/golden/collision_threshold_cases.json +26 -0
- silica_core-0.7.0/tests/golden/keyphrase_cases.json +72 -0
- silica_core-0.7.0/tests/link_cases.py +44 -0
- silica_core-0.7.0/tests/llm_mocks.py +58 -0
- silica_core-0.7.0/tests/test_adr_link_resolution.py +45 -0
- silica_core-0.7.0/tests/test_ast_balance.py +16 -0
- silica_core-0.7.0/tests/test_atomic_write_bytes.py +71 -0
- silica_core-0.7.0/tests/test_clean_tag.py +27 -0
- silica_core-0.7.0/tests/test_code_lane_guards.py +169 -0
- silica_core-0.7.0/tests/test_codeast.py +721 -0
- silica_core-0.7.0/tests/test_codegraph.py +398 -0
- silica_core-0.7.0/tests/test_config_home.py +50 -0
- silica_core-0.7.0/tests/test_config_stt.py +85 -0
- silica_core-0.7.0/tests/test_config_ws.py +21 -0
- silica_core-0.7.0/tests/test_convert.py +1908 -0
- silica_core-0.7.0/tests/test_convert_child_reaping.py +74 -0
- silica_core-0.7.0/tests/test_core_contract.py +148 -0
- silica_core-0.7.0/tests/test_dominance.py +33 -0
- silica_core-0.7.0/tests/test_dotenv_layering.py +100 -0
- silica_core-0.7.0/tests/test_episodic_boundary.py +81 -0
- silica_core-0.7.0/tests/test_frontmatter_split.py +59 -0
- silica_core-0.7.0/tests/test_gitstate.py +125 -0
- silica_core-0.7.0/tests/test_gitstate_codegraph.py +64 -0
- silica_core-0.7.0/tests/test_ipynb.py +53 -0
- silica_core-0.7.0/tests/test_json_salvage.py +78 -0
- silica_core-0.7.0/tests/test_language.py +134 -0
- silica_core-0.7.0/tests/test_mcp_surface.py +119 -0
- silica_core-0.7.0/tests/test_media.py +188 -0
- silica_core-0.7.0/tests/test_mineru_error.py +92 -0
- silica_core-0.7.0/tests/test_no_shadowed_imports.py +64 -0
- silica_core-0.7.0/tests/test_payload_gate.py +100 -0
- silica_core-0.7.0/tests/test_plugin_manifests.py +96 -0
- silica_core-0.7.0/tests/test_recall_windows.py +54 -0
- silica_core-0.7.0/tests/test_relatedness_boundary.py +83 -0
- silica_core-0.7.0/tests/test_repl.py +78 -0
- silica_core-0.7.0/tests/test_repo_root.py +77 -0
- silica_core-0.7.0/tests/test_retrieval_check.py +79 -0
- silica_core-0.7.0/tests/test_setup_client.py +187 -0
- silica_core-0.7.0/tests/test_silicaignore.py +64 -0
- silica_core-0.7.0/tests/test_smoke.py +158 -0
- silica_core-0.7.0/tests/test_tabular.py +264 -0
- silica_core-0.7.0/tests/test_target_dir_resolution.py +50 -0
- silica_core-0.7.0/tests/test_text_seam.py +89 -0
- silica_core-0.7.0/tests/test_tokenizer_atoms.py +76 -0
- silica_core-0.7.0/tests/test_tokenizer_folding.py +42 -0
- silica_core-0.7.0/tests/test_tool_collapse_field.py +37 -0
- silica_core-0.7.0/tests/test_tool_name_contract.py +98 -0
- silica_core-0.7.0/tests/test_update.py +210 -0
- silica_core-0.7.0/tests/test_user_env_precedence.py +83 -0
- silica_core-0.7.0/tests/test_vault_conventions.py +255 -0
- silica_core-0.7.0/tests/test_vault_factory.py +151 -0
- silica_core-0.7.0/tests/test_vault_manifest.py +183 -0
- silica_core-0.7.0/tests/test_vault_pin_source.py +37 -0
- silica_core-0.7.0/tests/test_wikilink.py +57 -0
- silica_core-0.7.0/tests/test_wikilink_fast_path.py +189 -0
- silica_core-0.7.0/tests/test_write_boundary_regression.py +39 -0
- silica_core-0.7.0/tests/webassets.py +44 -0
- silica_core-0.7.0/uv.lock +1578 -0
- silica_core-0.7.0/vault.yaml +1 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "silica-core",
|
|
3
|
+
"interface": {
|
|
4
|
+
"displayName": "Silica Core"
|
|
5
|
+
},
|
|
6
|
+
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"name": "silica-core",
|
|
9
|
+
"source": {
|
|
10
|
+
"source": "local",
|
|
11
|
+
"path": "./"
|
|
12
|
+
},
|
|
13
|
+
"policy": {
|
|
14
|
+
"installation": "AVAILABLE",
|
|
15
|
+
"authentication": "ON_INSTALL"
|
|
16
|
+
},
|
|
17
|
+
"category": "Productivity"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "silica-core",
|
|
3
|
+
"description": "Silica for Claude Code: retrieval tools over the folder you work in, no model in the loop.",
|
|
4
|
+
"owner": {
|
|
5
|
+
"name": "Alessandro Carosia"
|
|
6
|
+
},
|
|
7
|
+
"plugins": [
|
|
8
|
+
{
|
|
9
|
+
"name": "silica-core",
|
|
10
|
+
"source": "./",
|
|
11
|
+
"description": "files, search, read, code_pack, write_note over MCP; located evidence, never answers."
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "silica-core",
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"description": "Retrieval tools for coding agents, no model in the loop: files, search, read, code_pack and write_note over the folder the session is opened in, served over MCP, plus a skill that teaches when to search and how to read coverage.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Alessandro Carosia"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"retrieval",
|
|
10
|
+
"search",
|
|
11
|
+
"bm25",
|
|
12
|
+
"mcp",
|
|
13
|
+
"agents"
|
|
14
|
+
],
|
|
15
|
+
"skills": "./silica/skills",
|
|
16
|
+
"mcpServers": "./mcp.json"
|
|
17
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "silica-core",
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"description": "Retrieval tools for coding agents, no model in the loop: files, search, read, code_pack and write_note over the folder the session is opened in, served over MCP, plus a skill that teaches when to search and how to read coverage.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Alessandro Carosia"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"retrieval",
|
|
10
|
+
"search",
|
|
11
|
+
"bm25",
|
|
12
|
+
"mcp",
|
|
13
|
+
"agents"
|
|
14
|
+
],
|
|
15
|
+
"skills": "./silica/skills",
|
|
16
|
+
"mcpServers": "./mcp.codex.json",
|
|
17
|
+
"interface": {
|
|
18
|
+
"displayName": "Silica Core",
|
|
19
|
+
"shortDescription": "Located evidence from your folder: search, read, inventory, code packs. No model, no memory lane.",
|
|
20
|
+
"developerName": "Alessandro Carosia"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Allowlist, not a blocklist: the repo root holds .env, a 346 MB data/, dist/,
|
|
2
|
+
# .venv and five caches, and a blocklist chasing them is one `git add` away from
|
|
3
|
+
# leaking the next one into the build context.
|
|
4
|
+
*
|
|
5
|
+
!pyproject.toml
|
|
6
|
+
!README.md
|
|
7
|
+
!LICENSE
|
|
8
|
+
!LICENSE-COMMERCIAL.md
|
|
9
|
+
!silica/
|
|
10
|
+
# setuptools-scm reads it in the build stage to derive the version; without it
|
|
11
|
+
# the wheel installs as pyproject's fallback_version, 0.0.0, and the image can no
|
|
12
|
+
# longer say which build it is. Costs ~115 MB of context. If that matters more
|
|
13
|
+
# than the version does, drop this line and pass
|
|
14
|
+
# --build-arg SETUPTOOLS_SCM_PRETEND_VERSION_FOR_SILICA_CORE=$(git describe --tags)
|
|
15
|
+
!.git/
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Silica core: every variable the code reads. Copy to ~/.silica/.env, or export
|
|
2
|
+
# in the shell; an exported value wins over the file. Everything below is
|
|
3
|
+
# optional: with no file at all the five tools work, offline, on the folder
|
|
4
|
+
# the session is opened in.
|
|
5
|
+
|
|
6
|
+
# ---------------------------------------------------------------------------
|
|
7
|
+
# The root
|
|
8
|
+
# ---------------------------------------------------------------------------
|
|
9
|
+
# Silica serves the folder it is started in. SILICA_VAULT pins another root
|
|
10
|
+
# and is honoured ONLY when exported in the shell or passed as --vault: in
|
|
11
|
+
# this file it is ignored with a warning, because a pinned root belongs to
|
|
12
|
+
# the invocation, not to every session on the machine.
|
|
13
|
+
#
|
|
14
|
+
# export SILICA_VAULT=/path/to/folder
|
|
15
|
+
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
# The runtime directory
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# `~/.silica` holds this file, `index/`, `tmp/` and `inbox/`. SILICA_HOME
|
|
20
|
+
# points all four somewhere else and, like SILICA_VAULT, is read from the
|
|
21
|
+
# shell alone: the variable that says where the config lives cannot live in
|
|
22
|
+
# the config. Use it to run a second tree on one machine without either
|
|
23
|
+
# inheriting the other's model, keys or index.
|
|
24
|
+
#
|
|
25
|
+
# export SILICA_HOME=~/.silica-core
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# Converters (`silica import FILE`)
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# PDF text layer: pdfium (default, one wheel, no OCR), or a binary you install
|
|
31
|
+
# and Silica calls: mineru (the only OCR path), docling, opendataloader.
|
|
32
|
+
# SILICA_PDF_PROVIDER=pdfium
|
|
33
|
+
# Languages mineru's OCR is given, comma-separated.
|
|
34
|
+
# SILICA_PDF_OCR_LANG=en,it,fr,de,es
|
|
35
|
+
# Root-relative folder the converted .md notes are written to.
|
|
36
|
+
# SILICA_INBOX_DIR=Inbox
|
|
37
|
+
|
|
38
|
+
# Audio and video are transcribed, not parsed: ffmpeg demuxes, then either an
|
|
39
|
+
# OpenAI-compatible /v1/audio/transcriptions endpoint (`endpoint`, the
|
|
40
|
+
# default; LM Studio, a local whisper server, or a hosted key) or a local
|
|
41
|
+
# whisper.cpp binary (`whispercpp`).
|
|
42
|
+
# SILICA_STT_PROVIDER=endpoint
|
|
43
|
+
# SILICA_STT_BASE_URL=http://localhost:1236/v1
|
|
44
|
+
# SILICA_STT_MODEL=whisper-1
|
|
45
|
+
# SILICA_STT_API_KEY=lm-studio
|
|
46
|
+
# SILICA_STT_LANG=auto
|
|
47
|
+
# SILICA_STT_WHISPERCPP_BIN=/path/to/whisper-cli
|
|
48
|
+
# SILICA_STT_WHISPERCPP_MODEL=/path/to/ggml-model.bin
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
# Embeddings extension (`silica index --embed`, `silica search --hybrid`)
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
# Off until a base URL is set. Any OpenAI-compatible /v1/embeddings endpoint:
|
|
54
|
+
# LM Studio, llama-server, Ollama, or a hosted key. One vector per document.
|
|
55
|
+
# Not part of the core contract: BM25 alone is what TOOLS.md promises.
|
|
56
|
+
# SILICA_EMBEDDING_BASE_URL=http://localhost:1234/v1
|
|
57
|
+
# SILICA_EMBEDDING_MODEL=text-embedding-qwen3-embedding-4b
|
|
58
|
+
# SILICA_EMBEDDING_API_KEY=lm-studio
|
|
59
|
+
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
# REPL (`silica repl`), the one surface with a model in the loop
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
# "provider/model" with a preset provider, or a bare model id served by
|
|
64
|
+
# SILICA_PROVIDER_BASE_URL. Presets and the key each one reads:
|
|
65
|
+
# openrouter OPENROUTER_API_KEY openai OPENAI_API_KEY
|
|
66
|
+
# deepseek DEEPSEEK_API_KEY groq GROQ_API_KEY
|
|
67
|
+
# mistral MISTRAL_API_KEY xai XAI_API_KEY
|
|
68
|
+
# gemini GEMINI_API_KEY lmstudio, ollama: local, no key
|
|
69
|
+
# SILICA_MODEL=openrouter/deepseek/deepseek-chat
|
|
70
|
+
# SILICA_PROVIDER_BASE_URL=http://localhost:1234/v1
|
|
71
|
+
# SILICA_PROVIDER_API_KEY=
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# Obsidian bridge (`silica connect`, needs the [connect] extra)
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
# SILICA_WS_PORT=0
|
|
77
|
+
# SILICA_WS_TOKEN=
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something Silica does that it should not.
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened
|
|
9
|
+
description: What you ran, what you expected, what you got instead. A minimal reproduction helps most.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: input
|
|
13
|
+
id: version
|
|
14
|
+
attributes:
|
|
15
|
+
label: Version and how you installed it
|
|
16
|
+
placeholder: "0.7.0, uv tool install silica-core"
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: doctor
|
|
21
|
+
attributes:
|
|
22
|
+
label: silica doctor
|
|
23
|
+
description: Optional, and it answers most environment questions in one paste. It prints setting names, never their values.
|
|
24
|
+
render: text
|
|
25
|
+
validations:
|
|
26
|
+
required: false
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Questions and open-ended ideas belong in Discussions, security in the advisory
|
|
2
|
+
# flow: this file is what keeps them out of the tracker. blank_issues_enabled is
|
|
3
|
+
# false so every issue arrives through a form carrying a reproduction or a problem
|
|
4
|
+
# statement, because triage on a solo project is the scarce resource, not filing.
|
|
5
|
+
blank_issues_enabled: false
|
|
6
|
+
contact_links:
|
|
7
|
+
- name: Question, or something behaving in a way you did not expect
|
|
8
|
+
url: https://github.com/kiycoh/silica-core/discussions/categories/q-a
|
|
9
|
+
about: Ask in Q&A. Faster than an issue when you are not yet sure it is a bug.
|
|
10
|
+
- name: Idea, or a use case you want to talk through
|
|
11
|
+
url: https://github.com/kiycoh/silica-core/discussions/categories/ideas
|
|
12
|
+
about: For anything that is not yet a concrete feature request.
|
|
13
|
+
- name: Security vulnerability
|
|
14
|
+
url: https://github.com/kiycoh/silica-core/security/policy
|
|
15
|
+
about: Never open a public issue for a security problem. Read the policy first.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Something Silica should do and does not.
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: The problem
|
|
9
|
+
description: What you were doing when you hit the wall. Describe the situation, not the solution you already have in mind.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: proposal
|
|
14
|
+
attributes:
|
|
15
|
+
label: What you would want instead
|
|
16
|
+
description: Silica takes the smallest diff that works, so a proposal that reuses a path already in the codebase lands where one that adds a layer does not.
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## What and why
|
|
2
|
+
|
|
3
|
+
<!-- One paragraph: what changes, and what made the obvious alternative wrong. -->
|
|
4
|
+
|
|
5
|
+
## Checklist
|
|
6
|
+
|
|
7
|
+
- [ ] `uv run pytest` passes
|
|
8
|
+
- [ ] Non-trivial logic (a branch, a parser, a write path) leaves a runnable test behind
|
|
9
|
+
- [ ] No mutation reaches the vault outside the Injector FSM
|
|
10
|
+
- [ ] Code, comments and UI copy are English-only
|
|
11
|
+
- [ ] Conventional commits (`feat(scope): …`), one logical change each
|
|
12
|
+
|
|
13
|
+
By opening this PR you accept the license and relicensing terms in [CONTRIBUTING.md](https://github.com/kiycoh/silica-core/blob/main/CONTRIBUTING.md#license).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Dependabot version updates.
|
|
2
|
+
# Two ecosystems match this repo: uv-managed Python deps (pyproject.toml + uv.lock)
|
|
3
|
+
# and the pinned actions in .github/workflows/release.yml.
|
|
4
|
+
# Commit prefixes (build / ci) map to cliff.toml's "^build|^ci" -> Build & CI section.
|
|
5
|
+
version: 2
|
|
6
|
+
updates:
|
|
7
|
+
- package-ecosystem: "uv"
|
|
8
|
+
directory: "/"
|
|
9
|
+
schedule:
|
|
10
|
+
interval: "weekly"
|
|
11
|
+
commit-message:
|
|
12
|
+
prefix: "build"
|
|
13
|
+
include: "scope"
|
|
14
|
+
open-pull-requests-limit: 5
|
|
15
|
+
# Minor/patch bumps arrive as one grouped PR to cut solo-maintainer noise;
|
|
16
|
+
# majors stay individual so breaking changes are reviewed on their own.
|
|
17
|
+
groups:
|
|
18
|
+
python-minor-patch:
|
|
19
|
+
patterns: ["*"]
|
|
20
|
+
update-types: ["minor", "patch"]
|
|
21
|
+
|
|
22
|
+
- package-ecosystem: "github-actions"
|
|
23
|
+
directory: "/"
|
|
24
|
+
schedule:
|
|
25
|
+
interval: "weekly"
|
|
26
|
+
commit-message:
|
|
27
|
+
prefix: "ci"
|
|
28
|
+
include: "scope"
|
|
29
|
+
groups:
|
|
30
|
+
actions:
|
|
31
|
+
patterns: ["*"]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
# The gates CONTRIBUTING already asks for, run where they can actually block:
|
|
4
|
+
# every PR and every push to main. A checklist the author ticks by hand is not
|
|
5
|
+
# a gate, and dependabot's weekly PRs have nothing checking them without this.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
pull_request:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
# A superseded push on the same branch is dead work.
|
|
17
|
+
group: ci-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: true
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
test:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
strategy:
|
|
24
|
+
fail-fast: false
|
|
25
|
+
matrix:
|
|
26
|
+
# The two versions pyproject claims in its classifiers. A claim no job
|
|
27
|
+
# checks is how a package promises 3.12 and ships broken on it.
|
|
28
|
+
python-version: ["3.11", "3.12"]
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
31
|
+
|
|
32
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
33
|
+
with:
|
|
34
|
+
python-version: ${{ matrix.python-version }}
|
|
35
|
+
enable-cache: true
|
|
36
|
+
|
|
37
|
+
# --locked fails when uv.lock has drifted from pyproject.toml, so a
|
|
38
|
+
# dependency edited without a relock dies here and not at a user's
|
|
39
|
+
# install. Extras: exactly the ones the suite reaches for with
|
|
40
|
+
# importorskip (fastapi/uvicorn, websockets, mcp).
|
|
41
|
+
# [rerank] stays out: 2 GB of torch to run tests that skip. There is no
|
|
42
|
+
# [pdf] extra any more — the mineru subprocess is patched in the suite.
|
|
43
|
+
- run: uv sync --locked --extra dev --extra connect --extra mcp
|
|
44
|
+
|
|
45
|
+
- run: uv run pytest -q
|
|
46
|
+
|
|
47
|
+
# Both are seconds and both were green when this file landed
|
|
48
|
+
# (2026-08-22: 9/9 contracts kept, 0 mypy errors over 206 files). They
|
|
49
|
+
# are here because a contract nothing re-checks is a comment.
|
|
50
|
+
- run: uv run lint-imports
|
|
51
|
+
- run: uv run mypy silica
|
|
52
|
+
|
|
53
|
+
# Third of the seconds-long gates, added 2026-09-03 at 0 findings after a
|
|
54
|
+
# cleanup of 158 unused imports. Narrow by config (F, E9 in pyproject):
|
|
55
|
+
# it blocks dead code, it does not arbitrate style.
|
|
56
|
+
- run: uv run ruff check silica tests
|
|
57
|
+
|
|
58
|
+
# The Dockerfile landed unbuilt by anything and went stale in place: the
|
|
59
|
+
# rename to silica-core walked past a build arg in .dockerignore that no job
|
|
60
|
+
# was exercising. An image nobody builds is a promise that breaks in the hand
|
|
61
|
+
# of the first person who tries it.
|
|
62
|
+
docker:
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
steps:
|
|
65
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
66
|
+
with:
|
|
67
|
+
# .dockerignore ships .git so setuptools-scm can name the wheel; the
|
|
68
|
+
# default shallow checkout carries no tags and every image would
|
|
69
|
+
# install as pyproject's fallback_version, 0.0.0.
|
|
70
|
+
fetch-depth: 0
|
|
71
|
+
|
|
72
|
+
- run: docker build -t silica-core:ci .
|
|
73
|
+
|
|
74
|
+
# Building proves the wheel resolves; it does not prove the image runs.
|
|
75
|
+
# One vault, one file, one search covers the parts that actually rot: the
|
|
76
|
+
# [mcp] extra, the HOME=/data relocation the index writes through, the
|
|
77
|
+
# entrypoint, and the /vault working directory.
|
|
78
|
+
- name: smoke
|
|
79
|
+
run: |
|
|
80
|
+
mkdir -p vault
|
|
81
|
+
printf '# Compaction\n\nLSM trees merge sorted runs on write.\n' > vault/notes.md
|
|
82
|
+
docker run --rm -v "$PWD/vault:/vault" silica-core:ci search compaction -k 1 | tee out.json
|
|
83
|
+
grep -q notes.md out.json
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
name: release-shim
|
|
2
|
+
|
|
3
|
+
# Publishes one of the redirect distributions: `silica-agent` (the 2026-08-23
|
|
4
|
+
# rename) or `silica-harness` (the 2026-09-08 rename), both pointing at
|
|
5
|
+
# `silica-core` (see packaging/*/pyproject.toml for why the old names are kept
|
|
6
|
+
# alive rather than deleted).
|
|
7
|
+
#
|
|
8
|
+
# Manual, not tag-driven: a shim's job is to point at a moving target, so one
|
|
9
|
+
# release keeps working for every silica-core that follows it. Run this only
|
|
10
|
+
# when a shim's own metadata changes, which should be close to never.
|
|
11
|
+
on:
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
inputs:
|
|
14
|
+
shim:
|
|
15
|
+
description: which redirect to publish
|
|
16
|
+
required: true
|
|
17
|
+
type: choice
|
|
18
|
+
options: [silica-harness, silica-agent]
|
|
19
|
+
|
|
20
|
+
permissions: {}
|
|
21
|
+
|
|
22
|
+
concurrency:
|
|
23
|
+
group: release-shim-${{ inputs.shim }}
|
|
24
|
+
cancel-in-progress: false
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
publish:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
# Same environment as release.yml, so PyPI's trusted publisher for the shim
|
|
30
|
+
# narrows to it the same way. NOTE: that publisher must name THIS
|
|
31
|
+
# repository (kiycoh/silica-core) and this workflow file — a publisher
|
|
32
|
+
# configured before a rename points at the old repository name and cannot
|
|
33
|
+
# match the OIDC claim.
|
|
34
|
+
environment:
|
|
35
|
+
name: pypi
|
|
36
|
+
url: https://pypi.org/p/${{ inputs.shim }}
|
|
37
|
+
permissions:
|
|
38
|
+
id-token: write # OIDC for PyPI Trusted Publishing (no stored token)
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
41
|
+
|
|
42
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
43
|
+
with:
|
|
44
|
+
python-version: "3.11"
|
|
45
|
+
|
|
46
|
+
# rm for the same reason release.yml does it: uv build writes a
|
|
47
|
+
# .gitignore into the output directory, and packages-dir uploads what it
|
|
48
|
+
# finds there.
|
|
49
|
+
- name: Build the shim
|
|
50
|
+
run: uv build --out-dir "$GITHUB_WORKSPACE/shim-dist" && rm -f "$GITHUB_WORKSPACE/shim-dist/.gitignore"
|
|
51
|
+
working-directory: packaging/${{ inputs.shim }}
|
|
52
|
+
|
|
53
|
+
# The only thing this distribution does is resolve to another one and
|
|
54
|
+
# keep the `silica` command working, so that is what gets checked before
|
|
55
|
+
# the irreversible step. A shim that installs but leaves no executable is
|
|
56
|
+
# the failure mode worth catching: nothing in the metadata shows it.
|
|
57
|
+
- name: Verify the shim resolves and still carries the command
|
|
58
|
+
run: |
|
|
59
|
+
uv venv /tmp/shim-check
|
|
60
|
+
VIRTUAL_ENV=/tmp/shim-check uv pip install "$GITHUB_WORKSPACE"/shim-dist/*.whl
|
|
61
|
+
VIRTUAL_ENV=/tmp/shim-check uv pip show silica-core > /dev/null
|
|
62
|
+
/tmp/shim-check/bin/silica --version
|
|
63
|
+
|
|
64
|
+
- name: Publish to PyPI
|
|
65
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
66
|
+
with:
|
|
67
|
+
packages-dir: shim-dist
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Cut a release by pushing a SemVer tag, e.g.:
|
|
4
|
+
# git tag v0.3.0 && git push origin v0.3.0
|
|
5
|
+
# setuptools-scm reads the tag to stamp the version; git-cliff builds the notes.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags:
|
|
10
|
+
- "v*"
|
|
11
|
+
|
|
12
|
+
permissions: {} # the one job below asks for what it needs; nothing inherits
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
# Never cancel a publish in flight: half a release is worse than a late one.
|
|
16
|
+
group: release-${{ github.ref }}
|
|
17
|
+
cancel-in-progress: false
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
release:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
# Scopes the Trusted Publishing OIDC to this environment (set the same name
|
|
23
|
+
# on the PyPI publisher for the narrowing to be real), and gives the repo
|
|
24
|
+
# one checkbox, required reviewers, to make a stray tag push wait for a
|
|
25
|
+
# human instead of shipping.
|
|
26
|
+
environment:
|
|
27
|
+
name: pypi
|
|
28
|
+
url: https://pypi.org/p/silica-core
|
|
29
|
+
permissions:
|
|
30
|
+
contents: write # create the GitHub Release
|
|
31
|
+
id-token: write # OIDC for PyPI Trusted Publishing (no stored token)
|
|
32
|
+
steps:
|
|
33
|
+
# Actions are pinned by commit, not by tag: this job holds a token that
|
|
34
|
+
# can publish to PyPI, and a tag is mutable. Dependabot (github-actions,
|
|
35
|
+
# weekly) bumps the pins and rewrites the version comment.
|
|
36
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
37
|
+
with:
|
|
38
|
+
fetch-depth: 0 # full history + tags for setuptools-scm and git-cliff
|
|
39
|
+
|
|
40
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
41
|
+
with:
|
|
42
|
+
python-version: "3.11"
|
|
43
|
+
|
|
44
|
+
- run: uv sync --locked --extra dev --extra connect --extra mcp
|
|
45
|
+
|
|
46
|
+
# A version on PyPI is burned even after a yank, so the irreversible step
|
|
47
|
+
# gets the gate: no green suite, no publish. ci.yml runs the same command
|
|
48
|
+
# on PRs; duplicated on purpose, because a tag can be pushed onto a commit
|
|
49
|
+
# no PR ever covered.
|
|
50
|
+
- name: Test before anything irreversible
|
|
51
|
+
run: uv run pytest -q
|
|
52
|
+
|
|
53
|
+
- name: Build sdist and wheel
|
|
54
|
+
# The rm is not tidiness: `files: dist/*` below attaches everything in
|
|
55
|
+
# here, and uv build writes a .gitignore that reached the v0.5.0
|
|
56
|
+
# release as an asset named default.gitignore. Excluding it there would
|
|
57
|
+
# mean listing the artifacts by name, which is a second way for the
|
|
58
|
+
# release to fail when one of them is not matched.
|
|
59
|
+
run: uv build && rm -f dist/.gitignore
|
|
60
|
+
|
|
61
|
+
- name: Verify built version matches the tag
|
|
62
|
+
run: ls -1 dist/ && uv run python -c "import pathlib,sys; t='${{ github.ref_name }}'.lstrip('v'); files=[p.name for p in pathlib.Path('dist').glob('*.whl')]; sys.exit(0 if any(t.replace('-','_') in f for f in files) else f'tag {t} not in {files}')"
|
|
63
|
+
|
|
64
|
+
- name: Verify the wheel ships the skill
|
|
65
|
+
# Guards the package-data config: `silica setup` copies SKILL.md out of
|
|
66
|
+
# the installed package, and a wheel that drops it fails at runtime.
|
|
67
|
+
run: |
|
|
68
|
+
unzip -l dist/*.whl | grep -q "silica/skills/silica/SKILL.md" || { echo "MISSING from wheel: SKILL.md"; exit 1; }
|
|
69
|
+
|
|
70
|
+
- name: Publish to PyPI
|
|
71
|
+
# Trusted Publishing via OIDC — the PyPI project must have this repo +
|
|
72
|
+
# workflow registered as a trusted publisher. No API token in secrets.
|
|
73
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
74
|
+
|
|
75
|
+
- name: Generate release notes
|
|
76
|
+
uses: orhun/git-cliff-action@f50e11560dce63f7c33227798f90b924471a88b5 # v4.8.0
|
|
77
|
+
id: notes
|
|
78
|
+
with:
|
|
79
|
+
config: cliff.toml
|
|
80
|
+
args: --latest --strip header
|
|
81
|
+
|
|
82
|
+
- name: Publish GitHub release
|
|
83
|
+
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
|
|
84
|
+
with:
|
|
85
|
+
body: ${{ steps.notes.outputs.content }}
|
|
86
|
+
files: dist/*
|
|
87
|
+
fail_on_unmatched_files: true
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Python & Bytecode
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
*.sage.py
|
|
7
|
+
|
|
8
|
+
# Build & Packaging
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
bin/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
downloads/
|
|
14
|
+
eggs/
|
|
15
|
+
.eggs/
|
|
16
|
+
lib/
|
|
17
|
+
lib64/
|
|
18
|
+
parts/
|
|
19
|
+
sdist/
|
|
20
|
+
var/
|
|
21
|
+
wheels/
|
|
22
|
+
share/python-wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
MANIFEST
|
|
27
|
+
silica/_version.py
|
|
28
|
+
*.manifest
|
|
29
|
+
*.spec
|
|
30
|
+
pip-log.txt
|
|
31
|
+
pip-delete-this-directory.txt
|
|
32
|
+
.pdm-plugins/
|
|
33
|
+
.pdm-python/
|
|
34
|
+
.pdm-build/
|
|
35
|
+
__pypackages__/
|
|
36
|
+
|
|
37
|
+
# Virtual Environments
|
|
38
|
+
.env
|
|
39
|
+
.venv
|
|
40
|
+
env/
|
|
41
|
+
venv/
|
|
42
|
+
ENV/
|
|
43
|
+
env.bak/
|
|
44
|
+
venv.bak/
|
|
45
|
+
|
|
46
|
+
# Testing, Linting & Coverage
|
|
47
|
+
.cache
|
|
48
|
+
.pytest_cache/
|
|
49
|
+
.mypy_cache/
|
|
50
|
+
.ruff_cache/
|
|
51
|
+
.tox/
|
|
52
|
+
.hatch/
|
|
53
|
+
.coverage
|
|
54
|
+
.coverage.*
|
|
55
|
+
htmlcov/
|
|
56
|
+
.htmlcov/
|
|
57
|
+
.nosenvy/
|
|
58
|
+
.noscript/
|
|
59
|
+
.nosetests/
|
|
60
|
+
nosetests.xml
|
|
61
|
+
coverage.xml
|
|
62
|
+
*.cover
|
|
63
|
+
*.py,cover
|
|
64
|
+
.hypothesis/
|
|
65
|
+
.run/
|
|
66
|
+
tests/fixtures/synthetic_vault/*
|
|
67
|
+
.import_linter_cache/
|
|
68
|
+
.grimp_cache/
|
|
69
|
+
|
|
70
|
+
# Editors & IDEs
|
|
71
|
+
.vscode/
|
|
72
|
+
.idea/
|
|
73
|
+
*.swp
|
|
74
|
+
*.swo
|
|
75
|
+
.spyderproject
|
|
76
|
+
.spyproject
|
|
77
|
+
.ropeproject
|
|
78
|
+
profile_default/
|
|
79
|
+
ipython_config.py
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# OS Files
|
|
83
|
+
.DS_Store
|
|
84
|
+
Thumbs.db
|
|
85
|
+
|
|
86
|
+
# Documentation
|
|
87
|
+
docs/_build/
|
|
88
|
+
/site
|
|
89
|
+
/docs/*
|
|
90
|
+
|
|
91
|
+
# Silica & Agent Specific
|
|
92
|
+
# .agents/ is local agent tooling, except the Codex plugin marketplace, which
|
|
93
|
+
# Codex looks for at exactly .agents/plugins/marketplace.json.
|
|
94
|
+
/.agents/*
|
|
95
|
+
!/.agents/plugins/
|
|
96
|
+
.claude/
|
|
97
|
+
CLAUDE.md
|
|
98
|
+
.impeccable
|
|
99
|
+
/memory
|
|
100
|
+
.superpowers
|
|
101
|
+
.silica/
|
|
102
|
+
.silicaignore
|
|
103
|
+
.obsidian
|
|
104
|
+
|
|
105
|
+
# Databases & Locks
|
|
106
|
+
ruvector.db
|
|
107
|
+
agentdb.rvf
|
|
108
|
+
*.rvf.lock
|
|
109
|
+
|
|
110
|
+
# Evals & Benchmarks
|
|
111
|
+
evals/.oracle/
|
|
112
|
+
evals/golden/metrics.json
|
|
113
|
+
evals/*/metrics.*.json
|
|
114
|
+
bench/
|
|
115
|
+
# downloaded benchmark corpora (LongMemEval s is 265 MB)
|
|
116
|
+
data/
|
|
117
|
+
|
|
118
|
+
# Generated Files & Reports
|
|
119
|
+
graph.html
|
|
120
|
+
CONTEXT.md
|
|
121
|
+
PRODUCT.md
|
|
122
|
+
DESIGN.md
|
|
123
|
+
|
|
124
|
+
# Research corpus: papers pulled for design decisions, kept out of the tree.
|
|
125
|
+
# Silica still indexes them — .gitignore is deliberately not an index rule.
|
|
126
|
+
docs/research/
|