Skip to content

Release v0.26.0

Choose a tag to compare

@github-actions github-actions released this 01 Jun 14:34
· 14 commits to master since this release

[0.26.0] - 2026-06-01

Theme: dogfood-driven CLI bug sweep. Five user-reported issues from fabricOS dogfood — mostly UX cliffs in init and list-dirs on non-Python projects, plus a navigation-correctness bug in nav-level README_AI.md. One contract change in init --yes so first scan-all --ai works out of the box.

Changed

  • codeindex init --yes now seeds the recommended ai_command (GH #75): the generated .codeindex.yaml includes ai_command: 'claude -p "{prompt}" --model haiku --allowedTools "Read"' so the first codeindex scan-all --ai works without "AI not configured" — the path the codeindex:index skill recommends by default. The documented default in DEFAULT_CONFIG_TEMPLATE and the actually-written value now share a single source of truth (config.RECOMMENDED_AI_COMMAND). Contract change vs pre-0.26: AI used to be opt-in at both layers (yaml absent AND --ai flag required); now opt-in lives only at the CLI flag — --ai is still required to spend tokens, but the yaml no longer blocks first-try success. The pre-existing regression test test_generated_config_no_ai_command was renamed to test_generated_config_seeds_recommended_ai_command and inverted to lock the new contract.

Fixed

  • codeindex init CLAUDE.md injection drops hardcoded .py / .php / .java examples and alternate-backend boilerplate (GH #77, partial): the injected ## codeindex section used to instruct users to "read the actual .py / .php / .java / etc." — wrong on TS / Swift / Go projects — and listed opencode run / gemini -p as ai_command alternatives in every project's CLAUDE.md regardless of which backend the user actually chose. Template now uses language-neutral wording ("read the source files") and links the backend-swap recipe to codeindex --help instead of inlining all options. Two regression tests (test_build_section_does_not_hardcode_language_extensions, test_build_section_does_not_advertise_alternative_backends) lock these absences. Deferred to a follow-up: zh/en locale detection (#77 also asked for the injection to match the host CLAUDE.md's language; that's a distinct design question — heuristic vs --lang flag vs dual templates — and out of this PR's scope).
  • codeindex list-dirs no longer silently returns empty when configured languages doesn't match present files (GH #74): previously, list-dirs printed nothing and exited 0 whenever the language filter excluded everything — indistinguishable from "nothing to index" and the single worst UX class (agents loop through --help / docs / doctor / pipx list debugging; humans give up). New scanner.diagnose_language_mismatch helper walks the include roots, counts actual file extensions, and identifies which codeindex-supported languages would cover them. When list-dirs returns empty AND files-with-known-extensions are present, it now raises a ClickException to stderr with the configured languages, the top file types seen, and the specific languages to add (e.g. "add typescript / javascript to .codeindex.yaml languages:"). Truly empty include roots stay silent + exit 0 — preserving scripts that pipe list-dirs as a "anything to index?" probe.
  • codeindex init now detects TypeScript / JavaScript projects (GH #73): init_wizard.py carried a stale local LANGUAGE_EXTENSIONS map listing only Python / PHP / Java behind a "no parser yet" comment, despite scanner.py having gained TS/JS parsers (and full parser modules existing under src/codeindex/parsers/typescript/). On a TS-only repo, detect_languages() returned [] → no languages: block was written → at scan time the runtime defaulted to DEFAULT_LANGUAGES=["python"] → 0 files matched → silent list-dirs empty (the user-visible symptom). Fix imports LANGUAGE_EXTENSIONS from scanner.py as the single source of truth, so any language the scanner supports is now also detectable by init. Verified end-to-end on a TS fixture (init --yes writes languages: [typescript], list-dirs returns the include root). A new structural test test_init_language_set_covers_scanner_supported_set locks the invariant — adding a language to scanner.py will fail this test until init can also see it, preventing the drift class from recurring.
  • Navigation-level README_AI.md no longer flattens descendant files into ## Files (GH #76): nav-level scans are recursive (per the GH #45 stats note), so parse_results includes every descendant. NavigationGenerator was iterating them with result.path.name only — dropping the path prefix — so a subdir file like src/components/ui/badge.tsx showed up in src/README_AI.md's ## Files as bare badge.tsx. Any agent reading src/README_AI.md then issuing Read src/badge.tsx got a 404 (or worse: a confident wrong-file Read on a name collision). Fix filters parse_results to r.path.parent == dir_path before grouping. Each subdir's files remain available via that subdir's own README_AI.md. Verified on fabricOS: src/README_AI.md ## Files shrank from 33 flattened entries to the single real direct child main.tsx. Test helpers _make_result / _create_mock_parse_result gained an optional parent_dir kwarg so fixtures pass through the new filter when a tmp_path is the dir under test.
  • Structural rewrites no longer wipe AI enrichment (GH #38): a scan-all --ai run injects an <!-- enrichment: ok --> marker and a > description blockquote, but any later structural-only write — a post-commit hook running scan-all without --ai, or Phase 1 of the next --ai run — used to erase both. The idempotent cache then went cold, so the next scan-all --ai re-paid the full N AI calls instead of restoring from cache. SmartWriter.write_readme now captures the ok marker + blockquote before overwriting and re-injects them, so the cache stays warm across non-AI invocations. Unenriched READMEs are untouched (no fabricated marker).