skill-router 0.0.1-snapshot → 0.0.2-snapshot

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/README.md CHANGED
@@ -11,9 +11,16 @@ npm install -g .
11
11
  The package runs this postinstall hook:
12
12
 
13
13
  ```bash
14
- python3 -m pip install fastembed numpy --user
14
+ node scripts/postinstall.js
15
15
  ```
16
16
 
17
+ It creates an isolated venv in `~/.cache/skill-router/venv` and installs:
18
+
19
+ - `fastembed`
20
+ - `numpy`
21
+
22
+ This avoids PEP 668 / externally-managed-environment failures on Ubuntu.
23
+
17
24
  ## Usage
18
25
 
19
26
  ```bash
@@ -38,6 +45,11 @@ If the provided `--skills-dir` does not exist or has no `.md` files, it returns:
38
45
  {"skill_id":null,"score":0.0}
39
46
  ```
40
47
 
48
+ Lookup order:
49
+
50
+ 1. `--skills-dir` (default: `~/.cache/skills-router`)
51
+ 2. Fallback: `~/.cache/gemini-skills`
52
+
41
53
  ## Cache
42
54
 
43
55
  Cache is stored in:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skill-router",
3
- "version": "0.0.1-snapshot",
3
+ "version": "0.0.2-snapshot",
4
4
  "description": "Local semantic skill router for gemini-cli-pro",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -8,10 +8,11 @@
8
8
  "skill-router": "./router.py"
9
9
  },
10
10
  "scripts": {
11
- "postinstall": "python3 -m pip install fastembed numpy --user"
11
+ "postinstall": "node scripts/postinstall.js"
12
12
  },
13
13
  "files": [
14
14
  "router.py",
15
- "README.md"
15
+ "README.md",
16
+ "scripts"
16
17
  ]
17
18
  }
package/router.py CHANGED
@@ -7,10 +7,26 @@ import argparse
7
7
  import hashlib
8
8
  import json
9
9
  import os
10
+ import site
10
11
  import sys
11
12
  from pathlib import Path
12
13
  from typing import Iterable
13
14
 
15
+
16
+ def _cache_root_dir() -> Path:
17
+ # Keep ~/.cache across Linux and macOS for compatibility with gemini-cli-pro integration.
18
+ return Path.home() / ".cache" / "skill-router"
19
+
20
+
21
+ def _inject_cached_venv_site_packages() -> None:
22
+ py_tag = f"python{sys.version_info.major}.{sys.version_info.minor}"
23
+ candidate = _cache_root_dir() / "venv" / "lib" / py_tag / "site-packages"
24
+ if candidate.exists() and candidate.is_dir():
25
+ site.addsitedir(str(candidate))
26
+
27
+
28
+ _inject_cached_venv_site_packages()
29
+
14
30
  import numpy as np
15
31
 
16
32
  try:
@@ -20,9 +36,11 @@ except Exception: # pragma: no cover - runtime dependency may be unavailable
20
36
 
21
37
 
22
38
  MODEL_NAME = "BAAI/bge-small-en-v1.5"
23
- CACHE_ROOT = Path.home() / ".cache" / "skill-router"
39
+ CACHE_ROOT = _cache_root_dir()
24
40
  DATA_FILE = CACHE_ROOT / "data.npy"
25
41
  SKILLS_CACHE_FILE = CACHE_ROOT / ".skills_cache.npy"
42
+ DEFAULT_SKILLS_DIR = Path.home() / ".cache" / "skills-router"
43
+ GLOBAL_GEMINI_SKILLS_DIR = Path.home() / ".cache" / "gemini-skills"
26
44
 
27
45
  _EMBEDDER = None
28
46
 
@@ -77,9 +95,14 @@ def _has_skill_markdown(skills_dir: Path) -> bool:
77
95
 
78
96
 
79
97
  def _resolve_available_skills_dir(preferred_skills_dir: Path) -> Path | None:
80
- candidate = preferred_skills_dir.resolve()
81
- if _has_skill_markdown(candidate):
82
- return candidate
98
+ primary = preferred_skills_dir.resolve()
99
+ if _has_skill_markdown(primary):
100
+ return primary
101
+
102
+ fallback = GLOBAL_GEMINI_SKILLS_DIR.resolve()
103
+ if _has_skill_markdown(fallback):
104
+ return fallback
105
+
83
106
  return None
84
107
 
85
108
 
@@ -155,7 +178,9 @@ def _get_embedder() -> TextEmbedding:
155
178
  global _EMBEDDER
156
179
  if _EMBEDDER is None:
157
180
  if TextEmbedding is None:
158
- raise RuntimeError("missing_dependency: fastembed")
181
+ raise RuntimeError(
182
+ f"missing_dependency: fastembed (expected venv at {CACHE_ROOT / 'venv'})"
183
+ )
159
184
  _EMBEDDER = TextEmbedding(model_name=MODEL_NAME)
160
185
  return _EMBEDDER
161
186
 
@@ -306,7 +331,7 @@ def main() -> int:
306
331
  parser.add_argument("--prompt", default="", help="User text prompt")
307
332
  parser.add_argument(
308
333
  "--skills-dir",
309
- default=".skills",
334
+ default=str(DEFAULT_SKILLS_DIR),
310
335
  help="Path to skills directory",
311
336
  )
312
337
  parser.add_argument(
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawnSync } = require("child_process");
4
+ const fs = require("fs");
5
+ const os = require("os");
6
+ const path = require("path");
7
+
8
+ function run(cmd, args, options = {}) {
9
+ const result = spawnSync(cmd, args, {
10
+ stdio: "inherit",
11
+ env: process.env,
12
+ ...options,
13
+ });
14
+ return result.status === 0;
15
+ }
16
+
17
+ function main() {
18
+ const cacheRoot = path.join(os.homedir(), ".cache", "skill-router");
19
+ const venvDir = path.join(cacheRoot, "venv");
20
+ const venvPython = path.join(venvDir, "bin", "python");
21
+
22
+ try {
23
+ fs.mkdirSync(cacheRoot, { recursive: true });
24
+ } catch (err) {
25
+ console.warn("[skill-router] warning: failed to create cache dir:", err.message);
26
+ process.exit(0);
27
+ }
28
+
29
+ if (!fs.existsSync(venvPython)) {
30
+ const created = run("python3", ["-m", "venv", venvDir]);
31
+ if (!created) {
32
+ console.warn("[skill-router] warning: failed to create Python venv. Install will continue without blocking.");
33
+ process.exit(0);
34
+ }
35
+ }
36
+
37
+ // Best-effort dependency setup. We avoid failing npm install if pip/network is unavailable.
38
+ run(venvPython, ["-m", "pip", "install", "--upgrade", "pip", "setuptools", "wheel"]);
39
+ const depsOk = run(venvPython, ["-m", "pip", "install", "--upgrade", "fastembed", "numpy"]);
40
+
41
+ if (!depsOk) {
42
+ console.warn("[skill-router] warning: could not install Python deps in venv. The CLI will report missing_dependency at runtime.");
43
+ }
44
+ }
45
+
46
+ main();