Commit graph

13 commits

Author SHA1 Message Date
github-actions[bot]
379bc57ef0
ci: release packages (#1378)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-06 23:18:32 +08:00
liruifengv
fc259abdb4
fix(tui): complete @ file mentions across additional workspace roots with fd (#1408)
* fix(tui): complete @ file mentions across additional workspace roots with fd

When additional workspace directories are added via /add-dir, @ file completion fell back to a readdir-based scanner capped at 2000 entries, so deeply nested files in large projects never appeared. Route @ completion through fd across every root instead, keeping the query pushed down to fd and deduplicating by absolute path. The readdir fallback remains for when fd is unavailable.

* fix(tui): preserve per-root full-path fallback for @ mentions

Address review feedback: decide the scoped-versus-full-path fallback per root instead of globally. When one root has the scoped directory but another does not, the latter still runs a whole-tree --full-path search with the original query, so a match that only exists under that root is not hidden just because a sibling root happens to contain the prefix directory.

* fix(tui): fall back to filesystem when fd binary is not executable

Address review: when fdPath is non-null but the binary cannot be spawned (managed fd removed or lost execute permission), @ completion returned null because pi-tui swallows the spawn error into an empty result, so the catch never ran. Probe fd with accessSync(X_OK) before delegating and use the filesystem fallback when it is not executable, while still returning null for genuine no-match results.

* fix(tui): trust bare fd command names when probing executability

Address review: when fd is discovered on the system PATH, detectSystemFdPath returns the bare name (fd/fdfind). accessSync checked that literal string relative to cwd and never searched PATH, so a valid system fd was treated as unavailable and @ completion fell back to the capped scanner. Trust bare names (spawn resolves them via PATH) and only probe absolute/relative paths, which is how the managed fd is referenced and which can go stale.

* chore: remove accidentally committed plan files

* test(pi-tui): stabilize paste-burst test by freezing the clock

The paste-burst heuristic uses an 8ms inter-character interval that a slow or busy CI runner can exceed between synchronous handleInput calls, which resets the burst and lets Enter submit. Freeze Date so the synchronous keystrokes always register as one burst, making the assertion deterministic.

* chore: ignore top-level plan directory
2026-07-06 15:02:02 +08:00
github-actions[bot]
dfcd6c8ed5
ci: release packages (#1355)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-04 19:17:47 +08:00
liruifengv
23daf0f3c1
revert(pi-tui): restore upstream differential rendering behavior (#1367)
* fix(pi-tui): make the viewport anchor follow above-viewport content shifts

The anchor pins a buffer row index, but an above-viewport length change
shifts the content living at every index below it. The pinned window
then suddenly showed content further along (visible upward creep), and
the rows that slid above the window top were lost: never committed to
scrollback, which holds older bytes at those indices. During streaming,
every above-viewport net shrink (a finished agent row collapsing, a
merged step, a spinner line disappearing) permanently swallowed that
many rows, and the blank area under the input box kept growing.

doRender now scores two hypotheses for such frames — window stayed put
vs window content shifted by the length delta — and when the shift
explains the frame strictly better, moves the anchor with the content:
a pure shift re-anchors with no painting at all (the screen already
shows exactly that content), and a shift with local in-window changes
(spinner/timer rows) repaints the window at the shifted anchor.
Commit order stays continuous, so the exactly-once scrollback invariant
is preserved: no loss, no duplication.

Covered by e2e case07 (above-viewport shift), which fails on the
previous revision; the stale-content unit test now asserts the
follow-the-content window instead of the old swallowed-row behavior.

* revert(pi-tui): restore upstream differential rendering behavior

The fork's viewport/scrollback rendering patches (clamping the diff to
the visible viewport, viewport re-anchoring on collapse, the pinned
anchor with commit-on-advance, cursor visibility guarding, and the
content-shift anchor follow) accumulated interacting edge cases faster
than they could be stabilized: blank screens, duplicated scrollback
spans, vanished rows, and a growing blank area under the input box.

Revert src/tui.ts to the upstream 0.80.2 differential rendering
behavior: a change above the viewport triggers a destructive full
redraw again. Verified line-by-line against the upstream source — the
only remaining divergences are the TypeScript strict-mode syntax
adaptations and the narrow-terminal fixes (Container width clamping
and overwide-line truncation replacing the upstream crash-and-throw),
which are kept.

Also remove the rendering-bug e2e ledger and the shrink test suite
that specified the reverted behavior, restore the pre-fork rendering
tests (the transient-content test asserts the upstream full-redraw
behavior again), and drop the e2e glob from the test script. Editor
input-history and paste-burst changes are untouched.

Known trade-off, accepted for now: the original scroll-position yank
during streaming (destructive redraws emitting ESC[3J) returns; the
rendering rework will restart from this clean baseline.

* chore: downgrade the web thinking-effort changeset to patch
2026-07-04 17:57:43 +08:00
github-actions[bot]
9f4079106c
ci: release packages (#1334)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-03 23:51:39 +08:00
liruifengv
68ad686211
fix(pi-tui): stop scrollback duplication from viewport rewinds (#1353)
* fix(pi-tui): stop scrollback duplication from viewport rewinds

Rewinding the viewport anchor repaints rows that the terminal
scrollback already holds, and the next scroll commits them again —
every rewind duplicated its span. Two paths triggered it during
streaming oscillation (content shrinking then growing back): the
shrink re-anchor rewound immediately, and the clamped differential
path then painted the shifted content through the screen.

Rework the shrink/shift handling around a shared in-place viewport
repaint that never scrolls, with the anchor treated as the scrollback
high-water mark that must never move backward:

- Partial shrinks keep the anchor pinned: the content bottom hovers
  above a bounded blank gap that the next growth naturally fills. No
  rewind means duplication is impossible by construction.
- Only a collapse past the viewport top (compaction, clears) rewinds,
  as nothing sensible could be shown otherwise; the content has
  changed so drastically there that the repainted span is not
  recognizable as a duplicate.
- Above-viewport length changes repaint the visible window in place
  instead of painting through: nothing scrolls, scrollback keeps the
  stale old version, and the anchor only advances when the content
  outgrows the pinned window.
- Deleted-tail changes within a pinned viewport repaint at the pinned
  anchor instead of falling back to a destructive full render.

Pure appends keep flowing through the screen into scrollback, and
equal-length above-viewport changes keep the bounded clamped diff.

* fix(pi-tui): commit skipped rows on anchor advance and guard cursor visibility

Address two review findings on the pinned-anchor rendering:

- An anchor advance (growth past the pinned viewport combined with an
  above-viewport change) repainted the screen in place without
  scrolling, so the rows between the old and new anchor were never
  committed to scrollback and vanished. repaintViewport now paints from
  the old anchor and lets the paint loop scroll the skipped rows out,
  committing each exactly once with fresh content.

- positionHardwareCursor recorded hardwareCursorRow on a logical row
  outside the visible window when the cursor marker sat above a pinned
  viewport (tall editor after a deep shrink), desyncing every later
  differential move. It now hides the cursor and keeps the bookkeeping
  on the real cursor row when the marker is not visible.

Also add an e2e rendering-bug ledger (packages/pi-tui/e2e): one
xterm-emulated repro per production rendering bug, asserting the
renderer invariants (monotonic anchor, exactly-once commit, cursor
bookkeeping sync). The two findings above are case05/case06.

* ci: run the pi-tui node:test suite in a dedicated job

pi-tui's tests (unit + e2e) run on node:test, which the root vitest
run silently skips, so CI never executed them. Add a test-pi-tui job
that runs the package's test script.
2026-07-03 23:49:04 +08:00
liruifengv
9091627257
fix(tui): avoid submitting rapid multi-line paste bursts (#1305)
* fix(tui): avoid submitting rapid multi-line paste bursts

* chore(tui): remove paste burst settings picker

* fix(tui): reset paste burst on DEL backspace
2026-07-03 14:21:02 +08:00
github-actions[bot]
508384502d
ci: release packages (#1291)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-02 21:56:42 +08:00
liruifengv
b40bb71399
fix(pi-tui): repaint viewport in place when content collapses above it (#1315)
* fix(pi-tui): repaint viewport in place when content collapses above it

When content shrinks past the viewport top while a line above the
viewport also changes, the clamped differential path left the render
loop empty, cleared deleted lines past the screen bottom, and desynced
the cursor anchor — leaving the viewport blank with the input box gone
until a full redraw.

Repaint the visible viewport in place for that case (no ESC[3J, so the
scrollback and the user's scroll position are preserved), and clamp
deleted-line clearing to the screen bottom so it can never scroll
untracked.

* fix(kimi-code): clear the screen fully on session reset

The collapse repaint in pi-tui intentionally preserves scrollback, so
/new, /clear, and session switches no longer got a clean screen as a
side effect of the destructive full redraw — the previous session's
text stayed above the welcome banner.

Session resets want a pristine screen, so force a destructive full
render explicitly instead of relying on the renderer's shrink
behavior.

* fix(pi-tui): delete kitty images straddling the viewport top on collapse repaint

A multi-row kitty image can start above prevViewportTop while its
reserved rows are still visible. The collapse repaint's image-delete
range started at prevViewportTop and missed the image line carrying the
id, leaving a stale overlay that also dropped out of
previousKittyImageIds tracking. Widen the range to include such a
straddling block.

* chore: shorten session reset changeset wording

* fix(pi-tui): re-anchor the viewport whenever content shrinks below the screen bottom

previousViewportTop only ever grows during normal rendering, so after a
shrink the content bottom could hover above the screen bottom, leaving
dead rows that nothing repaints. Upstream masked this by frequently
doing destructive full redraws, which re-anchored as a side effect; the
fork removed those redraws without replacing the re-anchoring.

Generalize the collapse repaint into a re-anchor check at the top of
the differential path: whenever prevViewportTop exceeds
max(0, newLines - height), repaint the visible viewport in place with
the tail of the new content. The input area snaps back to the screen
bottom, scrollback and the user's scroll position stay intact (no
ESC[3J), and the previous collapse branch becomes a defensive
destructive fallback.

Update the three renderer tests that encoded the old behavior
(destructive redraw on shrink, viewport hover after clamped shrink) to
assert the re-anchored behavior instead.

* fix(kimi-code): full repaint on ctrl+o expansion toggle

Expanding tool output shifts content above the viewport; the clamped
differential render paints the shifted content through the screen,
stacking a duplicate copy below the stale one in scrollback on every
toggle. The toggle is a deliberate user action (like /clear), so do a
destructive full render instead: scrollback holds exactly one copy and
the expanded output stays readable by scrolling up.

* chore: simplify user-facing changeset wording
2026-07-02 20:52:37 +08:00
liruifengv
77eb3a9fe4
feat(tui): include shell commands in input history (#1295)
* feat(tui): include shell commands in input history

Shell commands entered through the `!` prompt are now saved to input history. Recalling one restores bash mode, and in bash mode Up only cycles through previous shell commands while a normal prompt browses all history.

* docs(interaction): document shell command recall in input history

Note that shell commands are now saved to input history and can be recalled in Shell mode, in both the English and Chinese interaction guides.

* feat(pi-tui): add setHistoryFilter and onRecall to editor history

Add two first-class hooks to the editor's history navigation: setHistoryFilter to limit which entries Up/Down visit, and onRecall to decorate a recalled entry before it is shown. Draft restore, direction-aware cursor placement, and undo behavior are unchanged.

* refactor(tui): use pi-tui history filter for shell command recall

Replace the CustomEditor navigateHistory shadow with pi-tui's setHistoryFilter + onRecall hooks, wired in the editor-keyboard controller. This keeps pi-tui's draft-restore and direction-aware cursor behavior intact (the shadow dropped both) and moves the shell/prompt filtering and mode-restore logic into the business layer.

* feat(pi-tui): save and restore host state with the history draft

Add onHistoryDraftSave/onHistoryDraftRestore hooks so hosts can stash their own state when entering history browsing and restore it when the user navigates back to the draft. The saved host state is discarded when browsing ends any other way (typing, submit), mirroring the editor draft lifecycle.

* fix(tui): restore input mode when returning to the history draft

Wire pi-tui's history draft save/restore hooks to the editor input mode. Without this, recalling a shell entry and then pressing Down back to an empty draft left the editor in bash mode, so the next typed message was submitted as a shell command.

* fix(pi-tui): capture host draft state before running the history filter

Fire onHistoryDraftSave before the history filter runs when entering browse, so the host's filter can read the browse-entry mode rather than a mode that changes as entries are recalled. The captured state is still only committed once a matching entry is found.

* fix(tui): lock history filter to the browse-entry mode

Lock the history filter to the input mode captured when entering browse. Previously the filter read inputMode live, so after recalling a shell entry (which flips to bash mode) a second Up would only show shell commands.
2026-07-02 17:59:26 +08:00
liruifengv
2639786ce5
fix(pi-tui): prevent crashes on very narrow terminals (#1303)
* docs: add pi-tui narrow-width fix plan

* fix(pi-tui): stop wordWrapLine infinite recursion on wide graphemes at width 1

* docs: extend pi-tui narrow-width plan with emoji grapheme regression coverage

* fix(pi-tui): clamp container render width to a minimum of 1

* fix(pi-tui): truncate overwide rendered lines instead of throwing

* perf(pi-tui): fast-path overwide line detection and enlarge width cache

* test(pi-tui): assert exact truncated viewport in overwide line test

* docs: record review amendments in pi-tui narrow-width plan

* test(pi-tui): add editor narrow-width regression tests

* docs: record task 4 review amendments in pi-tui narrow-width plan

* fix(pi-tui): guard blank-line padding against negative widths

* docs: record task 5 review amendments in pi-tui narrow-width plan

* docs(pi-tui): document local divergences from upstream

* chore: add changeset for pi-tui narrow width fixes

* docs(pi-tui): point Text guard test to its actual test file

* test(pi-tui): translate narrow-width test comments to English

* docs: remove internal pi-tui narrow-width plan

* docs(pi-tui): translate AGENTS.md to English
2026-07-02 17:14:11 +08:00
github-actions[bot]
ba7f18b3fb
ci: release packages (#1268)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-02 11:12:20 +08:00
liruifengv
7859b0afe8
feat(kimi-code): vendor @moonshot-ai/pi-tui (#1254)
* chore(kimi-code): upgrade pi-tui to 0.78.1 and adapt native helpers

Bump @earendil-works/pi-tui from ^0.74.0 to ^0.78.1. pi-tui 0.75.5 replaced its koffi-based Windows VT input with a bundled native helper, and 0.76.0 added a darwin native helper for Terminal.app Shift+Enter.

- SEA build: teach native-deps to collect pi-tui's per-target .node files, drop the koffi registry, and add a native-file-only collect mode so only package.json + the target .node ship (28 -> 2 files).

- Redirect pi-tui's absolute-path native require() into the native-asset cache through the Module._load hook, and extend the native smoke test to actually load the helper.

- npm package: ship pi-tui's native/ directory so macOS Terminal.app Shift+Enter and Windows Shift+Tab keep working for npm installs.

* chore: add changeset for pi-tui upgrade

* chore: vendor @earendil-works/pi-tui 0.80.2

Fork the upstream pi-tui source into packages/pi-tui for local modification. Pristine snapshot of @earendil-works/pi-tui@0.80.2; the apps/kimi-code dependency on ^0.78.1 from npm is left unchanged.

* feat(kimi-code): integrate vendored @moonshot-ai/pi-tui

Replace the npm @earendil-works/pi-tui dependency with the vendored @moonshot-ai/pi-tui workspace package so the fork can be modified locally.

- Point apps/kimi-code imports and native-deps at @moonshot-ai/pi-tui.

- Make pi-tui source-first (exports -> src, publishConfig.exports -> dist, mirroring node-sdk) and strict-clean: bracket access for process.env / named capture groups, an override modifier, and non-null assertions for noUncheckedIndexedAccess.

- Bump the root tsconfig target to ES2024 and enable allowImportingTsExtensions (needed for pi-tui's /v regex and .ts imports, which node --test requires).

- Add packages/pi-tui to flake.nix workspaces and exclude the vendored source from oxlint.

* fix(pi-tui): export package.json for native asset resolution

The SEA native-asset collector resolves the package root via
require.resolve('@moonshot-ai/pi-tui/package.json'). The vendored
package's exports field only exposed ".", which blocked the
"./package.json" subpath and broke build:native:sea with
ERR_PACKAGE_PATH_NOT_EXPORTED.

* chore(kimi-code): sync pi-tui native prebuilds at build time

Copy packages/pi-tui/native prebuilds into apps/kimi-code/native
during build instead of tracking a manual copy in git. Only the
.node prebuilds are copied (not the C sources); the directory is
now a build artifact covered by .gitignore.

* fix(pi-tui): avoid destructive full redraw during streaming

When the first changed line is above the viewport, the differential
renderer fell back to fullRender(true), which clears scrollback and
yanks the user's viewport. On Windows Terminal this jumps to the
absolute top (microsoft/Terminal#20370).

Clamp the diff to the visible viewport when content length is
unchanged (spinner tick / markdown reflow above the viewport), so
streaming no longer triggers a full redraw in those cases. Length
changes still fall back to fullRender to reset the viewport.

* fix(kimi-code): update pi-tui imports in files merged from main

Two files added on main (effort-selector, plugin-command) still
imported the old @earendil-works/pi-tui package name; point them at
the vendored @moonshot-ai/pi-tui.

* chore(nix): update pnpmDeps hash after lockfile regen

* fix(pi-tui): clamp above-viewport diff even when content shrinks

Previously, when the first changed line was above the viewport and
content length changed (e.g. spinner removed at end of streaming), the
renderer fell back to fullRender(true), which clears scrollback and
yanks the viewport to the absolute top on Windows Terminal.

Always clamp the diff to the visible viewport instead, preserving the
user's scroll position. Stale bytes remain in scrollback but are not
visible.

* fix(kimi-code): keep activity placeholder to avoid streaming shrink

When streaming ends, removing the activity spinner shrank the content
by two rows, which (combined with transient→final code highlighting
above the viewport) triggered a destructive full redraw. Keep a one-row
placeholder in the activity pane when idle so the content does not
fully shrink.

* chore: refine streaming scroll changeset wording

* chore: remove obsolete pi-tui native helpers changeset

* chore: add pi-tui changesets and document the pi-tui changelog rule

Add changesets for the fork integration, package manifest export, and
viewport clamp fix so the vendored pi-tui keeps its own changelog.

Update the gen-changesets skill to treat @moonshot-ai/pi-tui as a
special internal package that lists itself instead of the CLI, with a
separate CLI changeset only when the change is user-visible there.

* chore(nix): update pnpmDeps hash after merging main

* fix(changeset): drop private kimi-code-docs from google-genai changeset
2026-07-01 20:23:35 +08:00