From 63f3b685590cfbe0c6036e26aa4ad0ea89067f30 Mon Sep 17 00:00:00 2001 From: Tobi Lutke Date: Mon, 16 Feb 2026 09:08:28 -0400 Subject: [PATCH] feat: show models in status, improve pre-push hook - Move model info from --help to `qmd status` with live HuggingFace links derived from actual configured URIs - Pre-push hook: handle non-interactive shells gracefully, resolve annotated tags correctly for CI checks --- CHANGELOG.md | 9 +++++ scripts/pre-push | 49 +++++++++++++++--------- skills/release/SKILL.md | 82 +++++++++++++++++++---------------------- src/qmd.ts | 18 ++++++--- 4 files changed, 91 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5a46b2..c424c36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] +### Changes + +- CLI: `qmd status` now shows models with full HuggingFace links instead of + static names in `--help`. Model info is derived from the actual configured + URIs so it stays accurate if models change. +- Release tooling: pre-push hook handles non-interactive shells (CI, editors) + gracefully — warnings auto-proceed instead of hanging on a tty prompt. + Annotated tags now resolve correctly for CI checks. + ## [1.0.5] - 2026-02-16 The npm package now ships compiled JavaScript instead of raw TypeScript, diff --git a/scripts/pre-push b/scripts/pre-push index 5b42f0f..1abad43 100755 --- a/scripts/pre-push +++ b/scripts/pre-push @@ -8,7 +8,28 @@ set -euo pipefail # 2. CHANGELOG.md has a "## [{version}] - {date}" entry # 3. CI passed upstream on GitHub for the tagged commit # -# Installed automatically by: bun install (via prepare script) +# In non-interactive shells (no tty), warnings auto-proceed instead of +# prompting. Hard failures (version mismatch, missing changelog) always block. + +# Detect if we can prompt +CAN_PROMPT=false +if [[ -t 0 ]] || [[ -e /dev/tty ]]; then + # Verify /dev/tty is actually usable + if echo -n "" > /dev/tty 2>/dev/null; then + CAN_PROMPT=true + fi +fi + +prompt_or_continue() { + local msg="$1" + if $CAN_PROMPT; then + read -p "$msg [y/N] " -n 1 -r tmp && mv tmp package.json" exit 1 fi - echo " package.json version: $PKG_VERSION" + echo " package.json version: $PKG_VERSION ✓" # --- 2. CHANGELOG.md must have an entry for this version --- if [[ ! -f CHANGELOG.md ]]; then @@ -51,17 +72,17 @@ while read -r local_ref local_sha remote_ref remote_sha; do echo "Write the changelog entry first. See CLAUDE.md for guidelines." exit 1 fi - echo " CHANGELOG.md: entry found" + echo " CHANGELOG.md: entry found ✓" # --- 3. CI must have passed on GitHub for this commit --- - COMMIT=$(git rev-parse "$TAG" 2>/dev/null || git rev-parse HEAD) + # Resolve annotated tag to its underlying commit + COMMIT=$(git rev-list -n 1 "$TAG" 2>/dev/null || git rev-parse HEAD) if ! command -v gh &>/dev/null; then echo " CI check: skipped (gh CLI not installed)" continue fi - # Check GitHub Actions check runs CHECK_JSON=$(gh api "repos/{owner}/{repo}/commits/$COMMIT/check-runs" 2>/dev/null || echo "") if [[ -z "$CHECK_JSON" ]]; then @@ -72,17 +93,13 @@ while read -r local_ref local_sha remote_ref remote_sha; do TOTAL=$(echo "$CHECK_JSON" | jq -r '.total_count // 0' 2>/dev/null || echo "0") if [[ "$TOTAL" -eq 0 ]] 2>/dev/null; then - # No checks found — commit may not be pushed yet MAIN_SHA=$(git rev-parse origin/main 2>/dev/null || echo "") if [[ "$COMMIT" != "$MAIN_SHA" ]]; then echo "" - echo "WARNING: No CI runs found for $COMMIT and it's not the tip of origin/main." - echo "Push the commit to main first and wait for CI to pass." - read -p "Push tag anyway? [y/N] " -n 1 -r /dev/null || echo "0") @@ -98,15 +115,13 @@ while read -r local_ref local_sha remote_ref remote_sha; do if [[ "$PENDING" -gt 0 ]] 2>/dev/null; then echo "" echo "WARNING: CI still running for commit $COMMIT ($PENDING pending)" - read -p "Push tag anyway? [y/N] " -n 1 -r `: -1. **Install hooks** — run `scripts/install-hooks.sh` (idempotent) -2. **Check for unstaged work** — run `git status`. If there are valuable - unstaged/uncommitted changes that belong in this release, commit them - first (use the commit skill or make well-formed commits directly). -3. **Validate changelog** — check if `## [Unreleased]` has content. - If empty or missing, **write the changelog now** (see below). -4. **Preview** — show the user what will be released (unreleased content + - minor series rollup via `scripts/extract-changelog.sh`) -5. **Ask for confirmation** — do NOT proceed without explicit user approval -6. **Run `scripts/release.sh `** — renames `[Unreleased]`, bumps - version, commits, tags -7. **Remind** — tell the user to `git push origin main --tags` +1. **Gather context** — run `skills/release/scripts/release-context.sh `. + This silently installs git hooks and prints everything needed: version info, + working directory status, commits since last release, files changed, current + `[Unreleased]` content, and the previous release entry for style reference. + +2. **Commit outstanding work** — if the context shows staged, modified, or + untracked files that belong in this release, commit them first. Use the + /commit skill or make well-formed commits directly. + +3. **Write the changelog** — if `[Unreleased]` is empty, write it now using + the commits and file changes from the context output. Follow the changelog + standard below. Re-run the context script after committing if needed. + +4. **Cut the release** — run `scripts/release.sh `. This renames + `[Unreleased]` → `[X.Y.Z] - date`, inserts a fresh `[Unreleased]`, + bumps `package.json`, commits, and tags. + +5. **Show the final changelog** — print the full `[Unreleased]` + + minor series rollup via `scripts/extract-changelog.sh `. + Ask the user to confirm before pushing. + +6. **Push** — after explicit confirmation, run `git push origin main --tags`. + +7. **Watch CI** — after the push, start a background dispatch to watch the + publish workflow. Use `interactive_shell` in dispatch mode with: + ``` + gh run watch $(gh run list --workflow=publish.yml --limit=1 --json databaseId --jq '.[0].databaseId') --exit-status + ``` + The agent will be notified when CI completes and should report the result. If any step fails, stop and explain. Never force-push or skip validation. -### Writing the changelog from git history - -If `[Unreleased]` is empty when the release is triggered, populate it before -continuing: - -1. Find the last release tag: `git describe --tags --abbrev=0` -2. Review commits since then: `git log ..HEAD --oneline` -3. Read the diffs for anything non-obvious: `git log ..HEAD --stat` -4. Write changelog entries following the standard below — group by theme, - explain the why, include numbers, credit contributors. -5. Add optional prose highlights (1-4 sentences) if the release warrants it. -6. Write the entries into `## [Unreleased]` in CHANGELOG.md. -7. Commit the changelog update, then continue with the release. - ## Changelog Standard The changelog lives in `CHANGELOG.md` and follows [Keep a Changelog](https://keepachangelog.com/) conventions. @@ -54,18 +57,15 @@ The changelog lives in `CHANGELOG.md` and follows [Keep a Changelog](https://kee - `## [Unreleased]` — accumulates entries between releases - `## [X.Y.Z] - YYYY-MM-DD` — released versions -The release script renames `[Unreleased]` → `[X.Y.Z] - date` and inserts a -fresh empty `[Unreleased]` section automatically. - ### Structure of a release entry Each version entry has two parts: **1. Highlights (optional, 1-4 sentences of prose)** -Immediately after the version heading, before any `###` section. This is the -elevator pitch — what would you tell someone in 30 seconds? Only include for -releases with significant changes. Skip for small patches. +Immediately after the version heading, before any `###` section. The elevator +pitch — what would you tell someone in 30 seconds? Only for significant +releases; skip for small patches. ```markdown ## [1.1.0] - 2026-03-01 @@ -110,8 +110,7 @@ through parallel contexts. GPU auto-detection replaces the unreliable ## GitHub Release Notes Each GitHub release includes the full changelog for the **minor series** back -to x.x.0. Releasing v1.2.3 includes entries for 1.2.3, 1.2.2, 1.2.1, and -1.2.0. The `scripts/extract-changelog.sh` script handles this, and the +to x.x.0. The `scripts/extract-changelog.sh` script handles this, and the publish workflow (`publish.yml`) calls it to populate the GitHub release. ## Git Hooks @@ -120,15 +119,8 @@ The pre-push hook (`scripts/pre-push`) blocks `v*` tag pushes unless: 1. `package.json` version matches the tag 2. `CHANGELOG.md` has a `## [X.Y.Z] - date` entry for the version -3. CI passed on GitHub for the tagged commit +3. CI passed on GitHub (warns in non-interactive shells, blocks in terminals) -Run `skills/release/scripts/install-hooks.sh` to install (also runs -automatically via `bun install` prepare script). - -## Scripts - -- [`scripts/install-hooks.sh`](scripts/install-hooks.sh) — install/update git hooks -- Project scripts used during release: - - `scripts/release.sh` — rename [Unreleased], bump version, commit, tag - - `scripts/extract-changelog.sh` — extract minor series notes for GitHub release - - `scripts/pre-push` — pre-push validation hook +Hooks are installed silently by the context script. They can also be installed +manually via `skills/release/scripts/install-hooks.sh` or automatically via +`bun install` (prepare script). diff --git a/src/qmd.ts b/src/qmd.ts index 8472388..b6678d2 100755 --- a/src/qmd.ts +++ b/src/qmd.ts @@ -365,6 +365,19 @@ async function showStatus(): Promise { console.log(`\n${c.dim}No collections. Run 'qmd collection add .' to index markdown files.${c.reset}`); } + // Models + { + // hf:org/repo/file.gguf → https://huggingface.co/org/repo + const hfLink = (uri: string) => { + const match = uri.match(/^hf:([^/]+\/[^/]+)\//); + return match ? `https://huggingface.co/${match[1]}` : uri; + }; + console.log(`\n${c.bold}Models${c.reset}`); + console.log(` Embedding: ${hfLink(DEFAULT_EMBED_MODEL_URI)}`); + console.log(` Reranking: ${hfLink(DEFAULT_RERANK_MODEL_URI)}`); + console.log(` Generation: ${hfLink(DEFAULT_GENERATE_MODEL_URI)}`); + } + // Device / GPU info try { const llm = getDefaultLlamaCpp(); @@ -2182,11 +2195,6 @@ function showHelp(): void { console.log(" --max-bytes - Skip files larger than N bytes (default: 10240)"); console.log(" --json/--csv/--md/--xml/--files - Output format (same as search)"); console.log(""); - console.log("Models (auto-downloaded from HuggingFace):"); - console.log(" Embedding: embeddinggemma-300M-Q8_0"); - console.log(" Reranking: qwen3-reranker-0.6b-q8_0"); - console.log(" Generation: Qwen3-0.6B-Q8_0"); - console.log(""); console.log(`Index: ${getDbPath()}`); }