Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)

### Added

- `qmd` crate `ast` feature (default on, forwarded by `qmd-cli`) — tree-sitter AST break points for code files, the engine half of `--chunk-strategy auto` (P6.1). New `chunk::ast` module ports upstream `src/ast.ts`: `detect_language` (11 extensions → 6 languages, `.jsx` shares the TSX grammar and `.js/.mjs/.cjs` the TypeScript grammar, `qmd://` virtual paths, case-insensitive), verbatim `LANGUAGE_QUERIES` compiled once per language (`OnceLock`), upstream `SCORE_MAP`/`ast:<capture>` kinds, strictly-highest-score dedup per position, never-panics empty-vec failure paths. `ast_break_points` returns **UTF-16** positions like web-tree-sitter's `startIndex` (Rust `start_byte` byte offsets are converted, so non-ASCII files match upstream verbatim). Grammar crates pinned to upstream's resolved npm versions (`tree-sitter-typescript` =0.23.2 — JavaScript intentionally absent, `tree-sitter-python`/`-go` =0.25.0, `tree-sitter-rust` =0.24.0, `tree-sitter` 0.26.13 on upstream's 0.26 runtime line); statically linked, so upstream's grammar-load-failure branch is unreachable. `extractSymbols` not ported (upstream Phase-2 stub, D3). `scripts/gen_ast_golden.ts` records upstream `getASTBreakPoints` over an 18-file corpus (all 11 extensions covering every capture name, invalid syntax, empty, CJK/emoji, non-code, `qmd://` path) → `ast_golden.json` replayed verbatim. Chunk-merge integration (`Auto` still degrades to `Regex`) and the `qmd status` AST block land in P6.2.
- P5 leftover test coverage: `mcp_daemon.rs` gains `--index` named-index data-serving, named+default daemons coexisting (separate pidfiles/ports, independent `mcp stop`), `--daemon` over a stale pidfile (rewritten) and over a live-foreign pidfile (foreign process unsignalled); new `mcp_stdio.rs` subprocess suite — EOF breadcrumb prints exactly once, stderr EPIPE still answers + exits 0; `tools.rs` `encode_qmd_path` unit test.

- P5 acceptance review: `qmd-mcp` gains an embeddable `HttpServer` handle mirroring upstream's `HttpServerHandle` (`server.ts:889-1183`) — `HttpServer::bind`/`port()`/`serve(shutdown_future)` let embedders learn the bound port (port 0) and drive shutdown, while `serve_http` remains the CLI convenience (`bind` + SIGINT/SIGTERM future); `HttpOptions` gains `allowed_origins`/`allowed_hosts` (explicit values win over `QMD_ALLOWED_*` env like upstream's `??`, `origin-guard.ts:98-99`). The `qmd://{+path}` decoder now follows JS `decodeURIComponent` semantics exactly — malformed escapes (`%ZZ`, lone `%`, `%FF`, truncated `%E2%82`) and invalid UTF-8 reject with upstream's recorded `-32603 "URI error"` JSON-RPC error instead of passing through literally. The golden fixture grows to 61 stdio responses + 56 HTTP cases across a 4-document corpus (new spaced-path file `notes/External Podcast/2023 April - Interview.md`): percent-encoded (`%2F`/`%20`), double-encoded (`%252F`, single-pass), bare-filename suffix matches for `resources/read` and `get`, the `Did you mean one of these?` suggestion branch, `multi_get` `Errors:` block and `[SKIPPED: …]` marker, edge queries (empty/special/unicode/long/stopwords), and HTTP `tools/call get`. New `crates/qmd-mcp/tests/http.rs` adds the parked-reranker concurrency test (other requests answer while a call blocks in inference), allowlist-override and port-zero tests; `server.rs` gains inflight-gate release tests (idle fast path, panic/error closure releases).

- `qmd mcp --http --daemon` and `qmd mcp stop` (P5.4): the launcher respawns `current_exe` with stdin nulled and stdout+stderr truncated into `$XDG_CACHE_HOME/qmd/mcp[-<index>].log` (`~/.cache/qmd` without XDG), writes `mcp[-<index>].pid`, prints upstream's `Started on http://<host>:<port>/mcp (PID <n>)` + `Logs: <path>` lines and exits — `INDEX_PATH` pins the resolved db so the child never re-parses `--index`. The child detaches its own session (`rustix setsid` behind an env marker, P5-D8 — `CommandExt::pre_exec` is `unsafe` and stays out of scope). `stop` walks upstream's three outcomes — `Not running (no PID file).`, `Cleaned up stale PID file (server was not running).`, `Stopped QMD MCP server (PID <n>).` — all exit 0; a recorded PID is only signalled after the cmdline check (`mcp-pid.ts` ported 1:1: `/proc/<pid>/cmdline`, `ps -o args=` then `-o command=` fallback, the `qmd[.ts|.js]` regex). `--daemon` already-running refuses on stderr with exit 1; `qmd status` gains the green `MCP: running (PID <n>)` line and silently cleans a stale pidfile; a foreground `--http` run unlinks the pidfile when it happens to name itself; `--index` scopes pid/log names (`#772`). New `qmd_mcp::daemon` module (state files, paths, lenient pidfile parse, cmdline identity, `sigterm`, `detach_daemon_child`) with 10 unit tests porting `mcp-pid.test.ts` — including a live symlinked `qmd`-named process upstream couldn't fake; new `mcp_daemon.rs` subprocess suite covers the full lifecycle, `--index` scoping, the `status` line and the log file.
Expand Down
71 changes: 71 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions crates/qmd-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ doc = false # bin `qmd` and lib `qmd` would collide under target/doc/
workspace = true

[features]
default = ["mcp"]
default = ["mcp", "ast"]
mcp = ["dep:qmd-mcp", "dep:tokio"]
ast = ["qmd/ast"]

[dependencies]
clap = { version = "4.6.7", features = ["derive"] }
qmd = { version = "0.6.0", path = "../qmd" }
qmd = { version = "0.6.0", path = "../qmd", default-features = false, features = ["llama"] }
qmd-mcp = { version = "0.6.0", path = "../qmd-mcp", optional = true }
serde_json = { version = "1.0.154", features = ["preserve_order"] }
signal-hook = "0.4.4"
Expand Down
Loading
Loading