diff --git a/CHANGELOG.md b/CHANGELOG.md index c6ad742..0d9109b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,21 @@ ## Unreleased +## 2.2.1 + +**Maintenance.** Ships the History visibility controls and clears the remaining SPEC audit backlog. + ### Added - **History visibility controls.** The History tab now defaults to a privacy-safe Telemetry view, adds an Exposed view for request/result text that was actually stored, and surfaces the redacted/visible capture setting in context. Rows captured while redacted remain text-free. +### Internal / docs + +- Removed dead release-install shims (`setup/install_release.{cmd,ps1,sh}`, `setup/bootstrap_release.sh`) — superseded by `scripts/install.py`. +- Installer README layout diagram corrected to the flattened `{app}` bundle (matches `installer.iss`). +- Provider roadmap updated — the provider selector + per-provider status UX shipped in 2.0; only side-by-side dual-provider and multi-device sync remain future work. +- New README↔dashboard tab-count parity test guards against tab-list doc drift. + ## 2.2.0 **Prompt builder controls.** Flowkey's `prompt:` mode can now target Claude Code or generic chat tools without exposing raw built-in system-prompt editing. diff --git a/README.md b/README.md index c6e986c..645f5c2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Flowkey is a Windows desktop assistant that adds local-LLM hotkeys for grammar f Everything runs locally through [FastFlowLM](https://fastflowlm.com) (AMD Ryzen AI NPU) or, on machines without the NPU, through [Ollama](https://ollama.com) (CPU/GPU) as a secondary provider. No cloud service, analytics, or telemetry is used by the app. -Current version: `2.2.0` +Current version: `2.2.1` ## What's new in 2.2 diff --git a/SPEC.md b/SPEC.md index c2ea6d2..e3670e8 100644 --- a/SPEC.md +++ b/SPEC.md @@ -16,7 +16,7 @@ Caveman-encoded (compression, not amputation). Paths / ids / action names / numb - LLM: FastFlowLM NPU @ `:52625` | Ollama @ `:11434`, OpenAI-compat `POST /v1/chat/completions` - dashboard: daemon-served `scripts/ui/web/{index.html,app.js,styles.css}`, CSP `default-src 'self'` - paths: `scripts/paths.py` → USER_ROOT/{config,data,logs}; `_version.py` = version src of truth -- version: `2.2.0` (prompt_builder release branch); `2.1.0` released (tag on `7835d4b`); repo `agr77one/Fastflow` +- version: `2.2.1` (maintenance: history-visibility release + audit cleanup); `2.2.0` released (`v2.2.0` on `25b8794`); repo `agr77one/Fastflow` - run tree = `flowkey-pub2` (worktree, branch `live`=origin/main). old `FastFlowPrompt_Local_Setup`=1.5.0 stale. ## §I interfaces @@ -86,16 +86,16 @@ T5|x|overview meeting hours + action-item board + weekly review|V15,V16 T6|x|git autosync: `sync.ps1` + daily task + autostart→flowkey-pub2|V21,V22 T7|x|2.1.0 released (tag on `7835d4b`); 2.1.1 maintenance batch on `fix/2.1.1-meeting-skip-and-audit`|V18,V19 T8|.|installer clean-VM smoke test|— -T9|.|[AUDIT] dead-code: unused daemon helper + 2 AHK wrappers + stale chat-popup config key + obsolete settings ref in test fixture + deprecated install shims|— +T9|x|[AUDIT] dead-code: removed deprecated install shims (`setup/install_release.*`,`bootstrap_release.sh`, superseded by `install.py`); chat-popup key + other residuals already clean in 2.0/2.1 (2.2.1)|— T10|x|[AUDIT-P1] autostart: unify 3 divergent Run keys (daemon/src-installer/pkg-installer) → single HKCU entry|V20,B6 T11|x|[AUDIT] old open_chat default `^+t` in first-run + `grammar_fix` snapshot + web config fallback → all `^!c` (2.1.1)|B5 T12|x|[AUDIT] seed thinner than DEFAULT_CONFIG (deep-merge fills at runtime ∴ harmless) → drift guard freezes known delta in `test_config_seeds` (2.1.1)|— T13|x|[AUDIT] `bootstrap.cmd` hardcoded stale `1.6.0` in build banner → version-neutral `` (2.1.1)|V18 -T14|.|[AUDIT] quality-gate gaps: installer policy drift, autostart reg-name drift, bootstrap output name, README/dashboard tab count|V20 +T14|x|[AUDIT] quality-gate gaps: tab-count drift now guarded by `test_dashboard_tabs_parity`; autostart reg-name (`test_installer_autostart`) + bootstrap name (T13) already covered (2.2.1)|V20 T15|x|[DOCS] dashboard docs: 7 tabs → 8 (added Benchmark) in README (2.1.1)|— T16|x|[DOCS] autostart docs conflict: main says no machine-wide entry; installer docs+impl still describe it → align on HKCU-only|B6 -T17|.|[DOCS] installer layout: build script says flattened, installer.md still shows nested layout|— -T18|.|[DOCS] provider roadmap marks selector/status UX incomplete → update to reflect it exists|— +T17|x|[DOCS] installer README layout diagram → flattened `{app}` bundle, matches `installer.iss` (2.2.1)|— +T18|x|[DOCS] provider roadmap: selector + per-provider status UX marked shipped (2.0); only side-by-side + sync remain (2.2.1)|— T19|x|[DOCS] first-run wizard text "chat popup" → "Open chat" + hotkey now `^!c` (2.1.1)|B5 T20|x|[DOCS] daemon log location — audited 2.1.1: no stale ref in README/docs; nothing to change|— T21|x|prompt_builder cfg + claude_code identity + generic_chat adapter + dashboard controls/preview|V17,V24 diff --git a/docs/prompt-logic-refinement-plan.md b/docs/prompt-logic-refinement-plan.md index 8934da5..6de57df 100644 --- a/docs/prompt-logic-refinement-plan.md +++ b/docs/prompt-logic-refinement-plan.md @@ -1,8 +1,8 @@ # FastFlow Prompt Logic Refinement Plan (rev 2) Date: 2026-07-09 -Target release: **2.2.0** (minor feature — touches the locked built-in prompt path) -Status: LOCAL 2.2.0 RELEASE WORK COMPLETE — remote PR/tag/installer release still pending +Target release: **2.2.0** shipped; **2.2.x** adapter follow-up planned +Status: 2.2.0 released (`v2.2.0`); this document now tracks the next prompt-adapter refinement pass ## Implementation status (2026-07-09) @@ -39,11 +39,13 @@ Verified: - `python tools\prompt_builder_eval.py --live --base-url http://127.0.0.1:52625 --model qwen3.5:4b --bearer flm --json --out data\benchmarks\prompt_builder_eval_flm_20260709.json` → `claude_code` 2/2, `generic_chat` 10/10 -Still pending before publishing 2.2.0: +Follow-up after 2.2.0: -- Commit/push branch, open PR, wait for CI, ruleset-toggle squash merge, tag - `v2.2.0`, build installer, and patch release notes. - Codex/Cursor adapters remain 2.2.x follow-up, as planned below. +- Add clearer dashboard help text explaining when to use Claude Code, Codex, + Cursor, or generic chat output. +- Decide whether extra dashboard knobs are worth the UI complexity after the + Codex/Cursor eval results are available. ## Revision note (what changed from rev 1, after code review) @@ -194,6 +196,177 @@ Anthropic prompting best practices Claude Code prompt library (code.claude.com/docs/en/prompt-library); Cursor rules (cursor.com/docs/rules). +## 2.2.x refinement decision + +Use one internal semantic prompt model, but keep different output adapters. A +single universal prompt shape is useful as a fallback, but it should not be the +primary output for every agent because the agents receive context differently: + +- Claude Code: good fit for explicit XML-like sections. Keep the existing + `///` default for non-regression. +- Codex: good fit for compact Markdown or label sections. Prefer objective, + constraints, likely files/patterns, verification commands, and final-response + expectations. Do not duplicate repo-wide rules that belong in `AGENTS.md`. +- Cursor: good fit for shorter editor-context prompts. Prefer direct action, + focus files/areas, project-rule references, and concise done criteria. +- Generic chat: include more background because it may not have repo memory, + project rules, or editor context. + +Universal semantic core: + +```text +Task / Outcome +Context / Source request +Target files or areas +Constraints / Safety rules +Acceptance criteria +Verification +Final response expectations +``` + +Adapter renderers decide how much of that core to emit and what syntax to use. +The dashboard can expose this as user settings, but it must keep raw built-in +system-prompt editing locked. + +## 2.2.x implementation plan + +### Phase A — Adapter contracts first + +Define the contract before exposing a target in the dashboard: + +- `claude_code`: existing XML contract remains the default identity path. +- `generic_chat`: existing Markdown contract remains shipped. +- `codex`: Markdown/label contract requiring `Task:`, `Context:`, + `Constraints:`, optional `Done when:`, optional `Verification:`, and optional + `Final response:`. No XML required by default. +- `cursor`: concise contract requiring an imperative first line, `Focus + files/areas:` or an explicit project-rule reference, optional `Done when:`, + and a length cap for concise/balanced modes. + +Each contract must reject near-verbatim echoes, code fences around the whole +prompt, `` residue, and invented hard limits not present in user input. + +### Phase B — Backend changes + +- Expand `TARGET_AGENTS` to include `codex` and `cursor` only after their eval + cases exist. +- Split `effective_structure()` from adapter behavior. Shape (`xml`, + `markdown`, `checklist`) is not enough; Codex/Cursor need different section + labels and required fields even when both use Markdown. +- Add adapter-specific target instructions, fallback renderers, validators, and + label repair paths. +- Keep `claude_code` default byte-identical to `CLAUDE_PROMPT_SYSTEM_PROMPT`. +- Keep `target_agent` orthogonal to LLM provider. FastFlowLM/Ollama generate the + prompt; Claude/Codex/Cursor describe where the generated prompt will be used. + +### Phase C — Dashboard settings + +Keep the current safe controls: + +- target agent +- action mode: plan / implement / review / debug / explain +- detail level: concise / balanced / detailed +- structure: agent default / Markdown / XML / checklist +- acceptance criteria, verification, output expectations +- preserve user constraints +- capped user suffix +- deterministic preview + +Add only high-value controls: + +- per-target help text: explain why Claude/Codex/Cursor outputs differ +- optional "verification strictness": off / suggest / require +- optional "context density": minimal / normal / include files-and-risks +- optional saved presets, e.g. "My Codex review prompt" or "Cursor quick fix" + +Do not add raw system-prompt editing for built-in `prompt:` mode. Use custom +modes as the full-custom escape hatch. + +### Phase D — Eval gates + +Extend `tools/prompt_builder_eval.py` before enabling UI options: + +- Add at least 10 Codex cases across plan/implement/review/debug/explain. +- Add at least 10 Cursor cases with short editor-context inputs. +- Run deterministic model-free eval: every fallback must pass its own contract. +- Run live local eval on the current preferred short-task model. +- Ship each new adapter only if it reaches at least 8/10 live valid outputs + with repair enabled and no default Claude regression. + +### Phase E — Docs and rollout + +- README: list supported targets and explain provider vs target-agent + separation. +- SPEC: add invariants for Codex/Cursor target-aware validation and locked raw + system prompt. +- CHANGELOG: add an Unreleased item for the adapters and dashboard help text. +- Keep Codex/Cursor hidden until their eval JSON is committed or attached to the + release evidence. + +## Recommended user-facing copy + +Dashboard help text: + +```text +Target agent controls where the generated prompt will be pasted. It does not +change the local model used to generate it. Claude Code uses XML-style sections, +Codex prefers compact task/context/verification instructions and repo rules from +AGENTS.md, Cursor prefers shorter prompts that reference editor context and +project rules, and Generic chat includes more standalone context. +``` + +Universal fallback note: + +```text +Most coding agents understand Task / Context / Constraints / Done when / +Verification / Final response. Agent default is usually better because each tool +already gets different persistent context. +``` + +## 2.2.x plan — review refinements (2026-07-09) + +The Phase A–E plan above is sound and follows the staged-rollout discipline that +made `generic_chat` succeed. Four specifics to pin before building, so the plan +is build-ready: + +**1. Version & sequencing.** Ship one adapter per release, each behind its own +eval-gated PR — do NOT bundle codex + cursor. Suggested: **`codex` in 2.2.2** +(closest to the proven generic_chat Markdown path), **`cursor` in 2.2.3**. +Each stays hidden from the dashboard `target_agent` selector until its live-eval +JSON is committed (Phase D). `universal` stays internal. + +**2. Machine-checkable contracts (pin the exact predicates, like generic_chat).** +A prose contract isn't testable. Define per adapter, in `ffp_prompt_builder`: + +- `codex` valid = contains `Task:` AND `Context:` AND `Constraints:`; `Done when:` + required iff `include_acceptance_criteria`; `Verification:` required iff + `include_verification`; `Final response:` required iff `include_output_format`; + no XML tags; not a Markdown-fenced whole; not a near-verbatim echo; no + `` residue. +- `cursor` valid = first non-empty line is imperative (starts with a verb, not a + heading); contains `Focus files/areas:` OR an explicit "project rules"/rule + reference; `Done when:` required iff acceptance; visible length ≤ **1200 chars** + for `concise`/`balanced` (no cap for `detailed`); not an echo. + +**3. Gate-fail path is per-adapter, never silent.** On contract failure: +model output → `repair_output(target)` → re-validate → if still invalid, +`render_fallback(target)` (the adapter's own deterministic shape), which by +construction passes its contract (assert this in `test_fallback_shape_passes_its_own_contract`, +extended to codex/cursor). The claude_code identity prompt remains the ultimate +safe default only for the claude_code target — do not cross-fall-back codex → +claude_code (that would paste XML into a Codex prompt). + +**4. Reuse the existing dispatch, don't fork it.** `effective_structure()`, +`validate()`, `render_fallback()`, `repair_output()`, and `has_target_structure()` +already dispatch on `target_agent`; add codex/cursor branches inside them rather +than new parallel code paths. Phase B's "split shape from adapter behavior" = +give each `target_agent` its own required-section set even when two share +`markdown` shape. + +**Effort:** each adapter is ~1 focused PR (contract + renderer + validator + +repair + ~10 eval cases + dashboard unhide + docs). Gate to ship: ≥ 8/10 live on +the preferred model with repair, and zero claude_code default regression. + ## Settings Add a `prompt_builder` config block (defaults above). Allowed values: diff --git a/docs/provider-and-sync-roadmap.md b/docs/provider-and-sync-roadmap.md index ff8b7ab..3bda9cc 100644 --- a/docs/provider-and-sync-roadmap.md +++ b/docs/provider-and-sync-roadmap.md @@ -10,10 +10,15 @@ Implemented on the local-LLM side: - dashboard fallback to the effective local provider instead of stale FLM defaults - dashboard control path to start Ollama when it is the active provider +Shipped since this roadmap was written (2.0): + +- explicit provider selector in Dashboard → Config with per-provider status + (installed / API reachable / active) and provider-scoped controls + (start server, disabled-with-reason for unsupported actions) + Still intentionally incomplete: -- explicit provider switcher UX -- dual-provider selection and comparison on one machine +- dual-provider selection and comparison on one machine (side-by-side) - multi-device sync and shared memory - encrypted/shared storage design @@ -27,19 +32,18 @@ Make provider choice explicit and easy to recover from: - `FastFlowLM only` - `Both installed` -### Next UI additions - -1. Add a provider selector to the dashboard config surface. -2. Show per-provider status rows: - - installed - - API reachable - - active/inactive - - supported actions -3. Add per-provider actions: - - start - - stop where supported - - open install/update docs -4. Make unsupported controls visibly disabled with a short reason. +### UI additions — DONE (2.0) + +1. ✅ Provider selector on the dashboard Config surface. +2. ✅ Per-provider status rows: installed / API reachable / active-inactive / + supported actions. +3. ✅ Per-provider actions: start (incl. `ollama serve`); FLM-only controls + hide/disable when Ollama is active. +4. ✅ Unsupported controls are visibly disabled. + +Remaining UI follow-up: + +- stop-server where supported; inline "open install/update docs" links. ### Runtime rules diff --git a/installer/README.md b/installer/README.md index 828bc58..b5f809c 100644 --- a/installer/README.md +++ b/installer/README.md @@ -125,12 +125,11 @@ the `-PfxPath` parameter — no other changes needed. ``` C:\Program Files\FastFlowPrompt\ (read-only, admin-installed) -├── Flowkey\ PyInstaller bundle -│ ├── ffp-daemon.exe -│ ├── ffp-grammar-fix.exe -│ ├── ffp-first-run.exe -│ ├── _internal\ -│ └── setup\defaults\ +├── ffp-daemon.exe PyInstaller bundle, flattened into {app} +├── ffp-grammar-fix.exe +├── ffp-first-run.exe +├── _internal\ shared Python runtime + bundled datas +├── setup\defaults\ seed config (also lands loose here for paths.py) ├── ahk\ │ └── AutoHotkey64.exe └── scripts\ diff --git a/installer/installer.iss b/installer/installer.iss index 8a8846e..35e24a6 100644 --- a/installer/installer.iss +++ b/installer/installer.iss @@ -41,7 +41,7 @@ #define AppURL "https://github.com/agr77one/Fastflow" #define AppExeName "Flowkey.exe" ; symbolic — actual launchers below ; Keep in lockstep with scripts\_version.py. -#define AppVersion "2.2.0" +#define AppVersion "2.2.1" [Setup] AppId={{8A4F1E6C-9B3D-4E62-9F7A-FASTFLOW140}} diff --git a/installer/sign.ps1 b/installer/sign.ps1 index dbe4c4a..a66ea16 100644 --- a/installer/sign.ps1 +++ b/installer/sign.ps1 @@ -52,7 +52,7 @@ .EXAMPLE # Sign the installer $env:FFP_SIGN_PFX_PASSWORD = "ChangeMe!" - .\sign.ps1 -FilePath ..\out\Flowkey-Setup-2.2.0.exe + .\sign.ps1 -FilePath ..\out\Flowkey-Setup-2.2.1.exe #> [CmdletBinding(DefaultParameterSetName = "Sign")] diff --git a/pyproject.toml b/pyproject.toml index f66233d..116c0e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ build-backend = "setuptools.build_meta" [project] name = "fastflowprompt" -version = "2.2.0" +version = "2.2.1" description = "Local-LLM-powered grammar fix, prompt rewrite, chat, and dashboard for Windows." readme = "README.md" requires-python = ">=3.11" diff --git a/scripts/_version.py b/scripts/_version.py index 7c956cf..f66ef05 100644 --- a/scripts/_version.py +++ b/scripts/_version.py @@ -1,3 +1,3 @@ """Single source of truth for the app version. Read by grammar_fix.py.""" -__version__ = "2.2.0" +__version__ = "2.2.1" diff --git a/setup/bootstrap_release.sh b/setup/bootstrap_release.sh deleted file mode 100644 index 6f26d30..0000000 --- a/setup/bootstrap_release.sh +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -RELEASE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" -CONFIG_EXAMPLE="${RELEASE_DIR}/config/grammar_hotkey.config.example.json" -CONFIG_FILE="${RELEASE_DIR}/config/grammar_hotkey.config.json" -PY_SCRIPT="${RELEASE_DIR}/scripts/grammar_fix.py" -DEFAULT_MODEL="qwen3.5:4b" - -log() { - printf '[ffp-bootstrap] %s\n' "$*" -} - -warn() { - printf '[ffp-bootstrap][warn] %s\n' "$*" >&2 -} - -fail() { - printf '[ffp-bootstrap][error] %s\n' "$*" >&2 - exit 1 -} - -has_cmd() { - command -v "$1" >/dev/null 2>&1 -} - -check_python() { - has_cmd python || fail "python is required. Install Python 3.11+ from https://www.python.org/downloads/windows/." - python - <<'PY' -import sys -if sys.version_info < (3, 11): - raise SystemExit("Python 3.11+ required") -print(f"python={sys.version.split()[0]}") -PY -} - -check_node_optional() { - if ! has_cmd node; then - log "node=not-installed (not required for Flowkey setup)." - return - fi - ver="$(node -v 2>/dev/null || true)" - major="${ver#v}" - major="${major%%.*}" - if [[ -n "$major" && "$major" =~ ^[0-9]+$ && "$major" -lt 20 ]]; then - warn "node=${ver} detected. Recommended if needed by your team tooling: Node.js 20 LTS+." - else - log "node=${ver} (optional)." - fi -} - -check_autohotkey() { - if powershell.exe -NoProfile -Command "$ErrorActionPreference='SilentlyContinue'; if (Get-Command AutoHotkey64.exe -ErrorAction SilentlyContinue -or Get-Command AutoHotkey.exe -ErrorAction SilentlyContinue) { exit 0 }; $paths=@('$env:ProgramFiles\\AutoHotkey\\AutoHotkey64.exe','$env:ProgramFiles\\AutoHotkey\\AutoHotkey.exe','$env:ProgramFiles(x86)\\AutoHotkey\\AutoHotkeyU64.exe','$env:ProgramFiles(x86)\\AutoHotkey\\AutoHotkey.exe'); foreach($p in $paths){ if($p -and (Test-Path $p)){ exit 0 } }; exit 1" >/dev/null 2>&1; then - log "autohotkey=ok" - return - fi - fail "AutoHotkey v2 not found. Install from https://www.autohotkey.com/." -} - -check_flm() { - has_cmd flm || fail "flm command not found. Install FastFlowLM from https://fastflowlm.com/." - log "flm=ok" -} - -ensure_config() { - if [[ ! -f "$CONFIG_FILE" ]]; then - cp "$CONFIG_EXAMPLE" "$CONFIG_FILE" - log "Created config: ${CONFIG_FILE}" - fi -} - -ensure_model() { - local installed - installed="$(flm list --quiet --filter installed 2>/dev/null | tr -d '\r' || true)" - if grep -qi "${DEFAULT_MODEL}" <<<"$installed"; then - log "model=${DEFAULT_MODEL} already installed" - else - log "Pulling model ${DEFAULT_MODEL} (first-time install can take several minutes)..." - flm pull "${DEFAULT_MODEL}" - fi -} - -verify_runtime() { - log "Warming up local server..." - python "$PY_SCRIPT" --server warmup >/dev/null - - local status - status="$(python "$PY_SCRIPT" --app-action status 2>/dev/null || true)" - [[ -n "$status" ]] || fail "Unable to read server status." - log "status=${status}" - - local tmpdir - tmpdir="$(mktemp -d)" - trap 'rm -rf "$tmpdir"' EXIT - - cat >"${tmpdir}/grammar_in.txt" <<'TXT' -this are test sentence for grammer check :) -TXT - - cat >"${tmpdir}/prompt_in.txt" <<'TXT' -build a concise release note prompt for engineering slack -TXT - - python "$PY_SCRIPT" --mode grammar --input-file "${tmpdir}/grammar_in.txt" --output-file "${tmpdir}/grammar_out.txt" >/dev/null - python "$PY_SCRIPT" --mode prompt --input-file "${tmpdir}/prompt_in.txt" --output-file "${tmpdir}/prompt_out.txt" >/dev/null - - [[ -s "${tmpdir}/grammar_out.txt" ]] || fail "Grammar verification failed: empty output." - [[ -s "${tmpdir}/prompt_out.txt" ]] || fail "Prompt verification failed: empty output." - - log "verification=ok" -} - -main() { - log "Starting bootstrap in ${RELEASE_DIR}" - has_cmd powershell.exe || fail "powershell.exe is required on Windows." - - check_python - check_node_optional - check_autohotkey - check_flm - if ! python -m pip install --upgrade "${RELEASE_DIR}"; then - fail "pip install failed for ${RELEASE_DIR}" - fi - ensure_config - if ! command -v ffp-install >/dev/null 2>&1; then - warn "ffp-install is not on PATH; continuing with direct script verification." - ensure_model - verify_runtime - else - ffp-install --phase full - log "After reboot, run: ffp-install --phase postreboot" - fi - - log "Setup complete." - log "Next: launch ${RELEASE_DIR}/scripts/grammarFix.ahk" -} - -main "$@" - diff --git a/setup/install_release.cmd b/setup/install_release.cmd deleted file mode 100644 index f7fbf72..0000000 --- a/setup/install_release.cmd +++ /dev/null @@ -1,54 +0,0 @@ -@echo off -REM Flowkey installer (CMD entry point). -REM -REM Steps: -REM 1. Verify Python 3.11+ is on PATH. -REM 2. pip install the wheel (./release) so entry points land in PATH. -REM 3. Run `ffp-install` for prerequisite checks + config + model pull. -REM -REM Re-run after a reboot with: -REM ffp-install --phase postreboot - -setlocal -REM This installer lives under setup/. SCRIPT_DIR is setup/, so RELEASE_DIR walks up one level. -set "SCRIPT_DIR=%~dp0" -set "RELEASE_DIR=%SCRIPT_DIR%.." -set "SCRIPTS_DIR=%RELEASE_DIR%\scripts" - -where python >nul 2>&1 -if errorlevel 1 ( - echo [Flowkey] Python not found on PATH. - echo Install Python 3.11+ from https://www.python.org/downloads/windows/ - echo Be sure to check "Add Python to PATH" in the installer. - pause - exit /b 1 -) - -for /f "tokens=2 delims= " %%v in ('python --version') do set PYVER=%%v -echo [Flowkey] Python %PYVER% detected. - -echo [Flowkey] Installing fastflowprompt package (pip install)... -python -m pip install --upgrade pip >nul -python -m pip install --upgrade "%RELEASE_DIR%" -if errorlevel 1 ( - echo [Flowkey] pip install failed. Aborting. - pause - exit /b 1 -) - -echo [Flowkey] Running prerequisite checks and config bootstrap... -ffp-install --phase full -if errorlevel 1 ( - echo [Flowkey] Installer reported errors. Review messages above. - pause - exit /b 1 -) - -echo. -echo [Flowkey] First-phase install complete. -echo If a reboot is required for AMD/NPU drivers, reboot now and then run: -echo ffp-install --phase postreboot -echo Otherwise launch the tray app via: -echo "%SCRIPTS_DIR%\grammarFix.ahk" -pause -endlocal diff --git a/setup/install_release.ps1 b/setup/install_release.ps1 deleted file mode 100644 index cfe7ea9..0000000 --- a/setup/install_release.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -# DEPRECATED — this PowerShell installer was replaced by a Python-based one. -# -# The new flow: -# 1. install_release.cmd verifies Python and runs `pip install .` -# 2. The pip install adds `ffp-install` to your PATH. -# 3. `ffp-install` does prerequisite checks, config bootstrap, and model pull. -# -# This shim forwards old invocations to the new installer. - -param( - [ValidateSet('full','precheck','prereboot','postreboot')] - [string]$Phase = 'full', - [switch]$RestartNow -) - -Write-Host "[Flowkey] install_release.ps1 is deprecated. Forwarding to install_release.cmd..." -ForegroundColor Yellow - -$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path - -# If the package is already installed, jump straight to ffp-install for the requested phase. -$ffpInstall = (Get-Command ffp-install -ErrorAction SilentlyContinue) -if ($ffpInstall) { - $args = @("--phase", $Phase) - if ($RestartNow) { $args += "--restart-now" } - & ffp-install @args - exit $LASTEXITCODE -} - -# Otherwise run the full bootstrap (pip install + ffp-install). -& "$scriptDir\install_release.cmd" -exit $LASTEXITCODE diff --git a/setup/install_release.sh b/setup/install_release.sh deleted file mode 100644 index a55a727..0000000 --- a/setup/install_release.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# Flowkey installer (Git Bash / WSL entry point). -# -# Same flow as install_release.cmd: -# 1. Verify Python 3.11+ is on PATH. -# 2. pip install ../release (the wheel). -# 3. Run ffp-install for prerequisite checks + config + model pull. -# -# Pass a phase as the first arg to skip steps: -# ./install_release.sh # full flow (default) -# ./install_release.sh precheck # just verify prereqs -# ./install_release.sh postreboot # after reboot - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -RELEASE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PHASE="${1:-full}" - -if ! command -v python >/dev/null 2>&1; then - echo "[Flowkey] Python not found on PATH." - echo " Install Python 3.11+ from https://www.python.org/downloads/windows/" - exit 1 -fi - -PYVER="$(python --version 2>&1 | awk '{print $2}')" -echo "[Flowkey] Python $PYVER detected." - -echo "[Flowkey] Installing fastflowprompt package (pip install)..." -python -m pip install --upgrade pip >/dev/null -python -m pip install --upgrade "$RELEASE_DIR" - -echo "[Flowkey] Running prerequisite checks and config bootstrap..." -ffp-install --phase "$PHASE" - -echo -echo "[Flowkey] Install step complete." -echo " If a reboot is required for AMD/NPU drivers, reboot now then run:" -echo " ffp-install --phase postreboot" -echo " Otherwise launch the tray app via:" -echo " $SCRIPT_DIR/grammarFix.ahk" diff --git a/tests/test_dashboard_tabs_parity.py b/tests/test_dashboard_tabs_parity.py new file mode 100644 index 0000000..9acf70b --- /dev/null +++ b/tests/test_dashboard_tabs_parity.py @@ -0,0 +1,30 @@ +"""Drift guard (T14): the README dashboard tab list must match the live tabs in +index.html. This catches the recurring "README says 7 tabs, dashboard has 8" +kind of drift (see T15) without a human re-counting on every UI change. +""" + +from __future__ import annotations + +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def _readme_tabs() -> list[str]: + text = (ROOT / "README.md").read_text(encoding="utf-8") + m = re.search(r"\*\*Tabs:\*\*\s*(.+)", text) + assert m, "README no longer has a '**Tabs:**' line" + raw = m.group(1).rstrip(". ") + return [t.strip().lower() for t in raw.split(",") if t.strip()] + + +def _index_tabs() -> list[str]: + text = (ROOT / "scripts" / "ui" / "web" / "index.html").read_text(encoding="utf-8") + return [t.lower() for t in re.findall(r'data-tab="([^"]+)"', text)] + + +def test_readme_tab_list_matches_dashboard(): + readme = _readme_tabs() + index = _index_tabs() + assert readme == index, f"README tabs {readme} != dashboard tabs {index}"