@heytherevibin/skillforge 0.2.1 → 0.7.0

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 CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0
4
+
5
+ - **Breaking:** Removed the optional **HTTP API** (`skillforge start`), **`skillforge chat`** harness, and **`skillforge auth`** (bearer tokens were only used by HTTP). MCP (`skillforge mcp`), **`skillforge route`**, **`skillforge events`**, and **`skillforge index`** are unchanged.
6
+ - **Migration:** The CLI deletes a leftover **`~/.skillforge/auth.json`** on **every** invocation (including **`skillforge --help`**), once the file is gone the message stops.
7
+ - Dropped **FastAPI**, **uvicorn**, and direct **pydantic** / **httpx** dependencies from `python/requirements.txt` (routing still uses libraries that may bundle their own deps).
8
+
9
+ ## 0.6.0
10
+
11
+ - **Phase 4 context safety (MCP meta 1.4)**: Default **secret / credential pattern redaction** and optional **home-directory stripping** on injected chunk text, relative **`path`** fields, stored route **`prompt`** snippet, **`reasoning`**, and **`orchestrator_db`** in **`_meta`**. Disable with **`SKILLFORGE_REDACT_CONTEXT=0`**; path scrub with **`SKILLFORGE_REDACT_HOME_IN_PATHS=0`**. New **[`app/redaction.py`](python/app/redaction.py)**; route events include **`context_redaction`** hit counts.
12
+
13
+ ## 0.5.0
14
+
15
+ - **Phase 3 context fusion (MCP meta 1.3)**: When **`include_project_rag`** is on and the project index is non-empty, skill + project chunk **pools** are merged with **greedy MMR** under a single **`SKILLFORGE_CONTEXT_BUDGET_CHARS`** (default: route max + project RAG max). Disable with **`SKILLFORGE_CONTEXT_FUSION=0`** to keep append-only behavior.
16
+ - **Telemetry**: route events and **`_meta.fusion`** include MMR trace; each context item may carry **`mmr_rank`**, **`mmr_score`**, **`retrieval_relevance`**, **`max_sim_to_prior`**.
17
+ - New **[`app/context_fusion.py`](python/app/context_fusion.py)**; **`load_project_fusion_pool`** in **`project_index`**.
18
+
19
+ ## 0.4.0
20
+
21
+ - **Phase 2 project RAG (MCP 1.2)**: **`skillforge index --project-root=…`** walks the repo (bounded file sizes, ignore dirs), chunks text files, and stores **`project_chunks`** + embeddings in **`<project>/.skillforge/orchestrator.db`** (same DB as sessions/weights).
22
+ - **MCP / CLI / HTTP**: optional **`include_project_rag`** (MCP + **`skillforge route --include-project-rag`**) appends top matching file chunks under **`SKILLFORGE_PROJECT_RAG_MAX_CHARS`**. **`_meta`**: schema **1.2**; **`sources`** may include **`kind: file`**; **`budget.chars_project_chunks`** / **`chars_context_items_total`**.
23
+
24
+ ## 0.3.0
25
+
26
+ - **Phase 1 skill RAG (MCP 1.1)**: Default **`SKILLFORGE_CONTEXT_MODE=chunks`** — line-bounded chunks from each picked **`SKILL.md`** body, scored by query similarity, injected up to **`SKILLFORGE_ROUTE_MAX_CHARS`**. Set **`full_body`** for legacy whole-document injection per skill.
27
+ - **`_meta`**: schema **1.1**; **`sources`** lists chunk-level rows with **`line_start` / `line_end`** and **`score`**; **`context_items_count`**.
28
+ - New **[`app/chunking.py`](python/app/chunking.py)**; **`Router.build_context_items`**, **`format_context_items_markdown`**.
29
+
30
+ ## 0.2.2
31
+
32
+ - **MCP Phase 0 contract**: **`route_skills`** success responses include versioned **`_meta`** (`schema_version` **1.0**, **`sources`**, **`budget`**, **`candidates_preview`**). Empty prompt returns **`isError`** + **`_meta.error`**. Shared builder in **`app.mcp_contract`**; **`skillforge route --json-meta`** matches.
33
+ - **Docs**: README “MCP response contract” section.
34
+
3
35
  ## 0.2.1
4
36
 
5
37
  - Same code as **0.2.0**. **npm never allows reusing a version** after it has been published once—even if you **unpublish** it and only **0.1.0** remains visible. The registry still blocks **`0.2.0`**; ship **`0.2.1`** (or higher) instead.
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  **Primary interface:** **stdio MCP** (`skillforge mcp`) — add it to Claude Desktop, Cursor, or Claude Code.
12
12
 
13
- **Optional:** **headless HTTP API** (`skillforge start`) for `/chat`, `/events`, and integrations. **Real-time usage:** run **`skillforge events --watch`** in a terminal (top skills, active sessions, and live **route** / **feedback** lines from SQLite).
13
+ **Observability:** run **`skillforge events --watch`** in a terminal (top skills, active sessions, and live **route** / **feedback** lines from SQLite).
14
14
 
15
15
  ---
16
16
 
@@ -23,11 +23,10 @@
23
23
  - [Usage](#usage)
24
24
  - [Run modes](#run-modes)
25
25
  - [Model Context Protocol (MCP)](#model-context-protocol-mcp)
26
- - [Multi-user authentication](#multi-user-authentication)
26
+ - [MCP response contract](#mcp-response-contract)
27
27
  - [Skills and packs](#skills-and-packs)
28
28
  - [Routing pipeline](#routing-pipeline)
29
29
  - [Configuration](#configuration)
30
- - [HTTP API](#http-api)
31
30
  - [Local data and operations](#local-data-and-operations)
32
31
  - [Security considerations](#security-considerations)
33
32
  - [Contributing and governance](#contributing-and-governance)
@@ -47,7 +46,7 @@
47
46
  | **Observability** | **`skillforge events`**: snapshots of **usage** + **active sessions**, **`--watch`** for realtime; **`--verbose`** for route detail. No browser UI. |
48
47
  | **Project bootstrap** | MCP tools **`materialize_project`** and **`skillforge_bootstrap`** write `.cursor/rules`, **`docs/SKILLFORGE-PRD.md`**, and a **`CLAUDE.md`** section (map **`/skillforge`** in rules to MCP tools). |
49
48
  | **Extensibility** | Custom skills, git-based **packs**, and overrides under a single user config directory. |
50
- | **Deployment flexibility** | **MCP stdio** (default story), optional **HTTP API**, dev **`skillforge chat`** harness. |
49
+ | **Deployment flexibility** | **MCP stdio** as the default integration; **CLI** helpers (`route`, `events`, `index`). |
51
50
 
52
51
  Bundled content includes **200+** curated skills (coding, security, research, frontend/backend patterns, and more). Exact counts are validated in CI.
53
52
 
@@ -58,8 +57,8 @@ Bundled content includes **200+** curated skills (coding, security, research, fr
58
57
  | Dependency | Version | Notes |
59
58
  |------------|---------|--------|
60
59
  | **Node.js** | **>= 18** | Required for the CLI bootstrapper. Continuous integration runs on **Node 22**. |
61
- | **Python** | **>= 3.10** | Used on the host PATH for embeddings and the FastAPI orchestrator. |
62
- | **Anthropic API** | — | **`ANTHROPIC_API_KEY`** is required for **HTTP**, **CLI chat**, and the **full** (Haiku) router. **MCP** can run **without** it when using **embedding-only** routing (default when the key is omitted; see [MCP](#model-context-protocol-mcp)). |
60
+ | **Python** | **>= 3.10** | Used on the host PATH for embeddings and routing (via the CLI-spawned **venv**). |
61
+ | **Anthropic API** | — | **`ANTHROPIC_API_KEY`** enables the **full** (Haiku) router when you want it. **MCP** can run **without** it using **embedding-only** routing (default when the key is omitted; see [MCP](#model-context-protocol-mcp)). |
63
62
 
64
63
  **First run:** The CLI creates **`~/.skillforge/`**, a dedicated **Python venv**, installs Python dependencies, and caches the default embedding model (typically on the order of one to two minutes once; subsequent starts are fast).
65
64
 
@@ -73,12 +72,6 @@ npx --yes @heytherevibin/skillforge --help
73
72
 
74
73
  Add Skillforge to your MCP config (see [MCP](#model-context-protocol-mcp)). No `ANTHROPIC_API_KEY` is required for **embedding-only** routing.
75
74
 
76
- Optional HTTP API (e.g. for `skillforge chat`): set **`ANTHROPIC_API_KEY`**, then:
77
-
78
- ```bash
79
- skillforge start
80
- ```
81
-
82
75
  Live log (usage + routes): **`skillforge events --watch`**.
83
76
 
84
77
  ---
@@ -111,11 +104,9 @@ Source and issues: [github.com/heytherevibin/skillforge](https://github.com/heyt
111
104
  |---------|---------|
112
105
  | `skillforge --help` | Recommended first step; **MCP** is the main integration. |
113
106
  | `skillforge mcp` | **stdio** MCP server (Claude, Cursor, …). |
114
- | `skillforge start [--port=8000]` | Optional **HTTP API** (no HTML or WebSocket UI). |
115
107
  | `skillforge events [--watch]` | **Terminal** log: usage snapshot + routes; see **`skillforge events --help`**. |
116
108
  | `skillforge route […]` | **Terminal** routing — same pipeline as MCP **`route_skills`** (loads embed model); see **`skillforge route --help`**. |
117
109
  | `skillforge mcp config [--local] [--with-anthropic]` | **stdout**: JSON snippet for **`mcp.json`** (merge manually). |
118
- | `skillforge chat` | Dev harness: HTTP client to **`POST /chat`** (needs **`start`** + API key). |
119
110
 
120
111
  ### Model Context Protocol (MCP)
121
112
 
@@ -163,29 +154,31 @@ With **Haiku** routing (uses your Anthropic key in the MCP process):
163
154
 
164
155
  | Tool | Purpose |
165
156
  |------|---------|
166
- | `route_skills` | Returns routed **`SKILL.md`** bodies. Pass **`project_root`** (workspace path) for per-repo SQLite under **`.skillforge/orchestrator.db`** and learning; or set env **`SKILLFORGE_PROJECT_ROOT`**. Optional **`session_id`**, **`user_id`** / **`SKILLFORGE_MCP_USER_ID`**. |
157
+ | `route_skills` | Returns routed **`SKILL.md`** context (chunks or full body). Pass **`project_root`** for per-repo SQLite under **`.skillforge/orchestrator.db`**. Optional **`include_project_rag`** (after **`skillforge index --project-root=…`**), **`session_id`**, **`user_id`** / **`SKILLFORGE_MCP_USER_ID`**, or env **`SKILLFORGE_PROJECT_ROOT`**. |
167
158
  | `list_skills` | Catalog overview; optional **`user_id`** scopes usage stats. |
168
- | `skill_feedback` | Feedback for the learning loop; optional **`user_id`**, **`session_id`** (for `/events`). |
159
+ | `skill_feedback` | Feedback for the learning loop; optional **`user_id`**, **`session_id`** (stored with events). |
169
160
  | `skill_referenced` | Mark a routed skill as **used** in the reply (increments **`referenced`** + weight; optional **`user_id`**). |
170
161
  | `disable_skill` | Toggle skills; optional **`user_id`**. |
171
162
  | `materialize_project` | Writes **`.cursor/rules/skillforge.mdc`**, **`docs/SKILLFORGE-PRD.md`**, updates **`CLAUDE.md`** (Skillforge block). Args: **`project_root`**, **`skill_names`** from **`route_skills`**. |
172
163
  | `skillforge_bootstrap` | **`route_skills`** + **`materialize_project`** in one call (needs **`project_root`**). |
173
164
 
174
- `/skillforge` is not registered by npm installs; add a **Cursor rule** or **CLAUDE.md** instruction so the agent calls these tools when the user asks.
165
+ ### MCP response contract
175
166
 
176
- Route events go to **`~/.skillforge/data/orchestrator.db`**; use **`skillforge events`** or **`GET /events`** when HTTP is running.
167
+ Structured diagnostics for tool **`route_skills`** live in **`result._meta`** (hosts may ignore or log them):
177
168
 
178
- ### Multi-user authentication
169
+ - **`schema_version`**: **`1.4`** — same as **1.3** plus optional **`context_redaction`** (`enabled`, `secret_hits`, `path_hits`) on **`route_skills`** success.
170
+ - **`sources`**: citations; each item has **`kind`** (`skill` or **`file`**), **`ref`**, **`line_start`** / **`line_end`**, **`score`**, optional **`mmr_rank`** after fusion.
171
+ - **`fusion`**: present when MMR fusion ran (**`enabled`: true**): **`lambda`**, **`budget_chars`**, **`pool_skill`**, **`pool_project`**, **`mmr_trace`**, …
172
+ - **`context_redaction`**: optional; when scrubbing is enabled, reports **`enabled`**, **`secret_hits`**, **`path_hits`** for exported context.
173
+ - **`budget`**: **`chars_skill_bodies`**, **`chars_project_chunks`**, **`chars_context_items_total`**, **`chars_response_total`**, **`est_tokens_approx`** (rough `chars/4`).
174
+ - **`candidates_preview`**: up to 15 shortlist entries **`{ name, score }`** for debugging.
175
+ - **`picked`**, **`reasoning`**, **`session_id`**, **`user_id`**, **`rerouted`**, **`change_pct`**, **`route_ms`**, **`orchestrator_db`**.
179
176
 
180
- Bearer tokens isolate sessions, weights, and events when enabled:
177
+ On validation errors (e.g. empty **`prompt`**), the tool returns **`isError`: true** and **`_meta.error`** (e.g. **`empty_prompt`**), still with **`schema_version`** and **`sources`: `[]`**.
181
178
 
182
- ```bash
183
- skillforge auth add <user-id>
184
- skillforge auth list
185
- skillforge auth remove <user-id>
186
- ```
179
+ `/skillforge` is not registered by npm installs; add a **Cursor rule** or **CLAUDE.md** instruction so the agent calls these tools when the user asks.
187
180
 
188
- When any token exists, protected **HTTP API** routes require **`Authorization: Bearer <token>`**. Single-user mode applies when no tokens are configured.
181
+ Route events go to **`~/.skillforge/data/orchestrator.db`** (or per-repo **`.skillforge/orchestrator.db`** when **`project_root`** is set); use **`skillforge events`** to inspect them.
189
182
 
190
183
  ---
191
184
 
@@ -233,7 +226,7 @@ User prompt
233
226
  → Usage signals update weights (optional)
234
227
  ```
235
228
 
236
- Re-route: when overlap between successive active sets falls below a configurable threshold, the pipeline selects a new set for the next turn. Events are stored in SQLite; stream them with **`skillforge events --watch`** (or **`GET /events`** when HTTP is running).
229
+ Re-route: when overlap between successive active sets falls below a configurable threshold, the pipeline selects a new set for the next turn. Events are stored in SQLite; stream them with **`skillforge events --watch`**.
237
230
 
238
231
  ---
239
232
 
@@ -243,16 +236,30 @@ Environment variables (see also inline help and server defaults):
243
236
 
244
237
  | Variable | Default | Role |
245
238
  |----------|---------|------|
246
- | `ANTHROPIC_API_KEY` | — | **Required** for HTTP/CLI chat and answer streaming; **optional** for MCP if you use embedding-only routing (default when unset). |
247
- | `SKILLFORGE_ROUTER_MODE` | *(auto)* | `full` = always use Haiku for final pick (MCP: requires key for routing). `embedding` = skip Haiku; top `SKILLFORGE_MAX_ACTIVE` from shortlist. Unset = **auto**: MCP uses embedding-only when `ANTHROPIC_API_KEY` is absent, else full. HTTP: unset or `full` uses Haiku when key is present; set `embedding` to skip Haiku on the server (answer model still needs a key). |
248
- | `SKILLFORGE_PORT` | `8000` | HTTP listen port. |
239
+ | `ANTHROPIC_API_KEY` | — | **Optional** for MCP: omit for embedding-only routing (default when unset); set for **full** (Haiku) routing. |
240
+ | `SKILLFORGE_ROUTER_MODE` | *(auto)* | `full` = always use Haiku for final pick (requires key). `embedding` = skip Haiku; top `SKILLFORGE_MAX_ACTIVE` from shortlist. Unset = **auto**: embedding-only when `ANTHROPIC_API_KEY` is absent, else full. |
249
241
  | `SKILLFORGE_EMBED_MODEL` | `all-MiniLM-L6-v2` | Embedding model id. |
250
- | `SKILLFORGE_ROUTER_MODEL` | `claude-haiku-4-5-20251001` | Routing model. |
251
- | `SKILLFORGE_ANSWER_MODEL` | `claude-opus-4-7` | Main response model. |
242
+ | `SKILLFORGE_ROUTER_MODEL` | `claude-haiku-4-5-20251001` | Routing model (Haiku). |
252
243
  | `SKILLFORGE_TOP_K` | `15` | Embedding shortlist size. |
253
244
  | `SKILLFORGE_MAX_ACTIVE` | `7` | Maximum skills injected per turn. |
254
245
  | `SKILLFORGE_REROUTE_THRESHOLD` | `0.4` | Re-route sensitivity (Jaccard distance). |
255
- | `SKILLFORGE_MCP_USER_ID` | `""` | Default logical **user id** for MCP tool calls when arguments omit `user_id` (weights, sessions, events—same SQLite namespace as HTTP `resolve_user`). |
246
+ | `SKILLFORGE_CONTEXT_MODE` | `chunks` | `chunks` = embed **line-bounded chunks** from each picked skill body (RAG) up to **`SKILLFORGE_ROUTE_MAX_CHARS`**. `full_body` = inject entire **SKILL.md** per pick (legacy). |
247
+ | `SKILLFORGE_CHUNK_MAX_CHARS` | `1200` | Max characters per chunk (before overlap split). |
248
+ | `SKILLFORGE_CHUNK_OVERLAP` | `200` | Character overlap when hard-splitting an oversized section. |
249
+ | `SKILLFORGE_ROUTE_MAX_CHARS` | `60000` | Skill chunk char cap when **`SKILLFORGE_CONTEXT_FUSION`** is off (append path); also part of default unified budget sum when fusion is on. |
250
+ | `SKILLFORGE_PROJECT_RAG_MAX_CHARS` | `24000` | Project chunk char cap when fusion is off (append path); part of default unified budget when fusion is on. |
251
+ | `SKILLFORGE_CONTEXT_BUDGET_CHARS` | *(route + project RAG defaults)* | Single cap for **MMR-fused** skill + project context. |
252
+ | `SKILLFORGE_CONTEXT_FUSION` | `1` | **`0`** / **`false`**: disable MMR fusion; append project chunks after skills. |
253
+ | `SKILLFORGE_CONTEXT_MMR_LAMBDA` | `0.7` | MMR tradeoff: higher ⇒ query relevance; lower ⇒ diversity vs. already-selected chunks. |
254
+ | `SKILLFORGE_FUSION_POOL_SKILL` | `96` | Max skill chunks in the fusion candidate pool. |
255
+ | `SKILLFORGE_FUSION_POOL_PROJECT` | `96` | Max project chunks in the fusion candidate pool. |
256
+ | `SKILLFORGE_FUSION_FULL_BODY_PREVIEW_CHARS` | `4000` | **`SKILL.md`** prefix length for embedding full-body / fallback fusion rows. |
257
+ | `SKILLFORGE_PROJECT_RAG_MAX_CHUNKS` | `20000` | Max **project_chunks** rows loaded for one retrieval. |
258
+ | `SKILLFORGE_INDEX_MAX_FILE_BYTES` | `524288` | Skip indexing files larger than this (bytes). |
259
+ | `SKILLFORGE_INDEX_IGNORE_DIRS` | `""` | Extra comma-separated directory **basename** ignores (e.g. `out,tmp`). |
260
+ | `SKILLFORGE_REDACT_CONTEXT` | `1` | When **`1`** (default), scrub common secret shapes and (with home path scrub) exported context before MCP/CLI output and route events. |
261
+ | `SKILLFORGE_REDACT_HOME_IN_PATHS` | `1` | Replace resolved home-directory prefixes in chunk paths / DB path hints with **`[HOME]`**. |
262
+ | `SKILLFORGE_MCP_USER_ID` | `""` | Default logical **user id** for MCP tool calls when arguments omit `user_id` (weights, sessions, events). |
256
263
  | `SKILLFORGE_PROJECT_ROOT` | `""` | Default workspace root when MCP **`project_root`** is omitted: events/weights/sessions live in **`<root>/.skillforge/orchestrator.db`**. Prefer passing **`project_root`** on each tool call from the host. |
257
264
  | `SKILLFORGE_SKILL_HOT_RELOAD` | `1` | When **`0`** / **`false`**, disable **SKILL.md** hot-reload; restart the MCP process to refresh the catalog. |
258
265
  | `SKILLFORGE_WATCH_SKILLS_INTERVAL` | `30` | Seconds between background catalog checks when hot reload is on. **`0`**: no background polling and no MCP **`tools.listChanged`**; **`tools/list`** and **`tools/call`** still reload when files change. |
@@ -260,29 +267,13 @@ Environment variables (see also inline help and server defaults):
260
267
 
261
268
  ---
262
269
 
263
- ## HTTP API
264
-
265
- | Method | Path | Description |
266
- |--------|------|-------------|
267
- | `POST` | `/chat` | Primary chat; SSE stream. |
268
- | `POST` | `/feedback` | Skill feedback for learning. |
269
- | `POST` | `/skills/disable` | Enable/disable a skill flag. |
270
- | `GET` | `/skills` | Catalog with stats and weights. |
271
- | `GET` | `/events` | Recent routing events (`?limit=`). |
272
- | `GET` | `/` | JSON service hint (use **`skillforge events --watch`** for a live terminal log). |
273
- | `GET` | `/healthz` | Health metadata (`skills_loaded`, **`live_log`** hint). |
274
-
275
- Authenticated mode applies **`Bearer`** tokens as described above. Do not expose unauthenticated instances beyond trusted networks.
276
-
277
- ---
278
-
279
270
  ## Local data and operations
280
271
 
281
272
  Optional **per-project** state (when **`project_root`** or **`SKILLFORGE_PROJECT_ROOT`** is set, or MCP passes **`project_root`** on tools):
282
273
 
283
274
  ```
284
275
  <workspace>/.skillforge/
285
- ├── orchestrator.db # SQLite for this repo (sessions, weights, events)
276
+ ├── orchestrator.db # SQLite: sessions, weights, events, **project_chunks** (after `skillforge index`)
286
277
  └── last_route.json # Last route_skills snapshot (after a routed call)
287
278
  ```
288
279
 
@@ -295,12 +286,12 @@ Global default when no project root:
295
286
  ├── skills/ # User-added skills
296
287
  ├── packs/<hash>/ # Cloned pack repositories
297
288
  ├── packs.json # Pack registry
298
- └── auth.json # Tokens (POSIX mode 0600 when used)
299
289
  ```
300
290
 
301
291
  | Command | Effect |
302
292
  |---------|--------|
303
293
  | `skillforge events` | Prints a **usage** snapshot and recent **`route`** / **`feedback`** rows; **`--watch`**, **`--project-root`** (per-repo DB), **`--user`**, **`--verbose`** (see **`--help`**). |
294
+ | `skillforge index` | Chunk/embed text files under **`--project-root`** into **`project_chunks`**. **`--reset`**, **`--stats-only`**, **`--quiet`** (see **`--help`**). |
304
295
  | `skillforge reset` | Clears learning state and event history in the database. |
305
296
  | `skillforge install` | Re-runs bootstrap (venv and dependencies). |
306
297
  | `rm -rf ~/.skillforge` | Full removal of local state and venv. |
@@ -309,8 +300,8 @@ Global default when no project root:
309
300
 
310
301
  ## Security considerations
311
302
 
312
- - Treat **`ANTHROPIC_API_KEY`** and bearer tokens as **secrets**. Prefer environment injection or secret stores, not committed files.
313
- - For **internet-facing** deployments, use **TLS**, **reverse proxies**, and **mandatory** bearer authentication; assume an open HTTP port is reachable by untrusted clients.
303
+ - **Redaction is best-effort regex scrubbing**, not a guarantee. Do not paste production secrets into prompts; treat routed context like **untrusted text** until reviewed.
304
+ - Treat **`ANTHROPIC_API_KEY`** as a **secret** when you set it (e.g. for Haiku routing in MCP). Prefer environment injection or secret stores, not committed files.
314
305
  - Vulnerability disclosure: see **[SECURITY.md](SECURITY.md)**.
315
306
 
316
307
  ---
package/RELEASING.md CHANGED
@@ -77,7 +77,7 @@ npm test
77
77
  Python (syntax only):
78
78
 
79
79
  ```bash
80
- for f in python/app/main.py python/app/cli.py python/app/mcp_server.py python/app/auth.py python/app/events_cli.py python/app/materialize.py python/app/db_paths.py python/app/route_cli.py; do python3 -m py_compile "$f"; done
80
+ for f in python/app/main.py python/app/mcp_server.py python/app/events_cli.py python/app/materialize.py python/app/db_paths.py python/app/route_cli.py python/app/mcp_contract.py python/app/chunking.py python/app/project_index.py python/app/index_cli.py python/app/context_fusion.py python/app/redaction.py; do python3 -m py_compile "$f"; done
81
81
  ```
82
82
 
83
83
  ## Troubleshooting: `EOTP` / one-time password in CI
package/SECURITY.md CHANGED
@@ -26,6 +26,6 @@ We aim to acknowledge valid reports within a few business days.
26
26
  - Keep **2FA** enabled on the npm account that owns the `@heytherevibin` scope.
27
27
  - Prefer pinning action versions or reviewing Dependabot PRs before merge.
28
28
 
29
- ## Runtime hardening
29
+ ## Runtime security
30
30
 
31
- If you expose the HTTP server beyond **localhost**, treat it as internet-facing: use **bearer-token auth** (`skillforge auth …`), terminate TLS at a reverse proxy, and restrict network access appropriately. The default single-user setup assumes a trusted local environment.
31
+ Skillforge’s published surface is **local**: **stdio MCP** and **CLI** commands. Keep your **MCP host** and **`ANTHROPIC_API_KEY`** (if used) within a trusted environment. Do not commit secrets.
package/STRATEGY.md CHANGED
@@ -7,8 +7,7 @@ Skillforge is an **npm-packaged skill orchestrator**: it routes tasks to a small
7
7
  ## Surfaces (today)
8
8
 
9
9
  - **MCP** (`skillforge mcp`): primary — `route_skills`, `list_skills`, feedback tools, `materialize_project`, `skillforge_bootstrap`.
10
- - **Terminal**: `skillforge events --watch` with optional **`--project-root`**.
11
- - **HTTP** (`skillforge start`): optional; still uses the global DB in **app_state** (not per-request project root unless extended later).
10
+ - **Terminal**: `skillforge events --watch` with optional **`--project-root`**; `skillforge route`, `skillforge index`.
12
11
 
13
12
  ## Cursor reality
14
13
 
@@ -17,7 +16,6 @@ Native **`/skillforge`** in editor chat is **not** registered by this npm packag
17
16
  ## Near-term backlog
18
17
 
19
18
  - Shared **`orchestrate()`** API for MCP + CLI parity.
20
- - HTTP: optional **`project_root`** header or body for `/chat` if needed.
21
19
  - Tests: MCP handshake + `resolve_orchestrator_db` behavior.
22
20
 
23
21
  ## Non-goals (v1)
package/bin/cli.js CHANGED
@@ -5,17 +5,15 @@
5
5
  * Usage:
6
6
  * skillforge, skillforge --help Show help (primary path: MCP, not a web app)
7
7
  * skillforge mcp MCP stdio server (Claude / Cursor / …)
8
- * skillforge start [--port=8000] Optional headless HTTP API (no browser UI)
9
8
  * skillforge events [--watch] [--limit=N] Print SQLite routing events
10
9
  * skillforge route [words…] [--prompt=…] Same routing as MCP route_skills (terminal)
11
- * skillforge chat Dev harness (needs `start` + ANTHROPIC_API_KEY)
10
+ * skillforge index --project-root=… Chunk/embed repo files for project RAG
12
11
  * skillforge install One-time Python venv + deps
13
- * skillforge skills … / pack … / auth … / reset
12
+ * skillforge skills … / pack … / reset
14
13
  */
15
14
 
16
15
  const path = require('path');
17
16
  const fs = require('fs');
18
- const crypto = require('crypto');
19
17
  const { spawn, spawnSync } = require('child_process');
20
18
  const os = require('os');
21
19
  const packs = require('../lib/packs');
@@ -26,8 +24,8 @@ const CONFIG_DIR = path.join(os.homedir(), '.skillforge');
26
24
  const VENV_DIR = path.join(CONFIG_DIR, 'venv');
27
25
  const DATA_DIR = path.join(CONFIG_DIR, 'data');
28
26
  const USER_SKILLS_DIR = path.join(CONFIG_DIR, 'skills');
29
- const PACKS_DIR = path.join(CONFIG_DIR, 'packs');
30
- const AUTH_FILE = path.join(CONFIG_DIR, 'auth.json');
27
+ /** Bearer-token file for the removed HTTP API (<=0.6.x); deleted on first CLI use. */
28
+ const LEGACY_AUTH_FILE = path.join(CONFIG_DIR, 'auth.json');
31
29
  const SETUP_MARKER = path.join(CONFIG_DIR, '.setup-complete');
32
30
 
33
31
  const args = process.argv.slice(2);
@@ -87,6 +85,18 @@ function ensureDirs() {
87
85
  }
88
86
  }
89
87
 
88
+ /** v0.7.0 removed HTTP + `skillforge auth`; leftover tokens file is misleading — remove once. */
89
+ function dropLegacyAuthJsonIfPresent() {
90
+ try {
91
+ if (fs.existsSync(LEGACY_AUTH_FILE)) {
92
+ fs.rmSync(LEGACY_AUTH_FILE);
93
+ info('Removed legacy ~/.skillforge/auth.json (HTTP API was removed in v0.7).');
94
+ }
95
+ } catch (e) {
96
+ err(`Could not remove legacy auth.json: ${e.message}`);
97
+ }
98
+ }
99
+
90
100
  function runSetup() {
91
101
  info('First-time setup — this happens once and takes ~2 minutes');
92
102
  ensureDirs();
@@ -145,77 +155,7 @@ function setupIfNeeded() {
145
155
  }
146
156
  }
147
157
 
148
- // ---- API key check ----
149
- function checkApiKey() {
150
- if (!process.env.ANTHROPIC_API_KEY) {
151
- err('ANTHROPIC_API_KEY environment variable is not set.');
152
- log(c.dim(' Get a key at https://console.anthropic.com/'));
153
- log(c.dim(' Then set it:'));
154
- log(c.dim(' export ANTHROPIC_API_KEY=sk-ant-...'));
155
- process.exit(1);
156
- }
157
- }
158
-
159
- // ---- auth management ----
160
- function loadAuth() {
161
- if (!fs.existsSync(AUTH_FILE)) return {};
162
- try { return JSON.parse(fs.readFileSync(AUTH_FILE, 'utf8')); } catch { return {}; }
163
- }
164
- function saveAuth(map) {
165
- ensureDirs();
166
- fs.writeFileSync(AUTH_FILE, JSON.stringify(map, null, 2), { mode: 0o600 });
167
- }
168
- function authToEnvVar(map) {
169
- // map is { token: userId }. Convert and inject as JSON env var.
170
- return JSON.stringify(map);
171
- }
172
-
173
- function authAdd(user) {
174
- if (!user) { err('Usage: skillforge auth add <user-id>'); process.exit(1); }
175
- const map = loadAuth();
176
- // Generate a token
177
- const token = 'sf_' + crypto.randomBytes(24).toString('base64url');
178
- map[token] = user;
179
- saveAuth(map);
180
- ok(`Created token for user "${user}":`);
181
- log('');
182
- log(' ' + c.bold(token));
183
- log('');
184
- log(c.dim('Use this token in the Authorization header:'));
185
- log(c.dim(` Authorization: Bearer ${token}`));
186
- log(c.dim('Restart the server for the token to take effect.'));
187
- }
188
-
189
- function authList() {
190
- const map = loadAuth();
191
- const tokens = Object.entries(map);
192
- if (tokens.length === 0) {
193
- info('No auth tokens. Server runs in single-user mode.');
194
- log(c.dim(' Add one with: skillforge auth add <user-id>'));
195
- return;
196
- }
197
- log(c.bold('Auth tokens:'));
198
- for (const [token, user] of tokens) {
199
- log(` ${c.dim(token.slice(0, 16) + '...')} → ${user}`);
200
- }
201
- }
202
-
203
- function authRemove(user) {
204
- if (!user) { err('Usage: skillforge auth remove <user-id>'); process.exit(1); }
205
- const map = loadAuth();
206
- const before = Object.keys(map).length;
207
- for (const [t, u] of Object.entries(map)) {
208
- if (u === user) delete map[t];
209
- }
210
- const removed = before - Object.keys(map).length;
211
- saveAuth(map);
212
- if (removed > 0) ok(`Revoked ${removed} token(s) for "${user}"`);
213
- else info(`No tokens for "${user}"`);
214
- }
215
-
216
- // ---- server lifecycle ----
217
158
  function buildEnv(extra = {}) {
218
- const authMap = loadAuth();
219
159
  return {
220
160
  ...process.env,
221
161
  SKILLFORGE_BUNDLED_SKILLS: path.join(PKG_ROOT, 'skills'),
@@ -223,36 +163,10 @@ function buildEnv(extra = {}) {
223
163
  SKILLFORGE_DB_PATH: path.join(DATA_DIR, 'orchestrator.db'),
224
164
  PYTHONPATH: path.join(PKG_ROOT, 'python'),
225
165
  PYTHONUNBUFFERED: '1',
226
- ...(Object.keys(authMap).length > 0 ? { SKILLFORGE_AUTH_TOKENS: authToEnvVar(authMap) } : {}),
227
166
  ...extra,
228
167
  };
229
168
  }
230
169
 
231
- function startServer({ port = 8000 } = {}) {
232
- setupIfNeeded();
233
- checkApiKey();
234
-
235
- const env = buildEnv({ SKILLFORGE_PORT: String(port) });
236
- const authEnabled = Object.keys(loadAuth()).length > 0;
237
-
238
- info(`Starting HTTP API on http://localhost:${port}`);
239
- log(c.dim(' Live log: skillforge events --watch'));
240
- log(c.dim(` Skills dir: ${USER_SKILLS_DIR} (drop folders here to add)`));
241
- log(c.dim(` Data dir: ${DATA_DIR}`));
242
- log(c.dim(` Auth: ${authEnabled ? 'enabled (bearer token required)' : 'disabled (single-user)'}`));
243
- log('');
244
-
245
- const proc = spawn(
246
- venvPython(),
247
- ['-m', 'uvicorn', 'app.main:app', '--host', '0.0.0.0', '--port', String(port)],
248
- { stdio: 'inherit', env }
249
- );
250
-
251
- proc.on('exit', (code) => process.exit(code || 0));
252
- process.on('SIGINT', () => proc.kill('SIGINT'));
253
- process.on('SIGTERM', () => proc.kill('SIGTERM'));
254
- }
255
-
256
170
  function printMcpConfig() {
257
171
  setupIfNeeded();
258
172
  const useLocal = args.includes('--local');
@@ -313,12 +227,14 @@ function runRouteCmd() {
313
227
  proc.on('exit', (code) => process.exit(code ?? 0));
314
228
  }
315
229
 
316
- function runChat() {
230
+ function runIndexCmd() {
317
231
  setupIfNeeded();
318
- checkApiKey();
319
- const env = buildEnv();
320
- const proc = spawn(venvPython(), ['-m', 'app.cli'], { stdio: 'inherit', env });
321
- proc.on('exit', (code) => process.exit(code || 0));
232
+ const sub = args.slice(1);
233
+ const proc = spawn(venvPython(), ['-m', 'app.index_cli', ...sub], {
234
+ stdio: 'inherit',
235
+ env: buildEnv(),
236
+ });
237
+ proc.on('exit', (code) => process.exit(code ?? 0));
322
238
  }
323
239
 
324
240
  // ---- skill management ----
@@ -341,7 +257,7 @@ function skillsAdd(srcPath) {
341
257
  const dest = path.join(USER_SKILLS_DIR, name);
342
258
  fs.cpSync(src, dest, { recursive: true });
343
259
  ok(`Added skill "${name}" → ${dest}`);
344
- log(c.dim(' Restart the server to pick up the new skill.'));
260
+ log(c.dim(' Restart skillforge mcp (or trigger catalog reload) to pick up the new skill.'));
345
261
  }
346
262
 
347
263
  function skillsList() {
@@ -373,7 +289,7 @@ function skillsRemove(name) {
373
289
  }
374
290
  const target = path.join(USER_SKILLS_DIR, name);
375
291
  if (!fs.existsSync(target)) {
376
- err(`No user skill named "${name}". Bundled skills cannot be removed (use disable_skill via MCP or HTTP API).`);
292
+ err(`No user skill named "${name}". Bundled skills cannot be removed (use disable_skill via MCP).`);
377
293
  process.exit(1);
378
294
  }
379
295
  fs.rmSync(target, { recursive: true, force: true });
@@ -398,10 +314,9 @@ ${c.bold('Run modes:')}
398
314
  skillforge --help This message (recommended first step)
399
315
  skillforge mcp MCP stdio — primary integration for Claude / Cursor
400
316
  skillforge mcp config [--local] [--with-anthropic] Print JSON for MCP host (merge into mcp.json)
401
- skillforge start [--port=8000] Optional HTTP API (no web dashboard)
402
317
  skillforge events [--watch] [--limit=N] [--verbose] [--user=…] Live routing log + usage (see --help)
403
- skillforge route [words…] [--project-root=…] [--session-id=…] Route a prompt (see skillforge route --help)
404
- skillforge chat Dev harness (needs start + ANTHROPIC_API_KEY)
318
+ skillforge route [words…] [--project-root=…] [--include-project-rag] Route a prompt (see skillforge route --help)
319
+ skillforge index --project-root=… [--reset] [--stats-only] Index repo text for include_project_rag
405
320
 
406
321
  ${c.bold('Skills:')}
407
322
  skillforge skills list List bundled and user skills
@@ -414,11 +329,6 @@ ${c.bold('Skill packs (install from git):')}
414
329
  skillforge pack update <name> Update a pack
415
330
  skillforge pack remove <name> Uninstall a pack
416
331
 
417
- ${c.bold('Auth (multi-user mode):')}
418
- skillforge auth add <user> Create a bearer token for a user
419
- skillforge auth list List users with tokens
420
- skillforge auth remove <user> Revoke all tokens for a user
421
-
422
332
  ${c.bold('Maintenance:')}
423
333
  skillforge reset Wipe learned state and event log
424
334
  skillforge install Re-run setup (auto-runs on first launch)
@@ -436,29 +346,25 @@ ${c.bold('MCP integration:')}
436
346
 
437
347
  // ---- main ----
438
348
  async function main() {
349
+ dropLegacyAuthJsonIfPresent();
350
+
439
351
  if (args.includes('--help') || args.includes('-h') || cmd === 'help') {
440
352
  showHelp();
441
353
  return;
442
354
  }
443
355
 
444
- const portArg = args.find((a) => a.startsWith('--port='));
445
- const port = portArg ? parseInt(portArg.split('=')[1], 10) : 8000;
446
-
447
356
  switch (cmd) {
448
357
  case undefined:
449
358
  showHelp();
450
359
  break;
451
- case 'start':
452
- startServer({ port });
453
- break;
454
360
  case 'events':
455
361
  runEventsCmd();
456
362
  break;
457
363
  case 'route':
458
364
  runRouteCmd();
459
365
  break;
460
- case 'chat':
461
- runChat();
366
+ case 'index':
367
+ runIndexCmd();
462
368
  break;
463
369
  case 'mcp':
464
370
  if (args[1] === 'config') {
@@ -492,7 +398,7 @@ async function main() {
492
398
  const result = packs.installPack(args[2]);
493
399
  ok(`Installed pack "${result.name}" (${result.version}) with ${result.skills.length} skill(s):`);
494
400
  result.skills.forEach(s => log(' ' + c.dim('•'), s));
495
- log(c.dim(' Restart the server to pick up new skills.'));
401
+ log(c.dim(' Restart skillforge mcp (or trigger catalog reload) to pick up new skills.'));
496
402
  } else if (sub === 'list') {
497
403
  const list = packs.listPacks();
498
404
  if (list.length === 0) {
@@ -522,18 +428,6 @@ async function main() {
522
428
  }
523
429
  break;
524
430
  }
525
- case 'auth': {
526
- const sub = args[1];
527
- if (sub === 'add') authAdd(args[2]);
528
- else if (sub === 'list') authList();
529
- else if (sub === 'remove' || sub === 'rm') authRemove(args[2]);
530
- else {
531
- err(`Unknown auth subcommand: ${sub}`);
532
- log(c.dim(' Try: add, list, remove'));
533
- process.exit(1);
534
- }
535
- break;
536
- }
537
431
  default:
538
432
  err(`Unknown command: ${cmd}`);
539
433
  showHelp();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@heytherevibin/skillforge",
3
- "version": "0.2.1",
4
- "description": "Skill orchestration for Claude: hybrid embedding and router-based routing, MCP and HTTP servers, per-user learning, and a large bundled SKILL.md catalog.",
3
+ "version": "0.7.0",
4
+ "description": "Skill orchestration for Claude: hybrid embedding and router-based routing, MCP stdio server, per-user learning, and a large bundled SKILL.md catalog.",
5
5
  "keywords": [
6
6
  "claude",
7
7
  "skills",