Skip to content

refactor: rewrite from Python to Bun + TypeScript + React Ink#1707

Open
Yuandiaodiaodiao wants to merge 28 commits into
MoonshotAI:mainfrom
Yuandiaodiaodiao:worktree-bun-ts-refactor
Open

refactor: rewrite from Python to Bun + TypeScript + React Ink#1707
Yuandiaodiaodiao wants to merge 28 commits into
MoonshotAI:mainfrom
Yuandiaodiaodiao:worktree-bun-ts-refactor

Conversation

@Yuandiaodiaodiao

@Yuandiaodiaodiao Yuandiaodiaodiao commented Apr 1, 2026

Copy link
Copy Markdown

kimicli用python是彻底的失败 立刻重构为ts

Summary

Complete rewrite of kimi-cli from Python to Bun + TypeScript + React Ink, delivering a fully functional terminal-native AI agent CLI.

  • 166 TypeScript/TSX source files, ~32k lines of code
  • 37 test files covering core, tools, UI, and utilities
  • 211 files changed, 38,735 insertions

SWE-bench Verified Results

kimi-k2.5 on SWE-bench Verified (500 tasks), comparing Python (kimi-cli) vs TypeScript (kimi-cli-ts):

Metric kimi-cli (Python) kimi-cli-ts (TypeScript)
Resolved 325 / 496 (65.5%) 317 / 500 (63.4%)
Unresolved 171 / 496 183 / 500
Errors 5 0
Runtime ~5h 37m (see TS run)

Python version ran 496 effective trials (5 infra errors out of 500).

Resolved tasks — Python kimi-cli (325)
  • astropy__astropy-12907
  • astropy__astropy-13453
  • astropy__astropy-13579
  • astropy__astropy-14096
  • astropy__astropy-14182
  • astropy__astropy-14309
  • astropy__astropy-14508
  • astropy__astropy-14539
  • astropy__astropy-14995
  • astropy__astropy-7166
  • astropy__astropy-7336
  • astropy__astropy-7671
  • django__django-10914
  • django__django-10973
  • django__django-11066
  • django__django-11095
  • django__django-11099
  • django__django-11119
  • django__django-11133
  • django__django-11138
  • django__django-11149
  • django__django-11179
  • django__django-11206
  • django__django-11211
  • django__django-11265
  • django__django-11276
  • django__django-11292
  • django__django-11299
  • django__django-11451
  • django__django-11490
  • django__django-11532
  • django__django-11551
  • django__django-11555
  • django__django-11603
  • django__django-11728
  • django__django-11740
  • django__django-11749
  • django__django-11790
  • django__django-11815
  • django__django-11848
  • django__django-11880
  • django__django-11999
  • django__django-12039
  • django__django-12050
  • django__django-12125
  • django__django-12155
  • django__django-12193
  • django__django-12209
  • django__django-12273
  • django__django-12276
  • django__django-12304
  • django__django-12419
  • django__django-12663
  • django__django-12708
  • django__django-12713
  • django__django-12774
  • django__django-12858
  • django__django-12965
  • django__django-13012
  • django__django-13023
  • django__django-13028
  • django__django-13033
  • django__django-13089
  • django__django-13109
  • django__django-13112
  • django__django-13121
  • django__django-13128
  • django__django-13158
  • django__django-13279
  • django__django-13297
  • django__django-13315
  • django__django-13343
  • django__django-13344
  • django__django-13346
  • django__django-13363
  • django__django-13410
  • django__django-13417
  • django__django-13449
  • django__django-13516
  • django__django-13551
  • django__django-13568
  • django__django-13569
  • django__django-13590
  • django__django-13670
  • django__django-13741
  • django__django-13786
  • django__django-13809
  • django__django-13810
  • django__django-13820
  • django__django-13821
  • django__django-13837
  • django__django-13925
  • django__django-13933
  • django__django-13964
  • django__django-14007
  • django__django-14089
  • django__django-14122
  • django__django-14238
  • django__django-14311
  • django__django-14349
  • django__django-14351
  • django__django-14373
  • django__django-14434
  • django__django-14493
  • django__django-14500
  • django__django-14534
  • django__django-14539
  • django__django-14559
  • django__django-14580
  • django__django-14608
  • django__django-14631
  • django__django-14672
  • django__django-14752
  • django__django-14765
  • django__django-14771
  • django__django-14787
  • django__django-14792
  • django__django-14855
  • django__django-14915
  • django__django-14999
  • django__django-15022
  • django__django-15037
  • django__django-15104
  • django__django-15127
  • django__django-15128
  • django__django-15161
  • django__django-15268
  • django__django-15277
  • django__django-15278
  • django__django-15315
  • django__django-15375
  • django__django-15380
  • django__django-15382
  • django__django-15467
  • django__django-15499
  • django__django-15525
  • django__django-15554
  • django__django-15561
  • django__django-15569
  • django__django-15572
  • django__django-15731
  • django__django-15732
  • django__django-15741
  • django__django-15814
  • django__django-15851
  • django__django-15863
  • django__django-15916
  • django__django-15930
  • django__django-15987
  • django__django-16032
  • django__django-16082
  • django__django-16136
  • django__django-16139
  • django__django-16255
  • django__django-16333
  • django__django-16429
  • django__django-16485
  • django__django-16493
  • django__django-16527
  • django__django-16560
  • django__django-16569
  • django__django-16595
  • django__django-16612
  • django__django-16661
  • django__django-16662
  • django__django-16801
  • django__django-16819
  • django__django-16877
  • django__django-16899
  • django__django-16901
  • django__django-16938
  • django__django-16950
  • django__django-17029
  • django__django-17084
  • django__django-17087
  • django__django-7530
  • django__django-9296
  • matplotlib__matplotlib-13989
  • matplotlib__matplotlib-14623
  • matplotlib__matplotlib-20859
  • matplotlib__matplotlib-21568
  • matplotlib__matplotlib-22719
  • matplotlib__matplotlib-23412
  • matplotlib__matplotlib-24570
  • matplotlib__matplotlib-24637
  • matplotlib__matplotlib-25122
  • matplotlib__matplotlib-25311
  • matplotlib__matplotlib-25775
  • matplotlib__matplotlib-25960
  • matplotlib__matplotlib-26291
  • matplotlib__matplotlib-26342
  • mwaskom__seaborn-3069
  • pallets__flask-5014
  • psf__requests-1142
  • psf__requests-1724
  • psf__requests-1766
  • pydata__xarray-2905
  • pydata__xarray-3151
  • pydata__xarray-3305
  • pydata__xarray-3677
  • pydata__xarray-3993
  • pydata__xarray-4075
  • pydata__xarray-4094
  • pydata__xarray-4356
  • pydata__xarray-4695
  • pydata__xarray-4966
  • pydata__xarray-6461
  • pydata__xarray-6599
  • pydata__xarray-7233
  • pydata__xarray-7393
  • pylint-dev__pylint-8898
  • pytest-dev__pytest-10051
  • pytest-dev__pytest-10081
  • pytest-dev__pytest-5262
  • pytest-dev__pytest-5631
  • pytest-dev__pytest-5809
  • pytest-dev__pytest-6197
  • pytest-dev__pytest-6202
  • pytest-dev__pytest-7205
  • pytest-dev__pytest-7236
  • pytest-dev__pytest-7324
  • pytest-dev__pytest-7432
  • pytest-dev__pytest-7521
  • pytest-dev__pytest-7571
  • pytest-dev__pytest-7982
  • pytest-dev__pytest-8399
  • scikit-learn__scikit-learn-10297
  • scikit-learn__scikit-learn-11578
  • scikit-learn__scikit-learn-12585
  • scikit-learn__scikit-learn-12682
  • scikit-learn__scikit-learn-12973
  • scikit-learn__scikit-learn-13135
  • scikit-learn__scikit-learn-13142
  • scikit-learn__scikit-learn-13328
  • scikit-learn__scikit-learn-13439
  • scikit-learn__scikit-learn-13496
  • scikit-learn__scikit-learn-13779
  • scikit-learn__scikit-learn-14053
  • scikit-learn__scikit-learn-14087
  • scikit-learn__scikit-learn-14141
  • scikit-learn__scikit-learn-14629
  • scikit-learn__scikit-learn-14710
  • scikit-learn__scikit-learn-14894
  • scikit-learn__scikit-learn-14983
  • scikit-learn__scikit-learn-15100
  • scikit-learn__scikit-learn-25102
  • scikit-learn__scikit-learn-25931
  • scikit-learn__scikit-learn-25973
  • scikit-learn__scikit-learn-26323
  • scikit-learn__scikit-learn-9288
  • sphinx-doc__sphinx-10449
  • sphinx-doc__sphinx-10466
  • sphinx-doc__sphinx-10673
  • sphinx-doc__sphinx-7757
  • sphinx-doc__sphinx-7889
  • sphinx-doc__sphinx-7910
  • sphinx-doc__sphinx-8035
  • sphinx-doc__sphinx-8120
  • sphinx-doc__sphinx-8269
  • sphinx-doc__sphinx-8475
  • sphinx-doc__sphinx-8548
  • sphinx-doc__sphinx-8551
  • sphinx-doc__sphinx-8593
  • sphinx-doc__sphinx-8595
  • sphinx-doc__sphinx-8621
  • sphinx-doc__sphinx-8721
  • sphinx-doc__sphinx-9230
  • sphinx-doc__sphinx-9258
  • sphinx-doc__sphinx-9281
  • sphinx-doc__sphinx-9591
  • sphinx-doc__sphinx-9658
  • sphinx-doc__sphinx-9673
  • sphinx-doc__sphinx-9698
  • sphinx-doc__sphinx-9711
  • sympy__sympy-11618
  • sympy__sympy-12096
  • sympy__sympy-12419
  • sympy__sympy-12481
  • sympy__sympy-13031
  • sympy__sympy-13480
  • sympy__sympy-13551
  • sympy__sympy-13615
  • sympy__sympy-13647
  • sympy__sympy-13757
  • sympy__sympy-13877
  • sympy__sympy-13878
  • sympy__sympy-14531
  • sympy__sympy-14711
  • sympy__sympy-14976
  • sympy__sympy-15345
  • sympy__sympy-15349
  • sympy__sympy-15599
  • sympy__sympy-15809
  • sympy__sympy-15976
  • sympy__sympy-16766
  • sympy__sympy-16792
  • sympy__sympy-16886
  • sympy__sympy-17139
  • sympy__sympy-17655
  • sympy__sympy-18189
  • sympy__sympy-18211
  • sympy__sympy-19346
  • sympy__sympy-19495
  • sympy__sympy-19637
  • sympy__sympy-19783
  • sympy__sympy-19954
  • sympy__sympy-20154
  • sympy__sympy-20590
  • sympy__sympy-20801
  • sympy__sympy-21379
  • sympy__sympy-21612
  • sympy__sympy-21847
  • sympy__sympy-22714
  • sympy__sympy-22914
  • sympy__sympy-23262
  • sympy__sympy-23413
  • sympy__sympy-23534
  • sympy__sympy-23824
  • sympy__sympy-23950
  • sympy__sympy-24066
  • sympy__sympy-24213
  • sympy__sympy-24443
  • sympy__sympy-24539
  • sympy__sympy-24562
  • sympy__sympy-24661
Resolved tasks — TypeScript kimi-cli-ts (317)
  • astropy__astropy-12907
  • astropy__astropy-13453
  • astropy__astropy-13579
  • astropy__astropy-14096
  • astropy__astropy-14309
  • astropy__astropy-14365
  • astropy__astropy-14508
  • astropy__astropy-14539
  • astropy__astropy-14995
  • astropy__astropy-7336
  • astropy__astropy-7671
  • django__django-10880
  • django__django-10914
  • django__django-11066
  • django__django-11095
  • django__django-11099
  • django__django-11119
  • django__django-11133
  • django__django-11138
  • django__django-11149
  • django__django-11163
  • django__django-11206
  • django__django-11211
  • django__django-11265
  • django__django-11276
  • django__django-11292
  • django__django-11299
  • django__django-11333
  • django__django-11451
  • django__django-11490
  • django__django-11532
  • django__django-11551
  • django__django-11555
  • django__django-11603
  • django__django-11728
  • django__django-11740
  • django__django-11749
  • django__django-11815
  • django__django-11880
  • django__django-11951
  • django__django-11999
  • django__django-12039
  • django__django-12050
  • django__django-12125
  • django__django-12143
  • django__django-12155
  • django__django-12209
  • django__django-12262
  • django__django-12273
  • django__django-12276
  • django__django-12304
  • django__django-12419
  • django__django-12663
  • django__django-12713
  • django__django-12741
  • django__django-12754
  • django__django-12774
  • django__django-12858
  • django__django-13012
  • django__django-13023
  • django__django-13028
  • django__django-13033
  • django__django-13089
  • django__django-13109
  • django__django-13112
  • django__django-13121
  • django__django-13128
  • django__django-13158
  • django__django-13279
  • django__django-13297
  • django__django-13343
  • django__django-13344
  • django__django-13346
  • django__django-13363
  • django__django-13401
  • django__django-13410
  • django__django-13417
  • django__django-13516
  • django__django-13551
  • django__django-13568
  • django__django-13569
  • django__django-13590
  • django__django-13658
  • django__django-13670
  • django__django-13741
  • django__django-13786
  • django__django-13807
  • django__django-13809
  • django__django-13810
  • django__django-13820
  • django__django-13821
  • django__django-13837
  • django__django-13933
  • django__django-13964
  • django__django-14007
  • django__django-14017
  • django__django-14053
  • django__django-14089
  • django__django-14122
  • django__django-14238
  • django__django-14311
  • django__django-14349
  • django__django-14373
  • django__django-14434
  • django__django-14493
  • django__django-14500
  • django__django-14534
  • django__django-14539
  • django__django-14559
  • django__django-14580
  • django__django-14608
  • django__django-14631
  • django__django-14672
  • django__django-14725
  • django__django-14752
  • django__django-14765
  • django__django-14771
  • django__django-14787
  • django__django-14792
  • django__django-14855
  • django__django-14915
  • django__django-14999
  • django__django-15037
  • django__django-15104
  • django__django-15127
  • django__django-15161
  • django__django-15268
  • django__django-15277
  • django__django-15278
  • django__django-15315
  • django__django-15368
  • django__django-15380
  • django__django-15382
  • django__django-15467
  • django__django-15499
  • django__django-15503
  • django__django-15525
  • django__django-15561
  • django__django-15569
  • django__django-15572
  • django__django-15731
  • django__django-15732
  • django__django-15741
  • django__django-15814
  • django__django-15851
  • django__django-15863
  • django__django-15930
  • django__django-15957
  • django__django-15987
  • django__django-16032
  • django__django-16082
  • django__django-16116
  • django__django-16136
  • django__django-16139
  • django__django-16145
  • django__django-16255
  • django__django-16315
  • django__django-16333
  • django__django-16429
  • django__django-16485
  • django__django-16493
  • django__django-16527
  • django__django-16569
  • django__django-16612
  • django__django-16642
  • django__django-16661
  • django__django-16662
  • django__django-16801
  • django__django-16819
  • django__django-16899
  • django__django-16901
  • django__django-16938
  • django__django-16950
  • django__django-17029
  • django__django-17087
  • django__django-7530
  • django__django-9296
  • matplotlib__matplotlib-13989
  • matplotlib__matplotlib-14623
  • matplotlib__matplotlib-21568
  • matplotlib__matplotlib-22719
  • matplotlib__matplotlib-24570
  • matplotlib__matplotlib-24637
  • matplotlib__matplotlib-25122
  • matplotlib__matplotlib-25332
  • matplotlib__matplotlib-26291
  • matplotlib__matplotlib-26342
  • matplotlib__matplotlib-26466
  • mwaskom__seaborn-3069
  • pallets__flask-5014
  • psf__requests-1142
  • psf__requests-1921
  • pydata__xarray-3095
  • pydata__xarray-3151
  • pydata__xarray-3677
  • pydata__xarray-4075
  • pydata__xarray-4094
  • pydata__xarray-4356
  • pydata__xarray-4629
  • pydata__xarray-4695
  • pydata__xarray-7233
  • pydata__xarray-7393
  • pylint-dev__pylint-8898
  • pytest-dev__pytest-10051
  • pytest-dev__pytest-10081
  • pytest-dev__pytest-5262
  • pytest-dev__pytest-5631
  • pytest-dev__pytest-5809
  • pytest-dev__pytest-6202
  • pytest-dev__pytest-7205
  • pytest-dev__pytest-7432
  • pytest-dev__pytest-7490
  • pytest-dev__pytest-7521
  • pytest-dev__pytest-7571
  • pytest-dev__pytest-7982
  • pytest-dev__pytest-8399
  • scikit-learn__scikit-learn-10908
  • scikit-learn__scikit-learn-11578
  • scikit-learn__scikit-learn-12585
  • scikit-learn__scikit-learn-12973
  • scikit-learn__scikit-learn-13142
  • scikit-learn__scikit-learn-13328
  • scikit-learn__scikit-learn-13779
  • scikit-learn__scikit-learn-14053
  • scikit-learn__scikit-learn-14141
  • scikit-learn__scikit-learn-14496
  • scikit-learn__scikit-learn-14629
  • scikit-learn__scikit-learn-14710
  • scikit-learn__scikit-learn-14894
  • scikit-learn__scikit-learn-14983
  • scikit-learn__scikit-learn-25102
  • scikit-learn__scikit-learn-25232
  • scikit-learn__scikit-learn-25747
  • scikit-learn__scikit-learn-25931
  • scikit-learn__scikit-learn-25973
  • scikit-learn__scikit-learn-9288
  • sphinx-doc__sphinx-10449
  • sphinx-doc__sphinx-10466
  • sphinx-doc__sphinx-10673
  • sphinx-doc__sphinx-7440
  • sphinx-doc__sphinx-7454
  • sphinx-doc__sphinx-7757
  • sphinx-doc__sphinx-7889
  • sphinx-doc__sphinx-7910
  • sphinx-doc__sphinx-8035
  • sphinx-doc__sphinx-8120
  • sphinx-doc__sphinx-8269
  • sphinx-doc__sphinx-8459
  • sphinx-doc__sphinx-8475
  • sphinx-doc__sphinx-8551
  • sphinx-doc__sphinx-8593
  • sphinx-doc__sphinx-8595
  • sphinx-doc__sphinx-8621
  • sphinx-doc__sphinx-8638
  • sphinx-doc__sphinx-8721
  • sphinx-doc__sphinx-9230
  • sphinx-doc__sphinx-9258
  • sphinx-doc__sphinx-9281
  • sphinx-doc__sphinx-9320
  • sphinx-doc__sphinx-9591
  • sphinx-doc__sphinx-9658
  • sphinx-doc__sphinx-9673
  • sphinx-doc__sphinx-9698
  • sphinx-doc__sphinx-9711
  • sympy__sympy-12096
  • sympy__sympy-12419
  • sympy__sympy-12481
  • sympy__sympy-13031
  • sympy__sympy-13091
  • sympy__sympy-13372
  • sympy__sympy-13480
  • sympy__sympy-13551
  • sympy__sympy-13615
  • sympy__sympy-13647
  • sympy__sympy-13757
  • sympy__sympy-13877
  • sympy__sympy-13878
  • sympy__sympy-13974
  • sympy__sympy-14976
  • sympy__sympy-15017
  • sympy__sympy-15345
  • sympy__sympy-15349
  • sympy__sympy-15599
  • sympy__sympy-15809
  • sympy__sympy-15875
  • sympy__sympy-16450
  • sympy__sympy-16766
  • sympy__sympy-16792
  • sympy__sympy-16886
  • sympy__sympy-17318
  • sympy__sympy-17655
  • sympy__sympy-18189
  • sympy__sympy-18211
  • sympy__sympy-19040
  • sympy__sympy-19346
  • sympy__sympy-19495
  • sympy__sympy-19637
  • sympy__sympy-19783
  • sympy__sympy-19954
  • sympy__sympy-20154
  • sympy__sympy-20590
  • sympy__sympy-20801
  • sympy__sympy-21379
  • sympy__sympy-21612
  • sympy__sympy-21847
  • sympy__sympy-21930
  • sympy__sympy-22456
  • sympy__sympy-22714
  • sympy__sympy-22914
  • sympy__sympy-23262
  • sympy__sympy-23534
  • sympy__sympy-23824
  • sympy__sympy-23950
  • sympy__sympy-24066
  • sympy__sympy-24213
  • sympy__sympy-24443
  • sympy__sympy-24661

Tech Stack Migration

Before (Python) After (TypeScript)
Typer CLI Commander.js
Rich + prompt-toolkit React Ink (ink v6)
Pydantic validation Zod v4
asyncio async/await + Promises
kosong LLM @anthropic-ai/sdk, openai, @google/genai
KaosPath / kaos.exec Bun.file / Bun.spawn
loguru log4js (disk-only JSONL logging)

What's Implemented

Core Agent Loop (soul/)

  • KimiSoul: main agent loop with LLM streaming, tool execution, and context management
  • Context: conversation history with checkpoints, compaction support
  • Toolset: tool loading, dependency injection, concurrent tool execution
  • Compaction: context window management and summarization
  • Approval: tool approval facade with configurable policies
  • Slash commands: soul-level command registry and dispatch
  • Dynamic injection: plan mode and yolo mode runtime injections

Tools (12 categories)

  • File: read, write, replace (str_replace_editor), glob, grep, read_media, plan_mode
  • Shell: command execution via Bun.spawn with timeout and background support
  • Web: fetch (URL content extraction) and search
  • Agent: subagent creation and management
  • Background: background task manager
  • Plan: plan mode with hero quotes
  • Todo: task list management
  • Think: scratchpad reasoning tool
  • Ask User: interactive question prompts
  • DMail: checkpointed message delivery

Subagent System (subagents/)

  • Subagent type registry with builtin agent types
  • Builder pattern for subagent construction
  • Persistent store for subagent metadata and context
  • Model alias resolution for subagent instances
  • Git context injection for subagents

UI - React Ink TUI (ui/)

  • Shell: full interactive TUI with prompt, status bar, welcome box
  • Prompt: unified input handling with multiline support, history, Ctrl+V image paste
  • SlashMenu: autocomplete popup with windowed scrolling (max 6 visible items)
  • MentionMenu: @ file mention with fuzzy search
  • StatusBar: git branch info, tips rotation, inline toasts
  • ApprovalPanel / ApprovalPrompt: tool approval UI
  • CommandPanel: interactive choice panels for slash commands
  • NotificationStack: toast notification system
  • QuestionPanel: structured question display
  • TaskBrowser: task list viewer
  • SetupWizard: first-run configuration
  • UsagePanel / DebugPanel / ReplayPanel: utility panels
  • Print mode: non-interactive output mode
  • Custom renderer: ANSI-aware terminal renderer with diff-based updates

Infrastructure

  • LLM: multi-provider abstraction (Anthropic, OpenAI, Google GenAI)
  • Config: Zod schema + TOML loading from ~/.kimi/config.toml
  • Session: session persistence with fork support
  • Auth: OAuth flow with platform-specific endpoints
  • Hooks: hook engine for tool-call events
  • Wire: event protocol for UI <-> Agent communication with JSON-RPC server
  • Notifications: notification manager with LLM-powered summarization
  • Skills: skill loader with flow skills (d2, mermaid)
  • Plugins: plugin manager for external tool loading
  • Agent specs: YAML spec loading with inheritance
  • Logging: disk-only JSONL logs to ~/.kimi/sessions/ (no console pollution)

Architecture

src/kimi_cli_ts/
├── soul/              # Core agent loop, context, compaction, approval, toolset
├── tools/             # 12 tool categories (file, shell, web, agent, plan, etc.)
├── subagents/         # Subagent registry, builder, runner, store
├── wire/              # Wire protocol, JSON-RPC server, root hub
├── ui/
│   ├── shell/         # React Ink TUI (Shell, Prompt, panels)
│   ├── components/    # Shared components (StatusBar, SlashMenu, etc.)
│   ├── hooks/         # React hooks (useInput, useWire, useApproval, etc.)
│   ├── renderer/      # Custom ANSI terminal renderer
│   └── print/         # Non-interactive print mode
├── background/        # Background task manager
├── notifications/     # Notification system with LLM summarization
├── skill/             # Skill loader and flow skills
├── plugin/            # Plugin manager
├── auth/              # OAuth authentication
├── hooks/             # Hook engine for tool events
├── cli/               # CLI entry point and subcommands
├── config.ts          # Zod config schema + TOML loading
├── session.ts         # Session persistence
├── llm.ts             # Multi-provider LLM abstraction
├── agentspec.ts       # Agent spec loading
├── app.ts             # KimiCLI main app
└── utils/             # Logging, path, async, clipboard, etc.

Quick Start

bun run start

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@Yuandiaodiaodiao Yuandiaodiaodiao force-pushed the worktree-bun-ts-refactor branch from 751b51b to 963d58d Compare April 1, 2026 14:22

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 751b51bc86

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli/llm.ts Outdated
Comment on lines +333 to +337
const stubProvider: LLMProvider = {
modelName: model.model,
async *chat(messages: Message[], chatOpts?: ChatOptions) {
throw new Error(
`LLM provider "${provider.type}" is not yet implemented in TypeScript. ` +

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Replace stubbed chat provider with real provider dispatch

createLLM always returns stubProvider, and chat() immediately throws for every configured provider type, so every turn fails as soon as the model is invoked. This makes the CLI unable to generate any assistant response even with valid provider/model config, which is a full functional regression.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli/cli/index.ts Outdated
Comment on lines +68 to +72
React.createElement(Shell, {
modelName: app.soul.modelName,
onSubmit: (input: string) => {
app.soul.run(input);
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Wire soul events into shell so the interactive UI can update

The shell is rendered with only onSubmit, which calls app.soul.run(...) directly, but no onWireReady/callback bridge is provided to feed useWire with turn/text/tool/approval events. As a result the message list does not reflect agent output, and approval-required tool calls can stall because the UI never receives/returns approval events.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/soul/kimisoul.ts Outdated
Comment on lines +326 to +330
contentParts.push({
type: "tool_use",
id: tc.id,
name: tc.name,
input: JSON.parse(tc.arguments || "{}"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle malformed tool-call JSON without aborting the turn

Tool call arguments are parsed with JSON.parse while assembling assistant context content, and parsing errors are not caught. If a model emits invalid JSON arguments (a common transient/tool-calling failure mode), the whole turn throws before tool execution reaches the existing argument-parse error handling in toolset.handle, causing avoidable turn termination.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e67aa7e2f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli/cli/index.ts Outdated
Comment on lines +62 to +65
if (options.print && prompt) {
// Non-interactive print mode
await app.runPrint(prompt);
} else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor --print mode without requiring positional prompt

The --print flag is documented as non-interactive, but this branch only enters print mode when prompt is also present; kimi --print (or any future stdin-driven print flow) falls through to the interactive Ink shell instead. In automation/non-TTY contexts this causes unexpected hangs or UI startup failures instead of producing print output.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/tools/shell/shell.ts Outdated
Comment on lines +99 to +102
const shellPath = process.env.SHELL || "/bin/bash";

const proc = Bun.spawn([shellPath, "-c", params.command], {
stdout: "pipe",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use a Windows-compatible shell fallback

When SHELL is unset, the tool hardcodes /bin/bash as the executable. On Windows, SHELL is commonly absent and /bin/bash usually does not exist, so Shell tool calls fail before running the requested command. Because this commit adds Windows binaries in CI, this breaks core tool functionality on that target.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/session.ts Outdated
Comment on lines +236 to +237
const dirFile = Bun.file(sessionsDir);
if (!(await dirFile.exists())) return [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check session directory existence with dir-aware APIs

This uses Bun.file(sessionsDir).exists() to test whether the sessions directory exists, but Bun.file(...).exists() returns false for directories, so Session.list() returns [] even when sessions are present. That makes session discovery/listing logic silently fail for normal repositories.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3732f9cdd7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/llm.ts Outdated
Comment on lines +333 to +337
switch (provider.type) {
case "kimi":
case "openai_legacy":
case "openai_responses":
llmProvider = new OpenAICompatibleProvider({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Normalize OpenAI provider type in LLM dispatch

The provider switch only handles "openai_legacy"/"openai_responses", but config parsing allows "openai" and app.ts passes that value through. In that common configuration, createLLM falls into the default branch and returns a provider that throws on the first chat() call, so OpenAI-backed sessions fail at runtime despite valid config.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli/soul/agent.ts Outdated
Comment on lines +252 to +256
async function registerBuiltinTools(toolset: KimiToolset): Promise<void> {
// Import and register all built-in tools
const toolModules = [
() => import("../tools/file/read.ts"),
() => import("../tools/file/write.ts"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Load tools from agent spec instead of hardcoded list

Agent construction always registers a fixed module list here, so the tool set defined in agents/*/agent.yaml (including allow/exclude controls and declared tools like background/task/plan/Agent) is never applied. This disables configured agent behaviors and subagent/background workflows even though the specs exist in the repository.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/tools/file/glob.ts Outdated
Comment on lines +58 to +60
if (!dirPath.startsWith("/")) {
return ToolError(
`\`${params.directory}\` is not an absolute path. You must provide an absolute path to search.`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Accept Windows absolute paths in Glob validation

The absolute-path check only accepts paths starting with /, so Windows drive-letter paths (for example C:\repo) are rejected as non-absolute. On Windows, ctx.workingDir itself uses that format, which makes normal Glob calls fail with the absolute-path error and effectively breaks the tool on that platform.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/config.ts Outdated
Comment on lines +239 to +241
const filePath = configFile ?? getConfigFile();
const dir = filePath.substring(0, filePath.lastIndexOf("/"));
await Bun.$`mkdir -p ${dir}`.quiet();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use path-aware parent dir handling in saveConfig

Parent directory extraction uses lastIndexOf("/"), which returns -1 for Windows paths with backslashes. When config is first created, this can produce an empty directory string and cause the mkdir -p step to fail before config.toml is written, breaking first-run setup on Windows.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 38cf997b7c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/soul/approval.ts Outdated
});

try {
const [response, feedback] = await this._runtime.waitForResponse(requestId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve approval requests before waiting for response

This call can block forever because there is no wiring in this commit that resolves user approvals back into ApprovalRuntime for normal tool flows. I checked the tree for ApprovalRuntime.resolve usage and only found the internal auto-approve branch in approval.ts, so with default yolo=false any tool that requests approval can stall the turn indefinitely while awaiting waitForResponse.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli/session.ts Outdated
// ── WorkDir Metadata ────────────────────────────────────

function getSessionsBaseDir(workDir: string): string {
return join(getShareDir(), "sessions", workDir.replace(/\//g, "_").replace(/^_/, ""));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Normalize Windows paths when building sessions base dir

getSessionsBaseDir only rewrites /, so Windows workdirs like C:\repo\proj retain backslashes and the drive-colon in the path segment. That produces invalid/unstable session directory paths on Windows, which breaks Session.create/find/list for those workdirs.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/tools/shell/shell.ts Outdated
Comment on lines +122 to +125
// Background mode - stub for now
return builder.error(
"Background tasks are not yet implemented in this version.",
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Implement or disable run_in_background in Shell tool

The tool schema and description explicitly promote run_in_background=true for long-running commands, but this branch returns an immediate error for every such call. That creates deterministic failures for long tasks the model is guided to run in background, instead of executing or scheduling them.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6e7cae1919

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli/cli/index.ts Outdated
Comment on lines +167 to +171
yolo: options.yolo,
sessionId: options.session,
maxStepsPerTurn: options.maxStepsPerTurn,
callbacks,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Propagate --continue into interactive app creation

In interactive mode, KimiCLI.create(...) is called without continueSession, so kimi -C is silently ignored unless --print is also used. This breaks session resume behavior for the default shell workflow and causes users to start a fresh session unexpectedly even when they requested continuation.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli/cli/index.ts Outdated
Comment on lines +179 to +181
onSubmit: (input: string) => {
app.soul.run(input);
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Route streaming input to steer instead of dropping it

The shell prompt explicitly allows input while streaming, but onSubmit always calls app.soul.run(input). KimiSoul.run returns early when a turn is already running, so any mid-turn user guidance is discarded instead of being queued as steer input. In practice this makes in-flight correction/steering impossible during long tool runs.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/soul/agent.ts Outdated
Comment on lines +102 to +106
const approvalState = new ApprovalState({
yolo:
opts.config.default_yolo || opts.session.state.approval.yolo,
autoApproveActions: new Set(
opts.session.state.approval.auto_approve_actions,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Persist approval state changes back into session state

Approval settings are loaded from session.state.approval into a separate ApprovalState, but no change hook writes updates back to session.state. As a result, /yolo toggles and approve_for_session decisions are lost on shutdown/resume because Session.saveState() persists stale approval data.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 302656023c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/cli/index.ts Outdated
Comment on lines +160 to +164
const callbacks: SoulCallbacks = {
onTextDelta: (text) => process.stdout.write(text),
onThinkDelta: (text) => process.stderr.write(chalk.dim(text)),
onError: (err) =>
process.stderr.write(chalk.red(`[ERROR] ${err.message}\n`)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor print mode formatting flags

In the --print branch, output is hardwired to raw text streaming callbacks, so --output-format stream-json and --final-message-only are never applied. This causes automation that relies on JSON lines or final-only output to receive the wrong format even when those flags are explicitly set.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/cli/index.ts Outdated
Comment on lines +154 to +156
// Determine config source: --config-file takes precedence over legacy --config as path
const configFile = options.configFile ?? undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse inline --config payload instead of ignoring it

The CLI accepts --config <string>, but this code only forwards options.configFile and never consumes options.config (nor calls loadConfigFromString). As a result, users providing inline TOML/JSON config get silently ignored settings.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/app.ts Outdated
Comment on lines +62 to +64
if (opts.maxStepsPerTurn) {
config.loop_control.max_steps_per_turn = opts.maxStepsPerTurn;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply max-retries override in app creation

CLI call sites pass maxRetriesPerStep, but KimiCLI.create only applies max_steps_per_turn and never updates loop_control.max_retries_per_step. This makes --max-retries-per-step a no-op, so retry behavior stays at config defaults regardless of user input.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/soul/kimisoul.ts Outdated
Comment on lines +285 to +287
const file = Bun.file(path);
// Synchronous existence check is not available — use a simple approach
return file.size > 0 ? null : null; // Will be refined when plan tools exist

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return actual plan content in /plan view

readCurrentPlan() unconditionally returns null because the ternary expression returns null in both branches. This makes /plan view report no plan even when the plan file exists and has content.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94b7b71d07

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/tools/file/write.ts Outdated
Comment on lines +99 to +102
const planTarget = inspectPlanEditTarget(resolvedPath, {
planModeChecker: this._planModeChecker ?? ctx.getPlanMode,
planFilePathGetter: this._planFilePathGetter,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pass current plan path into WriteFile plan-mode guard

When plan mode is active, inspectPlanEditTarget requires a plan file path, but this call only passes this._planFilePathGetter and does not fall back to runtime context. Because tool registration never calls bindPlanMode, that getter stays unset, so WriteFile returns the “plan file is unavailable” error for every plan-mode write and blocks the core plan workflow from progressing.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/app.ts Outdated
});

// 6. Load agent
const agent = await loadAgent({ runtime });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor CLI agent selection when constructing the runtime agent

Agent loading is hardcoded to loadAgent({ runtime }), so runtime always uses the default agent spec. Since --agent and --agent-file are parsed in cli/index.ts but KimiCLI.create has no corresponding inputs to forward, those user-facing flags are effectively no-ops across print/wire/interactive modes.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/soul/agent.ts Outdated
Comment on lines +343 to +345
const gitFile = Bun.file(`${current}/.git`);
if (await gitFile.exists()) return current;
const parent = dirname(current);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Detect git roots with directory-aware filesystem checks

This root-detection logic relies on Bun.file(...).exists() for .git, but Bun reports false for directories, so standard repos with a .git/ directory are not recognized as project roots. The walk then falls back to workDir as root, which skips parent-scope AGENTS.md files and breaks the intended root-to-leaf instruction merge.

Useful? React with 👍 / 👎.

@luzhongqiu

luzhongqiu commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

???真的还有语言之真啊


? ? ? There really is truth in language

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ac1d84c76e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/soul/kimisoul.ts Outdated
Comment on lines +945 to +950
wireToolContext(): void {
const ctx = this.agent.toolset.context;
ctx.setPlanMode = (on: boolean) => this.setPlanMode(on);
ctx.getPlanMode = () => this._planMode;
ctx.getPlanFilePath = () => this.getPlanFilePath() ?? undefined;
ctx.togglePlanMode = () => this.togglePlanMode();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Expose askUser bridge when wiring tool context

wireToolContext() only assigns plan-mode helpers and never assigns ctx.askUser. I checked this commit for other ctx.askUser = call sites and found none, so AskUserQuestion and plan-review paths always fall back to auto/default answers even in interactive shell mode. This bypasses intended user confirmations and can make the agent proceed with an unintended plan or option.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/session.ts Outdated
Comment on lines +169 to +171
const file = Bun.file(sessionDir);
if (await file.exists()) {
await Bun.$`rm -rf ${sessionDir}`.quiet();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Delete sessions with directory-aware existence checks

Session.delete() checks a session directory using Bun.file(sessionDir).exists(), which is a file-oriented API and can return false for directories in this Bun migration. When that happens, empty sessions are never removed during shutdown, so stale session folders accumulate and session housekeeping no longer matches expected behavior.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/soul/toolset.ts Outdated
Comment on lines +124 to +126
inputData: {
session_id: _currentSessionId,
tool_name: name,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Populate hook session_id before running tool hooks

Tool hook payloads read session_id from _currentSessionId, but this module initializes it to an empty string and this commit has no setSessionId(...) call site. As a result, Pre/Post tool hooks always receive an empty session id, which breaks per-session policy checks and audit correlation for hook consumers.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae8f85b6fc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/soul/agent.ts Outdated
Comment on lines +275 to +277
const paths = [
`src/kimi_cli/agents/${agentName}/system.md`,
`agents/${agentName}/system.md`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve system prompt path from package directory

loadSystemPrompt looks for src/kimi_cli/agents/... and agents/... using process-relative paths, so when the CLI is launched from a normal project directory (not the repo root) those files are not found and the code silently falls back to the minimal hardcoded prompt. That drops the intended bundled agent instructions for most real runs (including packaged/binary installs), which can materially change tool-use behavior and response quality.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/cli/index.ts Outdated
.option("--agent-file <path>", "Custom agent specification file")
.option("--mcp-config-file <path...>", "MCP config file(s) to load")
.option("--mcp-config <json...>", "MCP config JSON to load")
.option("--command <cmd>", "Run a single shell command and exit")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Implement --command behavior instead of advertising a no-op

The CLI exposes --command <cmd> as “Run a single shell command and exit”, but this option is never consumed in the action handler (only declared in options typing), so invocations like kimi --command "..." still follow normal print/wire/interactive flows. That is a functional mismatch that can break automation expecting one-shot execution.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/tools/file/grep.ts Outdated
Comment on lines +178 to +180
if (!searchPath.startsWith("/")) {
searchPath = `${ctx.workingDir}/${searchPath}`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat Windows absolute paths as absolute in Grep

This absolute-path check only accepts /..., so Windows paths like C:\repo are treated as relative and rewritten to ${ctx.workingDir}/C:\repo, which causes ripgrep to search an invalid location. In Windows environments (which this migration targets), that breaks Grep for common absolute-path inputs.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b25be06cb

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/cli/index.ts Outdated
planMode: options.plan,
sessionId: resolvedSessionId,
continueSession: options.continue,
maxStepsPerTurn: options.maxStepsPerTurn ?? options.maxRetriesPerStep,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop mapping retry flag into max-steps override

This wiring makes --max-retries-per-step change max_steps_per_turn instead of retry behavior: when users pass retries (for example --max-retries-per-step 1) and do not set --max-steps-per-turn, turns are capped to one step and frequently terminate before tool-result follow-up can happen. Because KimiCLI.create has no separate retry override input, this also means the retry flag cannot work as intended at all.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/cli/index.ts Outdated
Comment on lines +414 to +418
const app = await KimiCLI.create({
workDir: options.workDir,
additionalDirs: options.addDir,
configFile,
modelName: options.model,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forward --plan when creating interactive sessions

In the interactive/shell branch, the app is created without passing planMode: options.plan, so kimi --plan starts with plan mode disabled in the default UI path and only print/wire branches honor the flag. This creates mode-dependent behavior for the same CLI option and breaks expected startup state for interactive users.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/tools/file/read.ts Outdated
}
return filePath;
}
return `${workingDir}/${filePath}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve Windows absolute paths correctly in ReadFile

The fallback path join treats any non-/ path as relative, so on Windows a drive-letter absolute path like C:\\repo\\a.txt is rewritten to ${workingDir}/C:\\repo\\a.txt and reported missing. This breaks core file-reading for Windows users whenever the model or user supplies absolute paths.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ee821c9595

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/tools/agent/agent.ts Outdated
): Promise<ToolResult> {
const timeout = params.timeout ?? undefined;
try {
const runner = new ForegroundSubagentRunner(runtime);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Initialize subagent runtime services before Agent execution

AgentTool._runForeground always constructs ForegroundSubagentRunner here, but Runtime.create never populates runtime.subagentStore/runtime.laborMarket/runtime.approvalRuntime (see src/kimi_cli/soul/agent.ts), and the runner constructor hard-fails when subagentStore is missing (src/kimi_cli/subagents/runner.ts). I searched the repo for any later assignment to these runtime fields and only found reads, so foreground Agent calls deterministically return an error instead of launching a subagent.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/tools/agent/agent.ts Outdated
Comment on lines +186 to +189
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout * 1000);
try {
return await runner.run(req);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Apply Agent timeout to the foreground subagent run

The timeout branch creates an AbortController and timer, but the signal is never passed into runner.run(req) (or any callee), so aborting the controller has no effect and timeout is silently ignored. Any long-running foreground subagent task can exceed the user-specified timeout indefinitely, despite the tool schema and docs promising enforced limits.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/soul/kimisoul.ts Outdated
// Check abort before each tool, but don't interrupt mid-append
if (this.abortController?.signal.aborted) break;

const result = await this.agent.toolset.handle(tc);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Emit tool_result callbacks after tool execution

After toolset.handle(tc) returns, this path appends the tool result to context but never calls callbacks.onToolResult; meanwhile KimiToolset's own onToolResult hook is not wired when constructed in loadAgent. In interactive shell mode, useWire relies on tool_result events to attach completion/error payloads to tool calls, so users never see per-tool results even though execution finished.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fe44b011b0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/tools/shell/shell.ts Outdated
Comment on lines +147 to +151
const proc = Bun.spawn([shellPath, "-c", wrappedCommand], {
stdout: "pipe",
stderr: "pipe", // stderr still piped for safety (but most goes to stdout via 2>&1)
stdin: "pipe",
cwd: ctx.workingDir,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use OS-appropriate shell invocation for command execution

This spawn call hardcodes -c, which only works for POSIX shells; on Windows the default shell is typically PowerShell/cmd and does not accept this invocation, so Shell tool calls fail before running the command. In Windows environments this makes core command execution unavailable for normal tool usage.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/tools/file/write.ts Outdated
Comment on lines +33 to +34
function resolvePath(filePath: string, workingDir: string): string {
if (filePath.startsWith("/") || filePath.startsWith("~")) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Treat Windows drive-letter paths as absolute in WriteFile

resolvePath only recognizes /... and ~... as absolute, so a Windows absolute path like C:\repo\file.txt is treated as relative and rewritten under workingDir. That produces invalid targets and causes WriteFile to fail for common Windows absolute-path inputs.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/soul/context.ts Outdated
Comment on lines +166 to +170
this._tokenCount = usage.inputTokens + (usage.cacheReadTokens ?? 0);
this._pendingTokenEstimate = 0;
const record: UsageRecord = {
_usage: {
input_tokens: usage.inputTokens,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Persist cache-read tokens in saved context usage records

The in-memory token counter includes cacheReadTokens, but the persisted _usage record only writes input_tokens/output_tokens. After restore, token count is rebuilt from input_tokens only, so sessions that use prompt caching undercount context usage and can delay compaction until requests exceed model context limits.

Useful? React with 👍 / 👎.

devin-ai-integration[bot]

This comment was marked as resolved.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f4ab7da272

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/cli/index.ts Outdated
callbacks,
});

if (prompt) await app.runPrint(prompt);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Read print-mode input from stdin when prompt is absent

This branch only runs app.runPrint(...) when prompt is present, so kimi --print with stdin input exits without processing any user command. That breaks non-interactive workflows that rely on piped input (including --input-format stream-json, which is advertised as print-mode input) because the CLI creates/shuts down a session but never executes a turn.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/tools/file/grep.ts Outdated
Comment on lines +233 to +234
output = "";
stderrStr = "";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve ripgrep output on timeout before returning

On timeout, this code clears output and stderrStr immediately after killing the process. That makes the later timeout handler always take the "no output" error path, so partial matches are never returned even when ripgrep already produced results before timing out. In long searches, users lose usable partial data despite the code path intending to support partial results.

Useful? React with 👍 / 👎.

@FradSer

FradSer commented Apr 3, 2026

Copy link
Copy Markdown

LGTM😂

@Yuandiaodiaodiao Yuandiaodiaodiao force-pushed the worktree-bun-ts-refactor branch from f4ab7da to 75e849d Compare April 3, 2026 14:10

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 22 additional findings in Devin Review.

Open in Devin Review

Comment thread src/kimi_cli_ts/soul/context.ts Outdated
Comment on lines +166 to +174
this._tokenCount = usage.inputTokens + (usage.cacheReadTokens ?? 0);
this._pendingTokenEstimate = 0;
const record: UsageRecord = {
_usage: {
input_tokens: usage.inputTokens,
output_tokens: usage.outputTokens,
},
};
await this._appendToFile(record);

@devin-ai-integration devin-ai-integration Bot Apr 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Context token count double-counts cache tokens, inflating context window estimate

updateTokenCount sets _tokenCount = usage.inputTokens + (usage.cacheReadTokens ?? 0). For OpenAI-compatible APIs (including Kimi), inputTokens (from prompt_tokens) already includes cached tokens — cacheReadTokens is a subset of inputTokens, not additional tokens. Adding them double-counts, inflating the context estimate by up to 2×. This causes premature compaction via shouldCompact() at src/kimi_cli_ts/soul/compaction.ts:220-223. The restore method at src/kimi_cli_ts/soul/context.ts:92 correctly uses just record._usage.input_tokens without adding cache tokens, confirming this is inconsistent.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 75e849d6bd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/session.ts Outdated
Comment on lines +277 to +278
const dirFile = Bun.file(sessionsDir);
if (!(await dirFile.exists())) return [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Check session directories with a directory-aware API

Session.list bails out early whenever Bun.file(sessionsDir).exists() is false, but Bun reports false for directories, so this path returns an empty list even when session folders are present. In practice this breaks session discovery flows that depend on Session.list (e.g. picker mode and continue_ fallback), making existing sessions appear missing.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/session.ts Outdated
Comment on lines +179 to +180
const stat = await Bun.$`stat -f %m ${this.contextFile} 2>/dev/null || stat -c %Y ${this.contextFile} 2>/dev/null`.quiet().text();
this.updatedAt = Number.parseFloat(stat.trim()) || 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse mtime without mixing incompatible stat outputs

This cross-platform command emits non-numeric stdout on GNU/Linux from the stat -f %m branch before falling back to stat -c %Y, so parseFloat(stat.trim()) resolves to NaN and updatedAt collapses to 0. As a result, session recency sorting becomes unreliable and resume/continue paths that expect “most recent” ordering can pick the wrong session.

Useful? React with 👍 / 👎.

@Lakr233 Lakr233 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@lumigj

lumigj commented Apr 3, 2026

Copy link
Copy Markdown

无敌了,不知道会不会火,先合个影


Invincible. I don’t know if it will be popular. Let’s take a photo together

@Yuandiaodiaodiao Yuandiaodiaodiao force-pushed the worktree-bun-ts-refactor branch from 75e849d to 08b03ed Compare April 3, 2026 14:22
Complete rewrite of the CLI from Python to TypeScript/Bun with React Ink UI,
including OpenAI-compatible LLM provider, slash commands, tool system,
subagent support, and all upstream Python fixes synced from main.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Yuandiaodiaodiao Yuandiaodiaodiao force-pushed the worktree-bun-ts-refactor branch from 08b03ed to 6d69435 Compare April 3, 2026 14:25

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 08b03ed067

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/soul/kimisoul.ts Outdated
type: "tool_use",
id: tc.id,
name: tc.name,
input: JSON.parse(tc.arguments || "{}"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard tool-call argument parsing before appending assistant msg

This JSON.parse runs on model-provided tool arguments before tool execution, so a single malformed argument payload causes _step() to throw and abort the turn. In that case the tool never reaches toolset.handle(...), which already has graceful argument-parse error handling, so recoverable tool-call formatting issues become hard failures that stop execution.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/soul/agent.ts Outdated
typeof value.prototype.toDefinition === "function"
) {
try {
const instance = new (value as new () => any)();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Initialize plan tools with the active session id

Tools are instantiated generically with a no-arg constructor here, but EnterPlanMode/ExitPlanMode default their internal session ID to "default" when no ID is provided (src/kimi_cli_ts/tools/plan/plan.ts). That makes plan files effectively global instead of per-session, so plan approval/review can read or overwrite another session’s plan content.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/hooks/engine.ts Outdated
): Promise<HookResult> {
const timeout = (opts?.timeout ?? 30) * 1000;
try {
const proc = Bun.spawn(["sh", "-c", command], {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use platform-appropriate shell to run hook commands

Hook execution is hardcoded to sh -c, which fails on Windows environments that do not provide a POSIX sh. In those setups, configured hook policies (including PreToolUse) never actually execute, so approval/policy enforcement via hooks is silently bypassed.

Useful? React with 👍 / 👎.

@Tubring25

Tubring25 commented Apr 3, 2026

Copy link
Copy Markdown

震撼首发👍


Shocking first release👍

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 29 additional findings in Devin Review.

Open in Devin Review

Comment thread src/kimi_cli_ts/wire/file.ts Outdated
Comment thread src/kimi_cli_ts/llm.ts Outdated
Comment on lines +612 to +614
} finally {
reader.releaseLock();
}

@devin-ai-integration devin-ai-integration Bot Apr 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Pending tool calls silently dropped when SSE stream ends without finish_reason

In OpenAICompatibleProvider.chat, tool call deltas are accumulated in pendingToolCalls (line 517-520) and only yielded when a chunk with finish_reason is received (line 598-608). After the stream reading loop exits (line 611), the code proceeds directly to emitting usage and done chunks (line 616-628) without checking for remaining entries in pendingToolCalls. If the stream ends without a finish_reason chunk (e.g. connection drop, API quirk, or the [DONE] sentinel arrives before finish_reason), all accumulated tool calls are silently lost. The caller (KimiSoul._step) would see zero tool calls, end the agent turn prematurely, and the LLM's tool call intent would be invisible to the user.

Missing flush after stream loop

Add after line 613 (reader.releaseLock()):

// Flush any remaining pending tool calls
for (const [, tc] of pendingToolCalls) {
  yield { type: "tool_call", id: tc.id, name: tc.name, arguments: tc.arguments };
}
pendingToolCalls.clear();
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

…fy UI panels

Refactor the main agent loop in kimisoul.ts for clarity, enhance grep
tool with advanced options and ask_user with multi-question support,
simplify QuestionPanel/SelectionPanel UI, and improve subagent runner
and CLI option handling.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 090fa31e52

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/soul/index.ts Outdated
Comment on lines +201 to +205
if (result === "cancelled") {
logger.debug("Cancelling the run");
// The soul should check the abort signal and stop
// We need to wait for it to actually stop
throw new RunCancelled();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Abort active soul run before returning RunCancelled

When cancellation wins the race, this branch throws RunCancelled without signaling the soul to stop, so KimiSoul.run() can continue executing LLM/tool work in the background after the UI path exits. In interactive Ctrl+C scenarios this leaves the prior turn running until natural completion (or timeout), which can block subsequent turns and produce late writes/errors against a shutting-down wire. Triggering soul.abort() before exiting this path is needed to make cancellation effective.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/soul/kimisoul.ts Outdated
Comment on lines +786 to +790
contentParts.push({
type: "tool_use",
id: tc.id,
name: tc.name,
input: JSON.parse(tc.arguments || "{}"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard tool-use argument parsing against malformed JSON

This eagerly parses streamed tool-call arguments with JSON.parse(...) while building the assistant message. If the model emits malformed/incomplete arguments (a common transient in streamed tool calls), _step() throws before tool execution, so the turn fails instead of returning a normal tool-parse error. Since tool argument parsing is already handled defensively in toolset.handle(), this unchecked parse introduces a hard crash path for otherwise recoverable tool calls.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/tools/file/replace.ts Outdated
}
return filePath;
}
return resolve(workingDir, filePath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Block parent traversal in StrReplaceFile relative paths

Relative paths are normalized with resolve(workingDir, filePath) but never constrained back to the workspace root, so inputs like ../../.ssh/config are accepted as valid edit targets. The tool contract says absolute paths are required outside the working directory, yet this allows escaping the workspace via relative traversal and modifying unintended files.

Useful? React with 👍 / 👎.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 42 additional findings in Devin Review.

Open in Devin Review

Comment thread src/kimi_cli_ts/session.ts Outdated
Comment on lines +179 to +180
const stat = await Bun.$`stat -f %m ${this.contextFile} 2>/dev/null || stat -c %Y ${this.contextFile} 2>/dev/null`.quiet().text();
this.updatedAt = Number.parseFloat(stat.trim()) || 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 stat -f %m returns wrong data on Linux, breaking session timestamps

On macOS, stat -f %m <file> returns the file modification time in epoch seconds. On Linux (GNU coreutils), -f means "display file system status" and %m means "maximum filename length" (typically 255). Since this command succeeds on Linux (exit code 0), the || fallback to stat -c %Y never executes. The result is that this.updatedAt gets a filesystem metadata value (e.g., 255) instead of the actual modification timestamp.

This causes incorrect session ordering in Session.list() (which sorts by updatedAt), the /sessions panel, and the session picker (--session flag).

Suggested change
const stat = await Bun.$`stat -f %m ${this.contextFile} 2>/dev/null || stat -c %Y ${this.contextFile} 2>/dev/null`.quiet().text();
this.updatedAt = Number.parseFloat(stat.trim()) || 0;
const stat = await Bun.$`stat -c %Y ${this.contextFile} 2>/dev/null || stat -f %m ${this.contextFile} 2>/dev/null`.quiet().text();
this.updatedAt = Number.parseFloat(stat.trim()) || 0;
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 947ab80e6b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/soul/kimisoul.ts Outdated
Comment on lines +191 to +195
const flowCommand: SlashCommand = {
name: commandName,
description: skill.description || "",
handler: this._makeSkillRunner(skill),
aliases: [],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Execute flow-skill commands with FlowRunner

For flow skills, /flow:<name> is wired to this._makeSkillRunner(skill), but that runner only reads SKILL.md and calls _turn(...) once, so decision edges/loop control in skill.flow are never executed. In practice, flow skills behave like plain prompt injections instead of graph-driven workflows, which breaks any skill relying on branch selection or repeated iterations.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/soul/kimisoul.ts Outdated
Comment on lines +598 to +599
// Execute tools and collect results — shielded from abort
await this._executeToolsShielded(toolCalls);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Re-check abort before dispatching tool calls

Cancellation is only checked before _step(), but not after it returns. If the abort signal is raised while _step() is in progress (e.g. during model streaming) and tool calls are produced, this branch still executes those tools, so a cancelled turn can still run side-effecting commands/filesystem edits. Add an abort guard before calling _executeToolsShielded(...) to prevent post-cancel tool execution.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/app.ts Outdated
Comment on lines +72 to +75
let planMode = opts.planMode ?? false;
if (!opts.resumed) {
planMode = planMode || config.default_plan_mode;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply default plan mode after session restore status

The plan-mode default is computed from opts.resumed before session lookup sets the local resumed flag, so resumed sessions are treated as “new” during this decision. With default_plan_mode=true, resuming an existing session that previously had plan mode off will be forced back into plan mode on startup, overriding persisted session behavior.

Useful? React with 👍 / 👎.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 45 additional findings in Devin Review.

Open in Devin Review

Comment thread src/kimi_cli_ts/tools/file/grep.ts Outdated
Comment on lines +60 to +64
// Read the embedded binary content
const file = Bun.file(embeddedRgPath);
const buffer = new Uint8Array(file.size);
const reader = file.stream().getReader();
let offset = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Stream reader resource leak and dead code in extractEmbeddedRg

At lines 61-64, Bun.file(embeddedRgPath).stream().getReader() acquires a read lock on the stream, but the reader is never released via releaseLock() or consumed. The function immediately falls through to fs.readFileSync at line 69 instead. The buffer (line 62), reader (line 63), and offset (line 64) variables are allocated but never used — this is leftover dead code from an incomplete refactor. The unreleased reader holds a lock on the underlying stream, constituting a resource leak (though mitigated by the function only being called once at first grep invocation).

Suggested change
// Read the embedded binary content
const file = Bun.file(embeddedRgPath);
const buffer = new Uint8Array(file.size);
const reader = file.stream().getReader();
let offset = 0;
// Synchronous-ish extraction: we use a blocking pattern since this
// only runs once on first grep invocation.
// Use writeFileSync with the embedded path directly via fs.
const content = fs.readFileSync(embeddedRgPath);
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@Yuandiaodiaodiao Yuandiaodiaodiao force-pushed the worktree-bun-ts-refactor branch from 947ab80 to 9f9b2dc Compare April 5, 2026 11:17

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f9b2dcccd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/kimi_cli_ts/tools/file/write.ts Outdated
}
return filePath;
}
return resolve(workingDir, filePath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Block parent traversal in WriteFile relative paths

The relative-path resolver currently does resolve(workingDir, filePath) without checking that the result stays under workingDir, so inputs like ../../.ssh/config are accepted and can overwrite files outside the workspace while still using a relative path. This breaks the tool contract that absolute paths are required for out-of-workdir writes and creates an unintended arbitrary file-write path.

Useful? React with 👍 / 👎.

Comment thread src/kimi_cli_ts/session.ts Outdated
Comment on lines +169 to +171
const file = Bun.file(sessionDir);
if (await file.exists()) {
await Bun.$`rm -rf ${sessionDir}`.quiet();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use directory-aware existence check before deleting session

Session.delete() gates removal on Bun.file(sessionDir).exists(), but Bun.file(...).exists() returns false for directories, so the rm -rf branch is skipped for normal session directories. In practice, empty sessions that shutdown tries to clean up are never deleted, causing stale session accumulation and breaking expected cleanup behavior.

Useful? React with 👍 / 👎.

…mports, fix print mode

Structural alignment:
- Add missing barrel index.ts for all modules (approval_runtime, background, hooks,
  plugin, subagents, soul/dynamic_injections, tools/*, utils)
- Extract share.ts, session_state.ts from config.ts/session.ts
- Rename tool files to index.ts for symmetry (agent.ts→index.ts, etc.)
- Add new modules: acp/, vis/, web/, cli/toad.ts, tools/plan/enter.ts, tools/test.ts
- Add 13 missing utils (datetime, editor, envvar, frontmatter, io, etc.)

Bug fixes:
- Fix circular import cli→app→soul→cli by extracting cli/errors.ts
- Lazy-load heavy modules (react, ink, app, soul) in CLI handler
- Fix print mode to match Python's rich.pretty_repr output format

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Yuandiaodiaodiao and others added 5 commits April 7, 2026 00:52
…entation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…I and LLM support

Major changes across the TS codebase: expanded toolset with MCP integration,
enhanced wire server/serde/jsonrpc, improved LLM multi-provider support,
new TaskPanel UI component, CLI command improvements (init/model/info),
and various soul layer refinements (context, kimisoul, agent).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…baseline build target

Remove 6 unused cell-level diffing files (ansi-parser, screen, types, diff, patch-writer, csi) from ui/renderer/ and clean up their re-exports from index.ts. Add build:linux-x64-baseline script to package.json.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a60a91fee

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +629 to +631
// Accept and register external tool
accepted.push(tool.name);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register accepted external tools with the toolset

This initialize path reports external tools as accepted but never actually registers them (accepted.push(...) is the only action). Because KimiToolset.registerExternalTool(...) is never called, the accepted tools are not added to the model-visible tool list, so wire clients that send external_tools get a successful handshake but those tools cannot be invoked in later turns.

Useful? React with 👍 / 👎.

Comment on lines +669 to +673
return runWithWireContext(wire, async () => {
const streamPromise = streamCallback(wire);
try {
await app.soul.run(userInput as string | ContentPart[]);
return "finished";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Respect cancel events in wire-mode prompt execution

Wire cancellation is effectively ignored here: the server passes a cancelEvent into onPrompt, but this implementation immediately awaits app.soul.run(...) without observing that signal. In wire clients, a cancel RPC can be acknowledged while the turn keeps running (including tool calls), which breaks expected interrupt semantics for long-running prompts.

Useful? React with 👍 / 👎.

Comment on lines +543 to +547
const app = await KimiCLI.create({
workDir: options.workDir,
additionalDirs: options.addDir,
configFile,
modelName: options.model,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass --config text into print-mode app creation

The CLI advertises --config as inline TOML/JSON config, but print mode only forwards configFile and drops options.config entirely. As a result, kimi --print --config '...' silently uses default/file config instead of the provided override, which can select the wrong model/provider/settings in automation.

Useful? React with 👍 / 👎.

Comment on lines +559 to +560
if (prompt) {
// Echo user input to stdout (matches Python Print.run() line 89)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Read stdin in print mode when no prompt is provided

Print mode only executes a run inside if (prompt), and prompt is derived from positional args/--prompt earlier, so piped stdin input is never consumed. This breaks non-interactive usage like echo "..." | kimi --print and also prevents --input-format stream-json from processing line-delimited user messages when no explicit prompt argument is passed.

Useful? React with 👍 / 👎.

…ython version

Add structured error classes (APIConnectionError, APITimeoutError,
APIStatusError, APIEmptyResponseError), RetryableChatProvider interface,
stream timeout mechanisms, and connection recovery logic to match the
Python kosong/kimisoul implementation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6cc2f7ea53

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

}
return filePath;
}
return resolve(workingDir, filePath);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Block parent traversal in ReadMediaFile relative paths

The path resolver accepts any non-absolute input via resolve(workingDir, filePath) without checking that the result stays under the workspace. That means inputs like ../../private/image.png are treated as valid relative paths and can read files outside workingDir, even though the tool contract says out-of-workdir access must use absolute paths. In environments where this tool is exposed to model-generated arguments, this becomes an unintended data-exfiltration path.

Useful? React with 👍 / 👎.

@Yuandiaodiaodiao

Copy link
Copy Markdown
Author

?

@fooofei

fooofei commented May 25, 2026

Copy link
Copy Markdown

already have https://github.com/MoonshotAI/kimi-code
install:
1、 version from https://code.kimi.com/kimi-code/latest
2、download from https://code.kimi.com/kimi-code/${version}/kimi-code-win32-x64.exe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.