From 55d0ab23362eafa789a6b4c56c34587444d64a98 Mon Sep 17 00:00:00 2001 From: Bozheng Long Date: Wed, 3 Jun 2026 21:19:52 +0800 Subject: [PATCH] docs: design + implementation plan for language auto-detection Co-Authored-By: Claude Opus 4.8 (1M context) --- .../2026-06-03-language-auto-detection.md | 210 ++++++++++++++++++ ...26-06-03-language-auto-detection-design.md | 147 ++++++++++++ 2 files changed, 357 insertions(+) create mode 100644 docs/superpowers/plans/2026-06-03-language-auto-detection.md create mode 100644 docs/superpowers/specs/2026-06-03-language-auto-detection-design.md diff --git a/docs/superpowers/plans/2026-06-03-language-auto-detection.md b/docs/superpowers/plans/2026-06-03-language-auto-detection.md new file mode 100644 index 0000000..8e3e217 --- /dev/null +++ b/docs/superpowers/plans/2026-06-03-language-auto-detection.md @@ -0,0 +1,210 @@ +# Conversation-Language Auto-Detection Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** On the first `/understand` run in a project, detect the user's conversation language and confirm it before generating — without changing anything for English users. + +**Architecture:** A pure prompt-logic change. The entire language decision lives in one place — `SKILL.md` step 3.6. We expand the `if --language NOT specified` branch into a resolution chain that adds conversation-language detection + a non-English-only, first-run-only confirmation gate before the existing `en` default. The resolved value is persisted to `config.json` so the gate fires at most once per project. A README sentence documents it. + +**Tech Stack:** Markdown skill prompt (`SKILL.md`), Markdown docs (`README.md`). No code, no schema, no TypeScript. There is no automated-test hook for skill-prompt behavior, so verification is the manual scenario walkthrough in Task 3 (this matches how the existing `--language` flag is verified). + +--- + +## File Structure + +| File | Responsibility | Change | +|---|---|---| +| `understand-anything-plugin/skills/understand/SKILL.md` | The `/understand` skill prompt; step 3.6 resolves `$OUTPUT_LANGUAGE`. | Rewrite the `If --language is NOT specified` sub-block (currently lines 142–144). | +| `README.md` | User-facing docs; *Localized output* section (~line 121). | Add one paragraph describing first-run auto-detection. | + +No other files. The `outputLanguage` field already exists in `packages/core/src/types.ts:119`; the `locales/.md` injection at `SKILL.md` step 4 (line ~424) and the `$LANGUAGE_DIRECTIVE` template (lines ~148–151) are unchanged and require no edits. + +--- + +## Task 1: Expand the language-resolution branch in SKILL.md + +**Files:** +- Modify: `understand-anything-plugin/skills/understand/SKILL.md` (step 3.6, the `If --language is NOT specified` sub-block) + +- [ ] **Step 1: Confirm the current text is unchanged** + +Run: `sed -n '142,144p' understand-anything-plugin/skills/understand/SKILL.md` + +Expected output (exactly these three lines): +``` + - If `--language` is NOT specified: + - Check `$PROJECT_ROOT/.understand-anything/config.json` for an existing `outputLanguage` field. If present, use that. + - If no stored preference, default to `en` (English). +``` + +If the text differs, STOP and re-read step 3.6 to find the equivalent block before editing — line numbers may have drifted. + +- [ ] **Step 2: Replace the block** + +Use the Edit tool on `understand-anything-plugin/skills/understand/SKILL.md`. + +`old_string` (match exactly, including leading spaces): +``` + - If `--language` is NOT specified: + - Check `$PROJECT_ROOT/.understand-anything/config.json` for an existing `outputLanguage` field. If present, use that. + - If no stored preference, default to `en` (English). +``` + +`new_string` (describes the confirmation *intent* in one instruction rather than +hard-coding a literal prompt box — the skill is a prompt the model interprets): +``` + - If `--language` is NOT specified: + - **Stored preference wins.** If `$PROJECT_ROOT/.understand-anything/config.json` has an `outputLanguage` field, set `$OUTPUT_LANGUAGE` to it and skip the rest. + - **Otherwise detect (first run only).** Infer the predominant language of the user's conversation as an ISO 639-1 code (`$DETECTED_LANG`). If it is `en` or cannot be confidently determined, set `$OUTPUT_LANGUAGE=en` and proceed silently — no prompt (English users see no change). + - **If `$DETECTED_LANG` ≠ `en`, confirm once before analyzing:** tell the user you detected `` and ask whether to generate all content in it; they press Enter/"yes" to accept, or type another language code/name to override (normalize via the friendly-name map above). If running non-interactively (no reply possible), skip the wait, use `$DETECTED_LANG`, and print a one-line notice instead of blocking. + - **Persist** the resolved `$OUTPUT_LANGUAGE` (including `en`) into `config.json` so it never re-prompts for this project. +``` + +- [ ] **Step 3: Verify the edit landed and is well-formed** + +Run: `sed -n '142,170p' understand-anything-plugin/skills/understand/SKILL.md` + +Expected: the new multi-level block is present; the very next content after it is the unchanged `- If `--language` IS specified:` line. Confirm no duplicate `default to en` line remains. + +Run: `grep -c 'default to \`en\`' understand-anything-plugin/skills/understand/SKILL.md` +Expected: `0` (the old standalone default line is gone; the new wording says "set `$OUTPUT_LANGUAGE` to `en`"). + +- [ ] **Step 4: Commit** + +```bash +git add understand-anything-plugin/skills/understand/SKILL.md +git commit -m "feat(understand): detect conversation language on first run + +Expand SKILL.md step 3.6 with conversation-language detection as a +fallback before the en default, gated behind a first-run-only, +non-English-only confirmation. Resolved value is persisted to +config.json so the gate fires at most once. English users see no change. + +Co-Authored-By: Claude Opus 4.8 (1M context) " +``` + +--- + +## Task 2: Document first-run auto-detection in README + +**Files:** +- Modify: `README.md` (*Localized output* section, before the "The `--language` parameter affects:" line, ~line 130) + +- [ ] **Step 1: Confirm the anchor line exists** + +Run: `grep -n 'The `--language` parameter affects:' README.md` +Expected: one match around line 130. + +- [ ] **Step 2: Insert the auto-detection paragraph** + +Use the Edit tool on `README.md`. + +`old_string`: +``` +The `--language` parameter affects: +``` + +`new_string`: +``` +On the **first run** in a project — when you don't pass `--language` and no language is stored yet — `/understand` detects the language you're conversing in. If it isn't English, it asks you to confirm (or override) before generating; English conversations are unaffected. Your choice is saved to `.understand-anything/config.json` and reused on every later run. + +The `--language` parameter affects: +``` + +- [ ] **Step 3: Verify** + +Run: `grep -n 'first run' README.md` +Expected: one match with the new sentence, located just above "The `--language` parameter affects:". + +- [ ] **Step 4: Commit** + +```bash +git add README.md +git commit -m "docs: note first-run conversation-language auto-detection + +Co-Authored-By: Claude Opus 4.8 (1M context) " +``` + +--- + +## Task 3: Manual verification (no automated test hook exists) + +There is no unit-test harness for skill-prompt behavior. Verify by reasoning through each scenario against the edited step 3.6 text and confirming the prompt logic produces the right `$OUTPUT_LANGUAGE` and `config.json` write. Record the result of each in the PR description. + +- [ ] **Step 1: Walk the five scenarios** + +For each, trace the edited step 3.6 and confirm the expected outcome: + +| # | Situation (fresh project, no `config.json`) | Expected | +|---|---|---| +| 1 | Conversation in Chinese, run `/understand` | Gate appears → confirm → `$OUTPUT_LANGUAGE=zh`; `config.json` gets `"outputLanguage":"zh"` | +| 2 | Re-run in the same project (config now has `zh`) | No gate (stored preference wins); generates `zh` | +| 3 | Conversation in English, run `/understand` | No gate; `$OUTPUT_LANGUAGE=en`; English output (no regression) | +| 4 | `--language ja` on a fresh project | No gate (flag wins); `config.json` gets `"outputLanguage":"ja"` | +| 5 | Detected `zh`, user types `en` at the gate | `$OUTPUT_LANGUAGE=en`; `config.json` gets `"outputLanguage":"en"` | + +- [ ] **Step 2: Confirm no-regression invariant** + +Re-read the edited block and confirm: there is NO code path where an English-only conversation with no flag/config produces a prompt. (Scenario 3 must be silent.) This is the single most important property for upstream acceptance. + +- [ ] **Step 3: Optional live smoke test** + +If you want a real run: in a throwaway repo with no `.understand-anything/config.json`, converse briefly in Chinese, run `/understand`, and confirm the gate appears and `config.json` is written with `outputLanguage: "zh"` after confirming. (Skip if a full analysis run is too costly; the trace in Step 1 is sufficient for the PR.) + +--- + +## Task 4: Open the PR + +- [ ] **Step 1: Push the branch** + +```bash +git push -u origin feat/understand-language-auto-detection +``` + +- [ ] **Step 2: Create the PR** + +```bash +gh pr create --title "feat(understand): detect conversation language on first run" --body "$(cat <<'EOF' +## What + +On the first `/understand` run in a project (no `--language` flag, no stored `outputLanguage`), the skill now detects the language of the conversation and — **only when it is not English** — asks the user to confirm or override before generating. The choice is persisted to `.understand-anything/config.json` and the gate never fires again. + +## Why + +The output language defaulted silently to English. A user conversing in Chinese would run the simplest command and only discover the English output after paying the full cost of an analysis run, then had to re-run with `--language zh`. + +## Zero change for English users + +The confirmation gate fires **only** when the detected language is non-English and no language has been chosen yet. English conversations follow the exact same silent `en` path as before. `--language` flag and stored config both take priority over detection. + +## Scope + +- Only `understand-anything-plugin/skills/understand/SKILL.md` step 3.6 + a README sentence. +- No code/schema changes. Other skills and the auto-update hook are unchanged (separate known gaps). +- Non-interactive invocations fall back to the detected language with a notice instead of blocking. + +Design + plan: `docs/superpowers/specs/2026-06-03-language-auto-detection-design.md`, `docs/superpowers/plans/2026-06-03-language-auto-detection.md`. + +🤖 Generated with [Claude Code](https://claude.com/claude-code) +EOF +)" +``` + +--- + +## Self-Review + +**Spec coverage:** +- Resolution chain (param > config > detect > en) → Task 1 Step 2. ✓ +- Non-English-only, first-run-only gate → Task 1 Step 2 (gate "shown ONLY when `$DETECTED_LANG` ≠ `en`"). ✓ +- Persist resolved value incl. `en` → Task 1 Step 2 ("Persist the resolved value"). ✓ +- Edge: uncertain/mixed → `en` silent → Task 1 Step 2 first bullet. ✓ +- Edge: non-interactive fallback → Task 1 Step 2 ("Non-interactive fallback"). ✓ +- Edge: auto-update hook unaffected → no task needed (no code path touched); noted here. ✓ +- `locales/.md` already wired → unchanged, noted in File Structure. ✓ +- README sentence → Task 2. ✓ +- Manual verification scenarios (5) → Task 3. ✓ + +**Placeholder scan:** No "TBD/TODO/handle edge cases" left vague — every edge case has explicit resolved behavior. The ``/`` tokens inside the gate are intentional template placeholders the skill fills at runtime, not plan gaps. ✓ + +**Type/name consistency:** Variable names used consistently — `$OUTPUT_LANGUAGE`, `$DETECTED_LANG`, `outputLanguage` (config key), `--language` (flag). These match the existing names in SKILL.md step 3.6. ✓ diff --git a/docs/superpowers/specs/2026-06-03-language-auto-detection-design.md b/docs/superpowers/specs/2026-06-03-language-auto-detection-design.md new file mode 100644 index 0000000..c3b5278 --- /dev/null +++ b/docs/superpowers/specs/2026-06-03-language-auto-detection-design.md @@ -0,0 +1,147 @@ +# Conversation-Language Auto-Detection for `/understand` + +**Date:** 2026-06-03 +**Status:** Approved — ready for implementation plan +**Scope:** `understand-anything-plugin/skills/understand/SKILL.md` (primary), `README.md` (docs) + +## Problem + +`/understand` generates all LLM-authored content (node summaries, tags, layer +names, guided tours, language notes) in **English by default**. The output +language is controlled solely by an explicit `--language ` flag or a +previously stored `outputLanguage` value in `.understand-anything/config.json`. + +The skill performs **no detection** of the language the user is actually working +in. `SKILL.md` step 3.6 (lines 142–144) hard-defaults to `en` whenever the flag +and config are both absent: + +``` +- If `--language` is NOT specified: + - Check config.json for outputLanguage. If present, use that. + - If no stored preference, default to `en` (English). +``` + +**Observed failure:** A user conversing entirely in Chinese ran the simplest +`/understand` command and received an English knowledge graph. They only +discovered the mismatch after paying the full cost of an analysis run (time + +tokens), then had to re-run with `--language zh`. The default is silent and +undiscoverable — nothing surfaces the language decision at the point it matters. + +## Goal + +On **first analysis of a project**, infer the user's working language from the +conversation and confirm it before spending the analysis budget — without +changing anything for English-speaking users (the project's core audience) and +without breaking non-interactive invocations. + +## Non-Goals + +- No side-by-side bilingual output. Output remains single-language per graph. +- No changes to other content-generating skills (`understand-domain`, + `understand-knowledge`, `understand-explain`, etc.). They currently ignore + `outputLanguage` entirely; that is a separate, known gap left for follow-up + PRs. +- No changes to the autonomous auto-update hook path + (`hooks/auto-update-prompt.md`). It reuses the existing graph and does not + resolve a language, so detection never runs there. Noted as a known separate + gap, out of scope here. +- No code, schema, or TypeScript changes. The `outputLanguage` field already + exists in `ProjectConfig` (`packages/core/src/types.ts:119`). + +## Approach (chosen) + +Add conversation-language **detection as a fallback** in the resolution chain, +placed *before* the `en` default, gated behind a **first-run-only, non-English-only** +confirmation. This was selected over two alternatives discussed: + +- **(A) Personal workaround** (always pass `--language zh` / hand-edit config): + zero upstream value, rejected — the goal is a contributable fix. +- **(B, chosen) Detection-as-fallback in `SKILL.md`:** minimal diff, strictly + better for non-English users, invisible to English users. +- **(C) Always-prompt menu on every run:** rejected — reintroduces friction and + is the most likely to be rejected by upstream maintainers. + +The confirmation gate (rather than silent application) was chosen at the user's +direction for explicitness, but constrained so it never affects English users. + +## Detailed Design + +### Resolution chain (rewrite of `SKILL.md` step 3.6) + +Priority, highest first: + +1. **`--language ` flag present** → normalize via the existing + friendly-name map (line 140), persist to `config.json`, use. *(unchanged)* +2. **`outputLanguage` present in `config.json`** → use it. *(unchanged)* +3. **First run (no flag AND no config) → NEW: detect conversation language.** + - Infer the predominant language of the user's messages in the current + conversation → `$DETECTED_LANG` (ISO 639-1 code, e.g. `zh`, `ja`). + - **If `$DETECTED_LANG` is `en`, or cannot be confidently determined, or the + conversation is mixed/ambiguous** → set `$OUTPUT_LANGUAGE = en`, persist, + **proceed with no prompt.** (Preserves current behavior exactly for the + core audience.) + - **If `$DETECTED_LANG` ≠ `en`** → show the confirmation gate below, resolve + `$OUTPUT_LANGUAGE`, persist to `config.json`. + +In all branches the resolved value is written to `config.json` +(`{"outputLanguage": ""}` merged into existing config), so the gate fires +**at most once per project**. + +### Confirmation gate (only when `$DETECTED_LANG` ≠ `en`) + +Shown before any pipeline phase runs. `SKILL.md` describes the *intent* in one +instruction rather than hard-coding a literal prompt box — the skill is a prompt +the model interprets, so it renders the question itself at runtime. The +instruction tells the model to: + +- State the detected language and ask whether to generate all content in it. +- Accept Enter / "yes" / the detected code as confirmation → `$OUTPUT_LANGUAGE = $DETECTED_LANG`. +- Accept any other language code or friendly name as an override → normalize via + the existing friendly-name map (line ~140) and use it. This doubles as the + "chatting in Chinese but want English docs for my team" escape hatch. + +The instruction text in `SKILL.md` stays in **English** (skill prompts are +English); only the *generated content* becomes the target language. + +### Edge cases + +| Case | Behavior | +|---|---| +| Detection uncertain / mixed languages | Treat as `en`, proceed silently. Never block on a guess. | +| Non-interactive invocation (headless/CI, no user to answer) | Fall back to `$DETECTED_LANG` with a one-line notice instead of hanging on the gate; persist. Confirm is best-effort, never a hard block. | +| Autonomous auto-update hook | Unaffected — that path does not resolve language. | +| Detected language has a `locales/.md` file | Already wired at step 4 (line 424); no change. | +| Detected language has no locale file | `$LANGUAGE_DIRECTIVE` still applies (existing "skip silently" behavior). | + +## Files Touched + +- `understand-anything-plugin/skills/understand/SKILL.md` — rewrite the step 3.6 + `if --language NOT specified` branch into the 4-step resolution chain above. + **Primary change.** +- `README.md` — add a sentence under the *Localized output* section (~line 121) + noting first-run auto-detection. + +No other files. No schema, code, or test-harness files change. + +## Testing / Verification + +Prompt-logic change → no unit-test hook. Verification is manual and documented +in the PR description: + +1. Fresh project, no config, converse in Chinese, run `/understand` → gate + appears → confirm → content generated in `zh`; `config.json` contains + `outputLanguage: "zh"`. +2. Re-run in same project → no gate (config wins). +3. Fresh project, converse in English → no gate, English output (proves no + regression for the core audience). +4. Fresh project, `--language ja` → no gate, flag wins, config stores `ja`. +5. Fresh project, override at the gate (detected `zh`, type `en`) → English + output, config stores `en`. + +## Risks & Upstream Framing + +The main reviewer concern is *any* added interactivity. Mitigations are built in: +the gate is **first-run-only**, **non-English-only**, and **degrades gracefully** +when non-interactive. The PR description leads with: **"Zero behavior change for +English users; the gate only appears when the conversation is non-English and no +language has been chosen yet."**