docs: refresh configuration guides and remove obsolete plans

This commit is contained in:
Julian lechner committed 2026-10-08 15:37:03 +02:00
1 parent 17d01d58f5
commit 8288ea51f0
11 files changed
+174 -2171

No files matched your search

+11 -10
View File
@@ -107,8 +107,7 @@ Install and update also rebuild internally; the explicit build above lets you ch
- Bash 4.3 or newer, with standard utilities such as `awk`, `sed`, and `sha256sum`.
- Python 3.11 or newer for configuration validation and task-state hooks/helpers (`python` for PowerShell scripts, `python3` for Bash scripts).
- `jq` for Claude's Bash status line; without it, that status line produces no output.
- Node.js 22 or newer and `npm` when local QMD search is enabled.
- Node.js 22 or newer and `npm` when MCPorter is selected.
- Node.js 22 or newer and `npm` when local QMD search or MCPorter is selected.
Core Bash setup scripts do not require PowerShell. The credential commands use Windows Credential Manager and require
PowerShell, including when invoked through their Bash wrappers. The PowerShell scripts remain compatible with
@@ -128,6 +127,11 @@ shared definitions -> client adapters -> generated packages -> user installation
| [`scripts/`](docs/scripts.md) | Build, installation, update, diagnosis, and validation scripts. |
| `docs/` | Focused documentation about the configuration design. |
Client settings start in `adapters/codex/config/config.toml` and
`adapters/claude/config/settings.json`. Shared Markdown and metadata define behavior;
adapters supply client-specific formats and placeholders resolved during build or installation.
Generated output is not a source to edit.
The build creates:
```text
@@ -142,16 +146,13 @@ generated/
| Document | Contents |
| --- | --- |
| [Architecture](docs/architecture.md) | Shared sources, adapters, generated packages, and installation. |
| [Scripts](docs/scripts.md) | Commands, checks, tests, and internal script layout. |
| [Configuration](docs/configuration.md) | Client settings, permission defaults, status displays, and verification. |
| [Configuration](docs/configuration.md) | Client settings, permissions, status displays, local QMD search, and verification. |
| [Extending the configuration](docs/extending-configuration.md) | Add shared rules, skills, and agents. |
| [Plugins](docs/plugins.md) | Plugin sources, selection, installation, updates, and removal. |
| [Hooks](docs/hooks.md) | Registered events, session-state helpers, notifications, and optional utilities. |
| [Agents](docs/agents.md) | Agent roles and when delegation is useful. |
| [Skills](docs/skills.md) | Skill categories and conditional rule loading. |
| [Codex adapter](adapters/codex/README.md) | Codex-specific configuration files. |
| [Claude Code adapter](adapters/claude/README.md) | Claude-specific configuration files. |
| [Hook package](shared/hooks/README.md) | Overview shipped with the hook scripts. |
| [Repository instructions](AI-Instructions.md) | Rules for maintaining this repository. |
@@ -233,9 +234,9 @@ Selecting Codex also installs shared skills to `.agents/skills`. An interactive
Codex skills; unchecked extensions are skipped on installation. Normal install and update ask whether to enable
Flashbang, apply the custom status line, and enable local QMD search. Choices are saved for Quickupdate.
The shared skill catalog includes development skills and focused writing, image, site structure, psychology, and
conversion skills. All retained skills are included in both generated clients.
conversion skills. All retained skills are included in both generated clients; installation includes `semantic-search` only when local QMD search is enabled.
The status line defaults to enabled to preserve existing behavior. Disabling it removes the custom client setting and,
for Claude, the setup-owned status line script. Disabling local QMD search on update removes only its setup-owned package, models, and repository collections.
for Claude, the setup-owned status line script. Disabling local QMD search removes its setup-owned package, models, and repository collections once no client still uses it. See [Local QMD search](docs/configuration.md#local-qmd-search) for storage and removal details.
Dry runs preview changes without updating extensions or retrieval. See [Plugins](docs/plugins.md) for client-specific behavior.
## Update Script
@@ -288,8 +289,8 @@ touched. Codex skill backups are kept under `.agents/.ai-config-skill-backups/`.
deleted once it is fully empty; backups already on disk are always kept.
Extensions and skills recorded in the ledger are removed the same way `update` removes a deselected extension:
pre-existing or manually installed ones outside the ledger are left alone, and a skill file modified since it was
installed is kept with a warning. An enabled local QMD search setup is disabled and its setup-owned runtime and
index removed for the selected client(s). The saved Quickupdate selection (`~/.my-ai-configuration/selection.json`)
installed is kept with a warning. Local QMD search is disabled for the selected client(s). Its shared runtime,
owned models, and recorded repository collections are removed only when no client still uses it; the shared QMD database and foreign collections are retained. The saved Quickupdate selection (`~/.my-ai-configuration/selection.json`)
is only removed once uninstalling leaves no client installed at all, so uninstalling just one client out of a
`Both` installation keeps the saved choices for the client that remains.
-3
View File
@@ -1,3 +0,0 @@
# Claude Code adapter
Claude Code uses global `CLAUDE.md`, `settings.json`, skills, and agents under the user profile. This adapter supplies client metadata; semantic content remains in `shared/`.
-3
View File
@@ -1,3 +0,0 @@
# Codex adapter
Codex uses global `AGENTS.md`, `config.toml`, skills, and agent configuration under the user profile. This adapter supplies client metadata; semantic content remains in `shared/`.
-5
View File
@@ -1,5 +0,0 @@
# Architecture
Shared Markdown and metadata define meaning once. Codex and Claude adapters contain only client-specific paths, filenames, and serialization details. The build script renders those inputs into `generated/`. Installation is a separate, explicit step that copies generated files into global user-level locations with backups.
Generated output is ignored because it is reproducible build output. The source files are the reviewable record.
+86 -1
View File
@@ -1,6 +1,19 @@
# Configuration
Codex receives a generated global `AGENTS.md`, agent TOML files, skills, rule files, and a `config.toml` adapter. It sets workspace sandboxing with network access for shell commands (so MCPorter calls such as Context7 work without approval), automatic approval review, `agents.enabled = true`, `agents.max_concurrent_threads_per_session = 5`, and `agents.max_depth = 1`. Claude Code receives a generated global `CLAUDE.md`, agent Markdown files, skills, and `settings.json` with `auto` permissions, explicit `Agent` and `mcporter call context7.*` shell permissions, `mcp.context7.com` as the only allowed sandbox domain where the Claude sandbox is enabled, a five-operation concurrency default, at most five concurrent subagents (`CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS`), and a subagent spawn depth of one (`CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH`), so subagents cannot start further subagents. Installation resolves user paths at runtime; the repository contains no machine-specific paths.
Source templates are [`adapters/codex/config/config.toml`](../adapters/codex/config/config.toml)
and [`adapters/claude/config/settings.json`](../adapters/claude/config/settings.json).
Installation targets `~/.codex` and `~/.claude`; Codex skills go to `~/.agents/skills`.
Choose the PowerShell or Bash package independently of the client.
Codex receives a generated global `AGENTS.md`, agent TOML files, skills, rule files, and a `config.toml` adapter.
It sets workspace sandboxing with shell network access, automatic approval review, `agents.enabled = true`,
`agents.max_concurrent_threads_per_session = 5`, and `agents.max_depth = 1`.
Claude Code receives a generated global `CLAUDE.md`, agent Markdown files, skills, and `settings.json` with
`auto` permissions and explicit `Agent` and `mcporter call context7.*` shell permissions. Where enabled, its sandbox
allows the `mcp.context7.com` domain. The adapter sets a five-operation concurrency default, at most five concurrent
subagents (`CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS`), and a spawn depth of one (`CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH`).
Installation resolves user paths at runtime. Active session policy can further restrict these defaults.
Both clients embed `shared/rules/general.md` and the rule-loading conditions from `shared/global-instructions.md`. Rules for languages and frameworks live in their own directories, with an `index.md` entry point and focused Markdown rules under `references/`. Security has its own directory with a baseline entry point and focused rules for authentication, web, APIs, data, files, network, cryptography, and supply chains. Git, Microsoft, and cross-cutting rules remain directly under `shared/rules/`. Codex receives the full directory tree; Claude generates a skill for each rule and copies supporting references alongside the index skill.
@@ -33,6 +46,78 @@ Model and context-window size are cyan, effort and cumulative tokens magenta, re
Claude's status line displays `permission_mode` or `permissionMode` when Claude provides it, and otherwise shows the configured `auto` review mode.
## Local QMD search
Install and update offer **Yes / No / Auto**, with No as the initial default. This option is separate
from the extension selector. The saved `semantic_retrieval` key keeps its name for compatibility.
Quickupdate reuses the saved choice and device; it does not rerun the benchmark.
- **Yes:** install `@tobilu/qmd@2.8.3` if QMD is absent, copy the shared runtime scripts, configure
and download the three models, and choose GPU when detected or CPU otherwise. An existing QMD
installation is reused, not upgraded by this step.
- **Auto:** prepare the same dependencies, then benchmark GPU when available and CPU if needed.
Both thresholds must pass: median warm search latency at most 5 seconds and embedding throughput
at least 20 chunks/second. Each device has a 10-minute timeout. No passing device disables search
and triggers setup-owned resource cleanup. Auto can download models while answering the setup prompt.
- **No:** omit the search skill and its instruction line, remove the warm-up hook registration,
and disable search for the selected clients. Dry runs do not download, benchmark, or remove QMD resources.
The models are Qwen3-Embedding-0.6B, Qwen3-Reranker-0.6B, and QMD Query Expansion 1.7B.
Exact model URIs live in [`shared/qmd/qmd-lib.mjs`](../shared/qmd/qmd-lib.mjs).
Auto thresholds can be overridden with `QMD_BENCHMARK_MAX_QUERY_SECONDS` and
`QMD_BENCHMARK_MIN_CHUNKS_PER_SECOND`. The benchmark uses a temporary database and a deterministic
100-document fixture; its measurements are not an end-to-end estimate for a user's repository.
### Search and storage
The `semantic-search` skill calls the installed wrapper from the current Git repository:
```text
node "$HOME/.my-ai-configuration/qmd/qmd-search.mjs" --query "Where are temporary upload failures retried?" --top-k 5
```
This command works in PowerShell and Bash. `--root` selects a repository explicitly. Results are JSON
objects with `path`, `line`, `score`, and `snippet`; repository hits use relative paths and other collections
use `qmd://` URIs. Known identifiers still use direct reads or `rg`; unknown identifiers use semantic retrieval first.
A token-protected daemon listens on `127.0.0.1`, refreshes changed repository collections, skips git-ignored
paths, and searches the current repository together with the user's default QMD collections. Repository
collections use `repo-` followed by a 12-character path hash. A SessionStart hook warms the daemon and index
in the background. Models stay loaded until the daemon stops after 30 idle minutes, configurable with
`QMD_IDLE_MINUTES`.
On daemon failure the wrapper tries the QMD CLI against the current repository collection only. This fallback
requires an existing index; it does not create or refresh the collection. If the wrapper fails, the skill
reports the limitation and continues with `rg`.
| Location | Contents |
| --- | --- |
| `~/.my-ai-configuration/qmd/` | Runtime scripts, daemon state, locks, collection ownership, and `warm.log`. |
| `~/.my-ai-configuration/qmd.json` | Selected device, enabled clients, and setup ownership records. |
| `$XDG_CACHE_HOME/qmd/` (default `~/.cache/qmd/`) | Shared `index.sqlite` and model cache. |
| `$QMD_CONFIG_DIR/index.yml` | Configuration when `QMD_CONFIG_DIR` is set; otherwise `$XDG_CONFIG_HOME/qmd/index.yml`, defaulting to `~/.config/qmd/index.yml`. |
Codex's adapter grants writable roots for `~/.my-ai-configuration/qmd` and `~/.cache/qmd`.
A custom cache location needs a matching writable root. QMD also writes its configuration when registering
collections and persisting ignore lists; the resolved configuration directory must be writable under the
active sandbox. These adapter roots do not themselves grant access to `~/.config/qmd`.
Disabling search or uninstalling one client keeps the shared runtime while another client remains enabled.
After the last client disables it, cleanup stops the daemon, removes recorded repository collections and
setup-owned models/package, restores the previous `models:` block, and removes the runtime scripts.
It preserves foreign QMD installations, models, collections, and the shared database. Failed cleanup can leave
resources behind; inspect reported warnings rather than deleting the whole cache. Updates also migrate the
old setup-owned Python retrieval directory and QMD extension ownership records.
### Verification and troubleshooting
Check `~/.my-ai-configuration/qmd/warm.log` for background warm-up errors. First startup or changed repositories
can take longer than a warm query. Windows sandbox setup and child-process pipe restrictions can prevent
repository discovery or daemon startup; see [Permission defaults](#permission-defaults).
Model-free unit tests do not verify downloads, GPU support, sandbox access, or real search quality.
See [QMD tests](scripts.md#qmd-tests) for the separate test commands and dependency setup.
## Verification and installation
The agent chooses the smallest sufficient checks based on behavior, affected callers, risk, reversibility, and uncertainty. Inspection can suffice for a small change. Full builds, broad test suites, and independent reviews are not automatic; explicit user and project requirements remain binding.
+4 -2
View File
@@ -9,9 +9,11 @@ The adapters register these events:
| Codex and Claude | `Stop` | Run the finish notification (`flashbang`). |
| Claude | `PreCompact` | Record the compaction timestamp under the installed configuration root and point to existing workspace task state. |
| Claude | `SessionStart` | Print an absolute pointer if the workspace's `.ai-session/state.json` exists. |
| Codex and Claude | SessionStart | Warm the QMD index and search daemon in the background (only while local QMD search is enabled). |
| Codex and Claude | `SessionStart` | Warm the QMD index and search daemon in the background (only while local QMD search is enabled). |
The main agent alone maintains canonical task state. Hooks never write or summarize it, print its contents, or infer completed verification. They use the hook event's `cwd`, falling back to the process working directory when no workspace is supplied. Malformed event data does not fall back to another project. An absent state file is a successful no-op. Legacy state under the installed configuration root is not automatically adopted; confirm its task before migrating it. The scripts do not grant permissions, bypass approvals, or replace client permission systems.
The main agent alone maintains canonical task state. Hooks never write or summarize it, print its contents, or infer completed verification. The task-state hooks use the hook event's `cwd`, falling back to the process working directory when no workspace is supplied. Malformed event data does not fall back to another project. An absent state file is a successful no-op. Legacy state under the installed configuration root is not automatically adopted; confirm its task before migrating it. The scripts do not grant permissions, bypass approvals, or replace client permission systems.
The QMD warm-up hook uses the process working directory and starts only inside a Git repository when Node.js and the installed wrapper are available. It runs detached; wrapper errors are recorded in `~/.my-ai-configuration/qmd/warm.log`. Disabling local search removes its hook registration. See [Local QMD search](configuration.md#local-qmd-search) for daemon behavior and storage.
When enabled during installation or update, Flashbang runs on `Stop`, after the main agent finishes responding. The default flash lasts about 500 milliseconds. The PowerShell command omits `-WindowStyle Hidden`, which can hide or minimize the shared terminal. The PowerShell hook creates a low-opacity click-through overlay without activating another window when its native GUI APIs are available; unsupported runtimes exit safely. Its native DLL cache is optional: a missing `LOCALAPPDATA` environment variable or an unusable cache falls back to in-process compilation instead of suppressing the notification. The Bash hook uses Python's `tkinter`, uses `xrandr` for multi-monitor bounds when available, and falls back to `notify-send` when the overlay is unavailable. Registered hooks are best effort: errors do not fail a Claude session.
+11 -2
View File
@@ -2,7 +2,7 @@
The user-level plugin manifest is `adapters/plugins.tsv`. It maps each logical extension to the client-specific plugin, skill, or MCP installation method.
The current baseline includes Ponytail, i-have-adhd, Superpowers, Context7, Caveman, MCPorter, Humanizer, Impeccable, and Anthropic Frontend Design. Both scripts operate at user scope and show an extension selector unless running a dry run. Selections are collected before configuration changes. Both rebuild generated packages first.
The current baseline includes Ponytail, i-have-adhd, Superpowers, Context7, Caveman, MCPorter, Humanizer, Impeccable, and Anthropic Frontend Design. Install and update operate at user scope and show an extension selector unless running a dry run or Quickupdate. Selections are collected before configuration changes. Both rebuild generated packages before synchronizing client files and extensions. Choosing Auto for local search can prepare and benchmark QMD earlier, during option selection.
### Automatic workflow integration
@@ -46,7 +46,16 @@ Codex receives Caveman through its native plugin adapter. Humanizer, Impeccable,
On Windows, installation and update repair the known Caveman 1.0.0 hook launcher in setup-owned Codex installations so it resolves the plugin path under PowerShell as well as Command Prompt. Custom commands and foreign installations are preserved. Review the changed hook definitions in Codex `/hooks`; setup does not grant hook trust automatically.
Normal installation and update ask whether to enable Flashbang, apply the custom status line, and enable optional local QMD search (Yes/No/Auto, disabled by default). Local search runs [QMD](https://github.com/tobi/qmd) (`@tobilu/qmd@2.8.3`, Node.js 22 or newer, `npm`) with Qwen3-Embedding-0.6B, Qwen3-Reranker-0.6B, and QMD Query Expansion 1.7B. Auto benchmarks GPU first, then CPU, and enables search only when median search latency is at most 5 s and embedding runs at least 20 chunks/s. While it is enabled, both clients receive the `semantic-search` skill, which the general rules require for questions whose identifiers are unknown; disabling local search removes the skill and its instruction line. The skill calls `qmd-search.mjs`, which reuses a background daemon on loopback 127.0.0.1 protected by a per-start token; the daemon keeps the models loaded and exits after 30 idle minutes (`QMD_IDLE_MINUTES`). A SessionStart hook warms the index and daemon in the background. Models and the index live in `$XDG_CACHE_HOME/qmd` (default `~/.cache/qmd`); the Codex `writable_roots` cover only `~/.cache/qmd`, so a custom `XDG_CACHE_HOME` needs its own writable root. Native Windows packages select the recommended elevated Codex sandbox; its setup can require administrator approval (see [Configuration](configuration.md#permission-defaults)). The previous unelevated mode reproduced `spawnSync git EPERM` during repository-root discovery, before the CLI fallback could run. Runtime search under the elevated mode still needs verification after applying the configuration and starting a new session. The wrapper falls back to the slower QMD CLI path for daemon failures, but that path also needs child-process pipes. Repository collections skip git-ignored paths; the daemon keeps that list as the collection's `ignore` entry in QMD's `index.yml`. The setup saves and restores a user's own QMD `models:` block, and removal touches only the setup-owned package, models, and repository collections; foreign installs and collections survive. Updating migrates from the earlier Python stack by removing `~/.my-ai-configuration/semantic-retrieval/` and old QMD extension ledger records. Quickupdate reuses the saved shell, client, status line, Flashbang, local search, and extension choices without opening menus. Disabling the status line removes its custom setting and setup-owned Claude script. Only successful runs save selections; dry runs do not update extensions or local search. Install and update do not modify the Codex or Claude CLI installations.
### Setup options outside the extension selector
Flashbang, the custom status line, and local QMD search have separate prompts during install and update.
Quickupdate reuses their saved choices together with the shell, client, and extension selection. Only successful
runs save selections; dry runs do not update extensions or local search. Disabling the status line removes its
custom setting and setup-owned Claude script.
QMD is managed by the local-search option, not as a plugin. See [Local QMD search](configuration.md#local-qmd-search)
for models, Auto thresholds, runtime behavior, sandbox paths, and ownership-aware removal.
Install and update do not modify the Codex or Claude CLI installations.
Plugin installation requires the corresponding client CLI, network access, and any authentication required by the plugin. Context7 may open an OAuth flow. Codex or plugin hooks may require a trust review in `/hooks`. The scripts never install plugins during a build or doctor run and never install them when `--dry-run` or `-DryRun` is used.
+59 -4
View File
@@ -23,7 +23,7 @@ Run scripts from the repository root. PowerShell and Bash commands have equivale
| `commands/doctor.ps1`, `commands/doctor.sh` | Check generated output and installed configuration. |
| `commands/prompt-budget.ps1`, `commands/prompt-budget.sh` | Track permanent context, skill metadata, the rule catalog, and agent metadata; warn above 10,000 permanent-context tokens, fail when any token estimate exceeds its baseline by 10%, or permanent context exceeds 15,000 tokens. |
| `commands/context-report.ps1`, `commands/context-report.sh` | Report the context installed Claude and Codex actually load, from Claude `/context`, `/skill-doctor`, and its init event, and Codex `debug prompt-input`: categories, active plugins, listed skills, installed skills missing from the listing, and skills listed without description context. `--check` fails on missing or description-less skills. No model request is made. |
| `commands/benchmark.ps1`, `commands/benchmark.sh` | Run the Python, C#/ASP.NET, and TypeScript tasks under `scripts/benchmarks/tasks/` with and without the installed setup. Both arms of a client use one pinned model and reasoning effort; a preflight records what each arm actually loads in `provenance.json` and stops when the arms are not comparable; `--arms setup setup-no-search --tasks 11-* 12-* 13-* --warm-index` compares Claude with and without semantic search on concept-search tasks in a large generated repository, and compare pass rate, rework turns, duration, tokens, tool calls, cost, and workflow checks per task category (`category` in `task.json`: small change, larger implementation, search) (no planning ceremony for a small change, targeted verification for a bugfix, bounded delegation for a larger change). Starts real model sessions; use `--dry-run` to list runs. The Codex baseline needs one `codex login` with `CODEX_HOME` set to `--codex-baseline-home`. |
| `commands/benchmark.ps1`, `commands/benchmark.sh` | Compare real Claude/Codex sessions with and without the setup. See [Session benchmarks](#session-benchmarks) for isolation, costs, and the search comparison. |
| `commands/repository-context.ps1`, `commands/repository-context.sh` | Refresh the local repository-context cache. |
| `commands/session-state.ps1`, `commands/session-state.sh` | Read or update the local agent session state. |
| `commands/telemetry.ps1`, `commands/telemetry.sh` | Append a local telemetry event. |
@@ -32,7 +32,14 @@ Run scripts from the repository root. PowerShell and Bash commands have equivale
| `commands/add-credentials.ps1`, `commands/add-credentials.sh` | Store a client-scoped tool credential in the operating-system credential store. |
| `commands/remove-credentials.ps1`, `commands/remove-credentials.sh` | Delete a selected client-scoped credential from the operating-system credential store. |
Prompt KPIs estimate tokens as UTF-8 bytes divided by four, using the larger shell package per client. Skill metadata counts `SKILL.md` frontmatter outside rule skills; the rule catalog counts all generated rule entry files but excludes references, so it measures the available catalog size rather than rules loaded in a typical session; agent metadata counts frontmatter for Claude and `name`/`description` for Codex. All metrics fail CI above baseline +10%; permanent context also warns above 10,000 tokens and fails above 15,000 tokens. The baseline covers the retained skill catalog. Project-specific instructions, external plugin prompt bodies, and live MCP/tool schemas are not measured.
Prompt KPIs estimate tokens as UTF-8 bytes divided by four, using the larger shell package per client.
Skill metadata counts every `SKILL.md` frontmatter, including Claude's generated rule skills. Permanent context
combines global instructions, skill and agent metadata, and the security baseline loaded for development tasks.
The rule catalog counts Codex rule files outside `references/` and Claude's generated `rules-*/SKILL.md` entries;
it measures available guidance, not all rules loaded in a typical session. Agent metadata counts Claude frontmatter
and Codex `name`/`description` fields. All metrics fail above baseline +10%; permanent context also warns above
10,000 tokens and fails above 15,000. Project instructions, external plugin bodies, and live MCP/tool schemas
are excluded. `context-report` complements this estimate with installed-client discovery.
Custom instructions remain under `~/.my-ai-configuration/instructions/`; installation and update scripts never reset their contents. Only clients with a nonempty instruction file receive a startup rule in their installed `AGENTS.md` or `CLAUDE.md`. That rule requires immediate loading before any response or task work and keeps the instructions active throughout the session, including after compaction. Adding or removing instructions refreshes the rule in an existing installation; empty or missing instruction files leave no rule. Generated packages contain no personal instruction rule. Changes apply when the client next loads its global instructions.
To undo an added instruction, pass the same text to `remove-instruction` with `-Client`/`--client` and `-Instruction`/`--instruction`. It removes the last exact matching block per selected client and preserves other text. A missing match leaves the file unchanged.
@@ -63,7 +70,8 @@ Gitea Actions runs the repository checks from `.gitea/workflows/ci.yml`.
| `tests/test-quick-update.py` | Validate saved Quickupdate choices. |
| `tests/test-remove-customizations.py` | Validate targeted instruction and credential removal without accessing the real credential store. |
| `tests/test-quick-update.ps1`, `tests/test-quick-update.sh` | Run the Quickupdate test through the relevant shell. |
| `tests/test-qmd-*.mjs` | Validate the QMD library, search, daemon, warm-up, and benchmark scripts. |
| `tests/test-qmd-lib.mjs`, `test-qmd-daemon.mjs`, `test-qmd-search.mjs`, `test-qmd-warm.mjs`, `test-qmd-benchmark.mjs` | Model-free QMD unit tests; require the YAML dependency. |
| `tests/test-qmd-integration.mjs` | Manual integration test using real QMD and downloaded models; excluded from CI. |
| `tests/test-qmd-setup.py` | Validate QMD installation, migration, and removal. |
| `tests/test-session-state.py` | Validate session-state helpers. |
| `tests/test-shell-packages.ps1`, `tests/test-shell-packages.sh` | Validate generated package contents and client settings. |
@@ -71,8 +79,55 @@ Gitea Actions runs the repository checks from `.gitea/workflows/ci.yml`.
| `tests/test-install-selections.ps1`, `tests/test-install-selections.sh` | Validate dry-run shell and client selections. |
| `tests/test-command-summaries.ps1`, `tests/test-command-summaries.sh` | Validate doctor, install, and update summary output. |
| `tests/test-telemetry.py` | Validate telemetry output. |
| `tests/test-personal-instructions.py` | Validate startup instruction pointers and preservation of personal instruction files. |
| `tests/test-user-customizations.py` | Validate user instruction overlays and credential helpers. |
## QMD tests
The CI `qmd` job installs only `yaml@2.9.0` into an isolated directory, sets `QMD_PACKAGE_DIR` to that directory,
and runs these five files. It does not install QMD or download models. With a complete global QMD installation,
the same tests resolve YAML from QMD automatically:
```text
node --test scripts/tests/test-qmd-lib.mjs scripts/tests/test-qmd-daemon.mjs scripts/tests/test-qmd-search.mjs scripts/tests/test-qmd-warm.mjs scripts/tests/test-qmd-benchmark.mjs
```
Without QMD installed, use the dependency setup from [the CI workflow](../.gitea/workflows/ci.yml).
`QMD_PACKAGE_DIR` must point to the directory containing the test dependency's `node_modules` directory.
A missing YAML dependency fails `test-qmd-lib.mjs` before its tests load.
Run the integration test separately, only with a complete QMD installation and resources for model downloads.
Unset `QMD_CONFIG_DIR` first so the test uses its temporary configuration rather than an inherited user path.
Clear a unit-test-only `QMD_PACKAGE_DIR` override as well:
```text
node --test scripts/tests/test-qmd-integration.mjs
```
The integration test creates a temporary home and repository, downloads models, searches through the daemon,
and stops it after successful assertions.
Its temporary data is not automatically removed. `QMD_TEST_CACHE` can select an existing QMD cache parent;
the test uses its `qmd/` subdirectory, so avoid pointing it at a cache containing unrelated data.
## Session benchmarks
The A/B benchmark runs Python, C#/ASP.NET, and TypeScript tasks under `scripts/benchmarks/tasks/`.
These are real model sessions and can incur usage charges; `--dry-run` lists planned runs.
Both arms use the same model and reasoning effort for each client. Preflight records loaded configuration in
`provenance.json` and normally stops when the arms are not comparable. Reports compare pass rate, rework turns,
time, tokens, tool calls, cost, and workflow checks per task category.
For Claude's semantic-search comparison, pass these arguments to the shell's benchmark command:
```text
--clients claude --arms setup setup-no-search --tasks 11-* 12-* 13-* --warm-index
```
`setup-no-search` is Claude-only. Warm-up starts QMD and indexes the fixture before timing setup-arm sessions.
The Codex baseline requires a separate login under `--codex-baseline-home`; set `CODEX_HOME` to that path for
`codex login`. Supply `--codex-model` if the installed configuration does not select a model.
Benchmark fixture READMEs describe the starting task state and are retained as test inputs.
## Internal helpers
| Script | Purpose |
@@ -90,4 +145,4 @@ Gitea Actions runs the repository checks from `.gitea/workflows/ci.yml`.
| `lib/selection-state.py` | Read and write Quickupdate state. |
| `lib/qmd.sh`, `lib/qmd.ps1` | Invoke local QMD search from the installation flow. |
| `lib/qmd.py` | Install, migrate, benchmark, and remove local QMD search. |
| `shared/qmd/*.mjs` | Installed QMD scripts: search CLI, daemon, warm-up, benchmark, and shared library. |
| [`shared/qmd/`](../shared/qmd/) (repository root) | Installed QMD scripts: search CLI, daemon, warm-up, benchmark, and shared library. |
File diff suppressed because it is too large. Load diff
@@ -1,168 +0,0 @@
# QMD local search design
Date: 2026-10-08
Status: approved in conversation, pending written-spec review
## Goal
Replace the Python semantic retrieval stack with QMD as the only local search engine, used by Claude Code and Codex alike.
| Role | Model | QMD URI |
|---|---|---|
| Embedding | Qwen3-Embedding-0.6B | `hf:Qwen/Qwen3-Embedding-0.6B-GGUF/Qwen3-Embedding-0.6B-Q8_0.gguf` |
| Reranking | Qwen3-Reranker-0.6B | `hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/qwen3-reranker-0.6b-q8_0.gguf` |
| Query expansion | QMD Query Expansion 1.7B | `hf:tobil/qmd-query-expansion-1.7B-gguf/qmd-query-expansion-1.7B-q4_k_m.gguf` |
Success criteria:
- No Python model code, virtual environment, or Python model dependency remains.
- QMD is no longer an extension in the plugin selector; the local-models question alone controls it.
- When enabled, agents use the search skill for every question whose identifiers are unknown, and the search answers in seconds because the index is warm and the models are loaded.
- When disabled or unsuitable, no skill, hook, instruction line, QMD package, or model installed by the setup remains.
Motivation from the 2026-10-08 Claude A/B benchmark: the Python search was never used in the three concept-search tasks, while the setup's fixed context made every run about 2.5 times more expensive in input tokens. A permanent MCP server would add to that cost, so the integration is a skill over a CLI wrapper.
## Decisions
1. Search scope: primarily the current Git repository; additionally user-registered QMD collections (`includeByDefault: true`), ranked together in one query.
2. Index location: one global QMD index (`~/.cache/qmd`). Each repository is a collection named `repo-<short hash of the absolute repository path>` with `includeByDefault: false`. No files are written into repositories.
3. Agent access: an own `semantic-search` skill for both clients that calls a Node wrapper. QMD's `qmd@qmd` plugin and MCP tools are not used.
4. Warm-up: a SessionStart hook indexes in the background and starts an own search daemon built on the QMD SDK so all three models stay loaded. QMD's HTTP daemon (`qmd mcp --http`) is not used: its `POST /query` accepts only pre-typed `lex`/`vec`/`hyde` searches and never runs query expansion.
5. Daemon lifetime: the daemon exits by itself after 30 idle minutes (`QMD_IDLE_MINUTES`).
6. Auto mode: benchmark GPU first, then CPU; both thresholds must pass; if no device passes, remove what the setup installed and continue as No.
## Components
### Removed
- `shared/retrieval/` (`server.py`, `benchmark.py`, `requirements.txt`).
- `scripts/lib/semantic-retrieval.py`, `.sh`, `.ps1`.
- `scripts/tests/test-retrieval-server.py`, `scripts/tests/test-semantic-retrieval.py`.
- The `retrieval` job in `.gitea/workflows/ci.yml`.
- The `QMD` row in `adapters/plugins.tsv` and QMD-specific ownership in `scripts/lib/managed-extensions.py`, `scripts/lib/plugins.sh`, and `scripts/lib/plugins.ps1`.
- Qwen/Python references in `README.md`, `docs/plugins.md`, `docs/scripts.md`, and other affected documentation.
### Added under `shared/qmd/`
- `qmd-daemon.mjs`: long-lived search process using the QMD SDK (`createStore({ dbPath, configPath })`, `store.search({ query, collections })` with expansion and reranking, `store.update()`, `store.embed()`). Listens on `127.0.0.1` only, on a port chosen at start; writes `{port, token, pid}` to `~/.my-ai-configuration/qmd/daemon.json` readable only by the user; rejects requests without the token header. Exits after `QMD_IDLE_MINUTES` (default 30) without a request. A second start while one is healthy is a no-op.
- `qmd-search.mjs`: ensures the repository collection, refreshes it incrementally, sends the search to the daemon, starts the daemon if it is not running, and falls back to `qmd query --json` when the daemon cannot be used. Collections searched: the repository collection plus every collection with `includeByDefault: true`. Output: JSON list of `{path, line, score, snippet}` with repository-relative paths, best first.
- `qmd-warm.mjs`: started detached by the SessionStart hook. Starts the daemon with the stored device if it is not running and asks it to refresh and embed the repository collection. A per-repository lock file prevents parallel indexing from concurrent sessions.
- `qmd-benchmark.mjs`: the Auto benchmark (see below).
- Thin `.sh` and `.ps1` entry points for every script, as the repository requires.
### Changed
- `shared/skills/research/semantic-search/SKILL.md`: rewritten for QMD; name kept so references and the A/B benchmark stay stable.
- `scripts/lib/qmd.sh`, `scripts/lib/qmd.ps1`: install, update, benchmark, and removal, called from `install` and `update`.
- Hooks: SessionStart entries for Claude and Codex are generated only when local models are enabled.
- `shared/rules/general.md` (and the generated global instructions): the semantic-search line becomes mandatory and is generated only when the skill is installed.
- `scripts/benchmarks/ab.py`: `setup-no-search` blocks the skill and wrapper instead of the Python CLI; `--warm-index` runs `qmd-warm.mjs`.
### Repository collection
- Name: `repo-<first 12 hex characters of SHA-256 of the absolute repository path>`.
- Mask: `**/*.{md,py,cs,ts,tsx,js,jsx,mjs,go,rs,java,ps1,sh,json,yml,yaml,xml,sql}`.
- Chunking: `--chunk-strategy auto` (AST chunking where QMD supports it; regex chunking elsewhere, including C#).
- QMD's built-in exclusions apply (`node_modules`, `.git`, `.cache`, `vendor`, `dist`, `build`).
### Configuration and state
- `~/.config/qmd/index.yml` (or `QMD_CONFIG_DIR`): the setup writes only the `models:` block; other keys and collections are preserved. An unparsable file aborts without writing. The daemon also writes the `ignore` list (the repository's git-ignored paths) of its own `repo-*` collections, because QMD re-syncs SQLite from this file and its `addCollection` write-through drops `ignore`.
- `~/.my-ai-configuration/qmd.json`: `{version, device: "gpu" | "cpu", owned_package: bool, owned_models: [...]}`.
- Device mapping when starting QMD: `gpu` sets `QMD_LLAMA_GPU=auto` (llama.cpp uses all detected GPUs); `cpu` sets `QMD_FORCE_CPU=1`.
## Install and update flow
Question, identical in Bash and PowerShell:
`Enable local QMD search models (Qwen3 embedding, Qwen3 reranker, QMD query expansion)? [y/N/a]`
- The default is the stored selection. The selection key `semantic_retrieval` and the `-SemanticRetrieval` / `--semantic-retrieval` parameters keep their names for compatibility.
- An update that keeps the stored selection does not rerun the benchmark; the benchmark runs only when `a` is chosen explicitly.
Prerequisites: Node.js 22 or newer and `npm`. Missing with Yes: abort with a clear message before any change. Missing with Auto: treat as unsuitable (No) and report the reason.
Yes:
1. `npm install -g @tobilu/qmd@<pinned version>`; record ownership only if QMD was not already installed.
2. Write the `models:` block into `index.yml`.
3. `qmd pull`.
4. Device: `gpu` when QMD's device probe (`qmd doctor`) reports a usable GPU, otherwise `cpu`. No benchmark.
5. Install skill, hook, scripts, and the mandatory instruction line.
Auto:
1. Steps 1 to 3 as for Yes.
2. Benchmark on GPU; if no GPU exists or it fails, benchmark on CPU.
3. The first passing device continues as Yes with that device.
4. If none passes: report the measurements, delete the models the setup downloaded, uninstall QMD only if the setup installed it, restore the configuration, store the selection as `false`, and continue as No.
No:
- Remove skill, hook, scripts, and instruction line; stop a running daemon.
- Remove the setup-owned QMD package and setup-owned models.
- Never remove a QMD installation, model, or collection the setup did not create.
Migration on the first update after this change:
- Remove the Python installation: `~/.my-ai-configuration/semantic-retrieval/` (virtual environment, model cache, data), its daemon, and `semantic-retrieval.json`.
- Remove previous QMD extension records (Claude plugin `qmd@qmd`, Codex QMD record) from the extension state. If the answer is Yes or a passing Auto, the existing npm package is adopted by the new path instead of being uninstalled.
Dry run: print every step as `DRYRUN ...`; no download, benchmark, or removal.
## Auto benchmark
- Fixture: a small fixed document set shipped with the scripts, indexed into a temporary named index (`qmd --index ai-config-benchmark`), removed afterwards.
- Models are warmed before measuring.
- Search: median latency of three `qmd query` runs with expansion, vector search, and reranking of 40 candidates must be at most 5 s (`QMD_BENCHMARK_MAX_QUERY_SECONDS`).
- Indexing: embedding throughput must be at least 20 chunks per second (`QMD_BENCHMARK_MIN_CHUNKS_PER_SECOND`).
- Order: GPU first, then CPU. Timeout per device: 10 minutes; a timeout fails that device.
- Result: JSON `{device, querySeconds, chunksPerSecond, passed}` per measured device, printed in the install summary.
## Runtime
SessionStart hook (Claude and Codex, `.sh` and `.ps1`):
- Runs only inside a Git repository. Starts `qmd-warm.mjs` detached and returns immediately with exit code 0.
- Errors go to `~/.my-ai-configuration/logs/qmd-warm.log`; the hook never blocks or fails a session.
Search (`qmd-search.mjs`):
1. Refresh the repository collection when files changed since the last refresh (`git status` and modification times), then `qmd embed` for it.
2. Query the daemon; start it if it is not running; fall back to `qmd query --json` when the daemon cannot be reached or started.
3. Print the JSON results.
Strict skill:
- Description: "MUST use before reading or grepping code whenever the exact symbol, file, or string is not already known: concept questions, unfamiliar areas, 'where/how is X done', bug reports that describe behavior. Use rg only for known identifiers, paths, or exact strings."
- Body: search first with a full behavioral question; open and verify the top hits; then use `rg` for callers and tests. If no relevant hit appears, run one rephrased search before switching to `rg`. The previous cost check (`status`, repository-size thresholds) is removed.
Global instruction line (only when the skill is installed): "When `semantic-search` is installed, run it first for any question whose identifiers are unknown; fall back to `rg` with a brief note only if it fails."
## Error handling
- Model download or `npm` failure during install or update: roll back what this run created. Yes fails the install; Auto treats it as unsuitable and reports the reason.
- Benchmark hang: per-device timeout of 10 minutes.
- Hook: detached, logged, always exit code 0. `qmd embed` uses QMD's 30-minute session cap and resumes incrementally on the next start.
- Wrapper failure: non-zero exit with a short message on stderr; the skill continues with `rg` and states the limitation.
- Stale `daemon.json` (process gone or token rejected): the wrapper deletes it and starts a new daemon; if that fails it uses the CLI fallback.
- `index.yml` that cannot be parsed: abort without writing.
## Testing
- Node unit tests (`node --test`): collection name and mask, benchmark decision (thresholds, GPU before CPU, unsuitable result), daemon idle exit with an injected clock, token rejection, wrapper fallback to the CLI, `index.yml` merge preserving foreign keys.
- Install guard tests in Bash and PowerShell with a mocked `qmd`: Yes, No, Auto pass and fail, dry run, ownership (a foreign QMD survives No), migration (Python installation removed), skill, hook, and instruction line present only when enabled.
- Integration job replacing the `retrieval` CI job: install QMD, pull models (cached), index a small fixture, assert the wrapper returns the expected hit, start and stop the daemon.
- `ab.py`: the `setup-no-search` arm and `--warm-index` use the QMD path.
- `prompt-budget`: the longer skill description changes the baseline; update it deliberately and report the difference.
## Risks to verify during implementation
- Whether the Codex `workspace-write` sandbox can reach the daemon on `127.0.0.1` and read `~/.cache/qmd`. If not, the Codex skill needs an approval rule for the wrapper or must run it outside the sandbox.
- Whether `qmd doctor`'s device probe is reliable enough to choose `gpu` for Yes without a benchmark.
- Disk and VRAM use: about 2.4 GB of models; the daemon holds them in VRAM until the idle stop.
## Out of scope
- Commands for managing user collections; users register them with `qmd collection add`.
- Any MCP integration of QMD.
+3 -1
View File
@@ -2,6 +2,8 @@
This directory contains registered hooks and optional utilities. They do not bypass client permission prompts or grant access.
Both clients register `Stop` (`flashbang`) for finish notifications. Claude also registers `PreCompact` (`Record-Compact`/`record-compact.sh`) and `SessionStart` (`Show-SessionStatePointer`/`show-session-state-pointer.sh`). PreCompact records its timestamp under the installed configuration root. Both session hooks point to an existing workspace `.ai-session/state.json`, using event `cwd` or the process working directory. The main agent owns that state; hooks never write it or print its contents. Missing state is a successful no-op.
When Flashbang is enabled, both clients register `Stop` (`flashbang`) for finish notifications. Claude also registers `PreCompact` (`Record-Compact`/`record-compact.sh`) and `SessionStart` (`Show-SessionStatePointer`/`show-session-state-pointer.sh`). PreCompact records its timestamp under the installed configuration root. Both session hooks point to an existing workspace `.ai-session/state.json`, using event `cwd` or the process working directory. The main agent owns that state; hooks never write it or print its contents. Missing state is a successful no-op.
`Validate-CommandSafety`/`validate-command-safety.sh` and `Invoke-PostChangeVerification`/`invoke-post-change-verification.sh` are not registered or invoked automatically. The safety utility checks selected command-text patterns; the verification utility runs the repository build only when explicitly invoked. `Test-SessionConfig`/`test-session-config.sh` checks repository inputs during a build.
When local QMD search is enabled, both clients also register a `SessionStart` warm-up hook (`Start-QmdWarm.ps1`/`qmd-warm.sh`). It starts the installed Node wrapper in the background from the process working directory, only inside a Git repository. Warm-up errors are recorded in `~/.my-ai-configuration/qmd/warm.log`; they do not fail the session. Disabling local search removes the hook registration.