@litfamily/litopencode 1.0.18 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README-Ko-KR.md +56 -86
  3. package/README.md +57 -86
  4. package/dist/activation-routing.js +3 -1
  5. package/dist/cli/args.js +5 -1
  6. package/dist/cli/auto-update.js +1 -1
  7. package/dist/cli/doctor.js +7 -5
  8. package/dist/cli/host-limits.js +2 -5
  9. package/dist/cli/install-config.d.ts +4 -0
  10. package/dist/cli/install-config.js +27 -1
  11. package/dist/cli/install-report.js +4 -1
  12. package/dist/cli/install.js +89 -28
  13. package/dist/cli/json.d.ts +1 -0
  14. package/dist/cli/json.js +10 -4
  15. package/dist/cli/jsonc.d.ts +17 -0
  16. package/dist/cli/jsonc.js +174 -0
  17. package/dist/cli/loop.d.ts +1 -1
  18. package/dist/cli/loop.js +81 -1
  19. package/dist/cli/motion-runtime.d.ts +13 -1
  20. package/dist/cli/motion-runtime.js +89 -2
  21. package/dist/cli/plugin-mutation.js +11 -6
  22. package/dist/cli/tui-config.d.ts +52 -0
  23. package/dist/cli/tui-config.js +198 -0
  24. package/dist/cli/types.d.ts +4 -0
  25. package/dist/cli.js +8 -3
  26. package/dist/deliverable-hedge-guard.js +2 -2
  27. package/dist/tui/mission-band.d.ts +47 -0
  28. package/dist/tui/mission-band.js +290 -0
  29. package/dist/tui/mission-model.d.ts +139 -0
  30. package/dist/tui/mission-model.js +318 -0
  31. package/dist/tui/mission-motion.d.ts +66 -0
  32. package/dist/tui/mission-motion.js +147 -0
  33. package/dist/tui/mission-skills.d.ts +38 -0
  34. package/dist/tui/mission-skills.js +81 -0
  35. package/dist/tui/mission-source.d.ts +68 -0
  36. package/dist/tui/mission-source.js +112 -0
  37. package/dist/tui/mission-text.d.ts +106 -0
  38. package/dist/tui/mission-text.js +613 -0
  39. package/dist/tui.d.ts +6 -0
  40. package/dist/tui.js +342 -0
  41. package/docs/assets/readme/badge-version.svg +1 -1
  42. package/docs/reference-Ko-KR.md +91 -1
  43. package/docs/reference.md +100 -1
  44. package/package.json +21 -2
  45. package/skills/lit-pptx/scripts/lib/template-registry.js +1 -1
  46. package/skills/lit-pptx/templates/tonalities/atlas/pack.yaml +0 -6
  47. package/skills/lit-pptx/templates/tonalities/chalk/pack.yaml +0 -6
  48. package/skills/lit-pptx/templates/tonalities/gazette/pack.yaml +0 -6
  49. package/skills/lit-pptx/templates/tonalities/ledger/pack.yaml +0 -6
  50. package/skills/lit-pptx/templates/tonalities/night/pack.yaml +0 -6
  51. package/skills/lit-pptx/templates/tonalities/paper/pack.yaml +0 -6
  52. package/skills/lit-pptx/templates/tonalities/signal/pack.yaml +0 -6
  53. package/skills/lit-pptx/templates/tonalities/studio/pack.yaml +0 -6
  54. package/skills/managed-skill-manifest.json +9 -9
  55. package/tools/check-pack-payload.mjs +20 -5
  56. package/tools/version-manifests.json +18 -6
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ are tracked separately from this product history.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 1.0.20 - 2026-10-12
9
+
10
+ - The band along the bottom of OpenCode's main screen is now a small card that stays on screen for the whole session. The top edge shows LIT, the current workflow and its phase. The first row shows PLAN ━ WORK ━ REVIEW with the goal and the next task. The second row shows a chip for each LitOpenCode skill that ran: skills used by the current request are bright and get a short shine as they appear, then dim into a recent list when the answer is done, and extra chips fold into `+N`. The bottom edge shows context use, elapsed time and the git branch. Before you use lit in a session, the card shows the project's last goal. `band: false` still hides it and `band: { card: true }` still adds the detail card.
11
+
12
+ ## 1.0.19 - 2026-10-11
13
+
14
+ - The README is shorter for first-time readers. It says what LitOpenCode does, how to install it and what to type first, and the detail now lives in docs pages (guide, skills, screens and brand) in English and Korean. The GitHub page also plays a short film of a real session.
15
+ - A lit mission now shows in OpenCode's TUI. A pill chip sits by the prompt, a rounded sidebar panel carries the phase and a smooth progress bar, a band runs along the bottom of the main screen, the home screen shows a line for the last mission, and a finished mission gets a short completion moment. In the plugin's `tui.json` options, `band: false` turns the band off and `band: { card: true }` adds a card under it with the warnings, the next task and the goal.
16
+ - Install adds the TUI plugin to `tui.json` or `tui.jsonc` and backs the file up first, and `litopencode tui remove` takes it out again. Auto-update backs up these files with the rest of the install and puts them back when an update fails.
17
+ - Install and doctor read a commented `opencode.jsonc` safely. They accept comments, trailing commas, a BOM and CRLF line endings, and an edit keeps every comment. When a comment sits inside an entry that has to change, install stops before writing anything and prints the entry to add by hand. A second install from a local checkout no longer adds a duplicate plugin entry.
18
+ - A prompt such as "lit turn notes.md into a 10-slide deck" now loads the office skills: lit-pptx for a deck and lit-docx for a Word report. The dot in a file name no longer breaks the match.
19
+ - The prose check that runs before a write now skips YAML, JSON and CSV files, so a line such as `source:` in a config file no longer blocks the write.
20
+ - `--dry-run` writes nothing. Loop commands that change the ledger list the files they would touch, and `motion-runtime` refuses an unknown option before it downloads anything.
21
+
8
22
  ## 1.0.18 - 2026-10-09
9
23
 
10
24
  - The prose check that runs before a file is written now works inside OpenCode. It used to start under OpenCode's own program instead of Node and to look for the new text in the wrong place, so it never saw a write. It now stops an added line such as an unrequested source label before the file is saved, adds a short note after the write when the wording only needs a second look, and checks the text of the Word, PowerPoint and PDF files a session creates.
package/README-Ko-KR.md CHANGED
@@ -1,120 +1,90 @@
1
- <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/cover-motion.webp" width="100%" alt="LitFamily 모션 커버: 다섯 로봇 패널이 차례로 켜지고, LitOpenCode 로봇의 눈과 테두리가 빛난 뒤 LITFAMILY와 KEEP THE WORK LIT. 문구가 밝아지는 영상" /></picture></p>
2
-
3
- <p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/readme/ascii-readme.svg" width="480" alt="LIT ASCII B 마크" /></p>
4
-
5
- <details>
6
- <summary>ASCII 로고 복사</summary>
7
-
8
- ```text
9
- ▄▄▄▄
10
- ▗███▌ ▗██████▖
11
- ▗▄▄▄▄▄ ▗▟████▌ ▝██████▘
12
- ▐█████ ▗▟██████▌ ▝▀▜█▀▘
13
- ▐█████ ▗▟███████▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
14
- ▐█████ ▗▟█████████████████████████ ▐█▀
15
- ▐█████ ████████████████████████████▀
16
- ▐█████ ██▛▘ ▄ ▄▄▄▄▖▄▄▄▄▄▄▄▄▄▄▄▄▄▖
17
- ▐█████ ▀ ▄██ ████▌█████████████▌
18
- ▐█████ ▄████ ████▌█████████████▌
19
- ▐█████ ▄█████▛ litopencode
20
- ▐█████ ▗▟█████▀▘ ▄▄▄▄▄ ▗▖
21
- ▐█████ ▐█████▀ █████ ▐▛▀
22
- ▐█████ ▐███▀ █████
23
- ▐█████ ▐█▀ █████
24
- ▐█████ ▝ █████
25
- ▐█████▄▄▄▄▄▄▄▖ █████
26
- ▐███████████▛ █████
27
- ▐██████████▀ █████
28
-
29
- ```
30
-
31
- </details>
1
+ <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/assets/cover-motion.webp" width="100%" alt="LitFamily 모션 커버: 다섯 로봇 패널이 차례로 켜지고, LitOpenCode 로봇의 눈과 테두리가 빛난 뒤 LITFAMILY와 KEEP THE WORK LIT. 문구가 밝아지는 영상" /></picture></p>
32
2
 
33
3
  # LitOpenCode
34
4
 
35
- [English](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/README.md) · [한국어](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/README-Ko-KR.md)
36
-
37
- > **불씨를 건네받았다.**<br>
38
- > **이제, 당신의 작업에 옮길 차례다.**
5
+ **LitOpenCode는 OpenCode용 워크플로 플러그인입니다. 프롬프트에 `lit`만 붙이면 계획하고, 만들고, 확인하고, 기록까지 남겨서 다음 세션이 어디서 멈췄는지 바로 압니다.**
39
6
 
40
7
  <p align="center">
41
- <a href="#설치"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/readme/badge-version.svg" alt="1.0.18" /></a>
42
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/readme/badge-license.svg" alt="MIT 라이선스" /></a>
8
+ <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/assets/readme/badge-version.svg" alt="1.0.20" /></a>
9
+ <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://img.shields.io/npm/dm/@litfamily/litopencode?label=downloads&color=D7F75B" alt="월간 npm 다운로드 수" /></a>
10
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/assets/readme/badge-license.svg" alt="MIT 라이선스" /></a>
11
+ <a href="https://github.com/wjgoarxiv/litopencode"><img src="https://img.shields.io/github/stars/wjgoarxiv/litopencode?style=social" alt="GitHub 스타" /></a>
43
12
  </p>
44
13
 
45
- LitOpenCode는 OpenCode에 워크플로 agent, 슬래시 명령, 로컬 근거 기록을 더합니다.
46
-
47
- 프롬프트에 `lit`을 붙이면 계획하고, 만들고, 확인한 뒤 다음 할 일을 프로젝트에 적어 둡니다. 그래서 다음 세션이 멈춘 자리에서 바로 이어 갈 수 있습니다. 모델을 돌리고 권한을 묻는 일은 지금처럼 OpenCode가 합니다.
14
+ LitOpenCode는 목표와 계획, 확인한 내용, 다음 할 일을 프로젝트 안의 `.litopencode/litgoal/`에 남겨 두기 때문에, 긴 작업도 다음 날 멈춘 자리에서 이어 갈 수 있습니다.
48
15
 
49
- **[전체 안내와 스킬 갤러리는 GitHub에서 →](https://github.com/wjgoarxiv/litopencode/blob/master/README-Ko-KR.md)**
50
-
51
- ## 설치
52
-
53
- Node.js와 npm, 그리고 OpenCode가 있으면 됩니다.
54
-
55
- ```sh
56
- LIT_PACKAGE='@litfamily/litopencode@latest'
57
- npm exec --package "$LIT_PACKAGE" -- litopencode install
58
- ```
16
+ **[전체 안내, 화면, 스킬 갤러리는 GitHub에서 →](https://github.com/wjgoarxiv/litopencode/blob/main/README-Ko-KR.md)**
59
17
 
60
- OpenCode를 다시 시작하고 `Tab` 키를 눌러 `lit-loop`를 고르세요. 권한은 `safe`에서 시작하는데, 무언가 하기 전에 OpenCode가 먼저 물어보는 설정입니다. 로그인과 API 키는 OpenCode에 이미 설정해 둔 모델 제공자(provider) 쪽 것을 그대로 씁니다.
18
+ ## 빠르게 시작하기
61
19
 
62
- 바뀔 내용을 먼저 보려면 `--dry-run`을 붙이세요. 아무것도 쓰지 않습니다. 질문 없이 설치하려면 `--yes`를 붙이세요. 기본값으로 설치하되 예전에 저장해 둔 선택은 그대로 둡니다.
20
+ Node.js와 npm, 그리고 모델 제공자를 설정해 둔 OpenCode가 있으면 됩니다.
63
21
 
64
22
  ```sh
65
- npm exec --package @litfamily/litopencode@latest -- litopencode install --dry-run # 변경 사항 미리 보기
66
- npm exec --package @litfamily/litopencode@latest -- litopencode install --yes # 기본값 사용, 저장된 선택 유지
23
+ npm exec --package @litfamily/litopencode@latest -- litopencode install
67
24
  ```
68
25
 
69
- ## 첫 작업
70
-
71
- 빈 폴더를 OpenCode에서 열고 `lit-loop`를 고른 다음 이렇게 보내 보세요.
26
+ 기본값으로 바로 설치하려면 `--yes`를, 아무것도 쓰지 않고 무엇이 바뀔지만 보려면 `--dry-run`을 붙이세요. OpenCode를 다시 시작하고 **Tab** 키로 **lit-loop**를 고른 뒤, 빈 폴더를 열고 이렇게 보냅니다.
72
27
 
73
28
  ```text
74
29
  lit 외부 의존성 없이 index.html 하나로 할 일 목록을 만들고, 추가와 완료 표시를 확인한 뒤 다음 할 일을 남겨줘.
75
30
  ```
76
31
 
77
- `index.html`이 만들어지면 열어서 직접 써 보세요. 다음에 돌아왔을 때는 `/lit-recap`이 어디까지 했는지 알려 줍니다.
32
+ 직접 열어 볼 수 있는 `index.html`이 생기고, 다음에 돌아오면 `/lit-recap`이 어디까지 했는지 알려 줍니다.
78
33
 
79
- ## 자주 쓰는 경로
34
+ ## 할 수 있는 일
80
35
 
81
36
  | 입력 | 하는 일 |
82
37
  | --- | --- |
83
- | `lit` 또는 `/lit` | 범위가 분명한 작업을 시작하고, 확인한 내용을 기록합니다. |
84
- | `lit-plan` → `/start-work` → `/review-work` | 계획, 승인, 실행, 검토. planner는 파일을 고치거나 shell을 실행할 수 없습니다. |
85
- | `handoff` 또는 `/lit-handoff` | 현재 결과와 다음 할 일을 다음 세션에 넘깁니다. |
86
- | `/lit-recap` | 로컬 기록을 짧게 요약해 보여 줍니다. |
87
- | `/litresearch` | 출처 근거를 남기며 조사합니다. |
88
-
89
- 이 밖에도 디버깅, 리팩터링, 코드 검토, 조사 스킬이 있고, 만들기 전에 디자인 방향을 골라 알려 주는 워드 보고서(`lit-docx`)와 파워포인트 발표자료(`lit-pptx`), 다이어그램, 과학 그림, 화면 구현, README, 글 다듬기(`/lit-humanizer`) 스킬도 함께 설치됩니다. 스킬마다 무엇을 만드는지는 그림과 함께 [GitHub 스킬 갤러리](https://github.com/wjgoarxiv/litopencode/blob/master/README-Ko-KR.md#스킬-한눈에-보기)에 있습니다.
90
-
91
- ## 설치하면 바뀌는 것
92
-
93
- 많지 않습니다. OpenCode 설정에 플러그인을 등록하고, LitOpenCode의 명령과 스킬 파일을 OpenCode 설정 폴더에 복사합니다. 어떤 일에 어떤 모델을 쓸지 정한 route는 `~/.config/opencode/litopencode.json`에 저장됩니다. `XDG_CONFIG_HOME`을 지정하면 설정 폴더가 그쪽으로 옮겨 가고, 이미 손봐 둔 route는 건드리지 않습니다. 작업 기록은 프로젝트마다 `.litopencode/litgoal/`에 쌓이고, 다음 세션은 이 기록을 읽고 이어 갑니다.
38
+ | `lit` 또는 `/lit` | 일을 정리하고, 계획하고, 만들고, 확인하고, 요약하며 단계마다 기록을 남깁니다. |
39
+ | `lit-plan` → `/start-work` → `/review-work` | 계획하고, 승인하고, 실행하고, 검토합니다. 계획 에이전트는 파일을 고치거나 셸 명령을 실행할 수 없습니다. |
40
+ | `handoff` · `/lit-recap` | 다음 세션이 이어 갈 인수인계 파일을 남기고, 끝낸 일과 다음 할 일을 다시 읽어 줍니다. |
41
+ | `/litresearch` | 여러 차례 조사하며 주장마다 출처를 남깁니다. |
42
+ | `lit` + 보고서·발표자료 | 마크다운 원고와 함께 편집할 수 있는 워드 보고서나 파워포인트 덱을 만듭니다. |
43
+ | `/lit-humanizer` | 딱딱한 영어나 한국어 문장을 사실은 그대로 두고 다시 씁니다. |
94
44
 
95
- OpenAI 제공자를 쓰면 새로 설치했을 때 계획과 검토는 GPT-6 Astra(`gpt-6-astra`)가 `xhigh`로, 만들기와 조사는 GPT-6 Luna(`gpt-6-luna`)가 `max`로 맡습니다.
45
+ 스킬마다 만들어 내는 결과의 그림은 [GitHub의 스킬 갤러리](https://github.com/wjgoarxiv/litopencode/blob/main/docs/skills-Ko-KR.md)에서 볼 수 있습니다.
96
46
 
97
- ## 안전과 업데이트
47
+ ## 업데이트와 제거
98
48
 
99
- - planner는 계획만 세웁니다. `lit-plan`은 `edit`, `bash`, `task` 권한이 거부되어 있고, 더 느슨한 `balanced`나 `yolo` 모드를 직접 골라도 그대로입니다.
100
- - LitOpenCode는 대화형으로 시작할 때와 install이나 doctor가 성공한 뒤에 스스로 업데이트할 수 있습니다. 끄려면 `--no-auto-update`를 붙이거나 `LITOPENCODE_NO_AUTO_UPDATE=1`을 설정하세요.
101
- - 스킬 학습 기능은 제거되었습니다. 이전 릴리스가 프로젝트의 `.litopencode` 폴더에 남긴 학습 기록은 더 이상 쓰이지 않으니, 남겨 두든 지우든 편한 대로 하면 됩니다.
102
- - Jev 스킬 힌트는 선택 기능이고 기본값은 꺼짐입니다. 켜면 조건에 맞는 프롬프트가 TypeSafe로 전송되니, 먼저 [Jev 스킬 힌트 참조 문서](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/reference-Ko-KR.md#jev-스킬-힌트-선택)를 읽어 보세요. 알림과 doctor 줄이 어떻게 보이는지는 [GitHub 페이지](https://github.com/wjgoarxiv/litopencode/blob/master/README-Ko-KR.md#화면에서-보이는-것)에서 볼 수 있습니다.
103
- - 뭔가 이상하면 `npm exec --package @litfamily/litopencode@latest -- litopencode doctor`로 패키지와 설정 상태를 확인하세요.
49
+ LitOpenCode는 대화형으로 시작할 때와 install·doctor가 성공한 뒤 새 버전을 확인합니다. 끄려면 `--no-auto-update`를 붙이거나 `LITOPENCODE_NO_AUTO_UPDATE=1`을 설정하세요. `npm exec --package @litfamily/litopencode@latest -- litopencode doctor`는 설치 상태를 확인할 뿐 아무것도 바꾸지 않습니다.
104
50
 
105
- ## 제거
106
-
107
- 별도의 제거 명령은 없습니다. OpenCode의 `opencode.jsonc`(custom root라면 `opencode.json`)에서 `plugin` 배열의 `@litfamily/litopencode` 항목을 지우고 OpenCode를 다시 시작하세요. 전역으로 설치했다면 다음도 실행합니다.
51
+ 제거하려면 OpenCode의 `opencode.jsonc`에서 `plugin` 배열의 `@litfamily/litopencode` 항목을 지우고 OpenCode를 다시 시작하세요. 전역으로 설치했다면 다음 명령도 실행합니다.
108
52
 
109
53
  ```sh
110
54
  npm uninstall -g @litfamily/litopencode
111
55
  ```
112
56
 
113
- 남겨 둘 만한 설치 파일과 기록은 [제거 상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/reference-Ko-KR.md#제거)에 정리되어 있습니다.
114
-
115
57
  ## 더 보기
116
58
 
117
- - [GitHub 전체 안내](https://github.com/wjgoarxiv/litopencode/blob/master/README-Ko-KR.md)
118
- - [워크플로 상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/reference-Ko-KR.md) · [English reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/reference.md)
119
- - [마이그레이션 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/migration.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/CHANGELOG.md) · [개인정보와 네트워크 동작](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/privacy.md)
120
- - [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/LICENSE) · [ASCII 글꼴 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/readme/JetBrainsMono-OFL.txt)
59
+ - [GitHub의 사용 안내](https://github.com/wjgoarxiv/litopencode/blob/main/docs/guide-Ko-KR.md): 설치 옵션, 자동 인수인계, 문제 해결, 제거
60
+ - [워크플로 상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/reference-Ko-KR.md) · [English reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/reference.md)
61
+ - [마이그레이션 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/migration.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/CHANGELOG.md) · [개인정보와 네트워크 동작](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/privacy.md)
62
+ - [LitFamily 허브](https://github.com/wjgoarxiv/litfamily) · [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/LICENSE) · [ASCII 글꼴 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/assets/readme/JetBrainsMono-OFL.txt)
63
+
64
+ <details>
65
+ <summary>ASCII 로고 복사</summary>
66
+
67
+ ```text
68
+ ▄▄▄▄
69
+ ▗███▌ ▗██████▖
70
+ ▗▄▄▄▄▄ ▗▟████▌ ▝██████▘
71
+ ▐█████ ▗▟██████▌ ▝▀▜█▀▘
72
+ ▐█████ ▗▟███████▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
73
+ ▐█████ ▗▟█████████████████████████ ▐█▀
74
+ ▐█████ ████████████████████████████▀
75
+ ▐█████ ██▛▘ ▄ ▄▄▄▄▖▄▄▄▄▄▄▄▄▄▄▄▄▄▖
76
+ ▐█████ ▀ ▄██ ████▌█████████████▌
77
+ ▐█████ ▄████ ████▌█████████████▌
78
+ ▐█████ ▄█████▛ litopencode
79
+ ▐█████ ▗▟█████▀▘ ▄▄▄▄▄ ▗▖
80
+ ▐█████ ▐█████▀ █████ ▐▛▀
81
+ ▐█████ ▐███▀ █████
82
+ ▐█████ ▐█▀ █████
83
+ ▐█████ ▝ █████
84
+ ▐█████▄▄▄▄▄▄▄▖ █████
85
+ ▐███████████▛ █████
86
+ ▐██████████▀ █████
87
+
88
+ ```
89
+
90
+ </details>
package/README.md CHANGED
@@ -1,119 +1,90 @@
1
- <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: five armored robots power on one by one, the LitOpenCode robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
2
-
3
- <p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/readme/ascii-readme.svg" width="480" alt="LIT ASCII B mark" /></p>
4
-
5
- <details>
6
- <summary>Copy ASCII logo</summary>
7
-
8
- ```text
9
- ▄▄▄▄
10
- ▗███▌ ▗██████▖
11
- ▗▄▄▄▄▄ ▗▟████▌ ▝██████▘
12
- ▐█████ ▗▟██████▌ ▝▀▜█▀▘
13
- ▐█████ ▗▟███████▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
14
- ▐█████ ▗▟█████████████████████████ ▐█▀
15
- ▐█████ ████████████████████████████▀
16
- ▐█████ ██▛▘ ▄ ▄▄▄▄▖▄▄▄▄▄▄▄▄▄▄▄▄▄▖
17
- ▐█████ ▀ ▄██ ████▌█████████████▌
18
- ▐█████ ▄████ ████▌█████████████▌
19
- ▐█████ ▄█████▛ litopencode
20
- ▐█████ ▗▟█████▀▘ ▄▄▄▄▄ ▗▖
21
- ▐█████ ▐█████▀ █████ ▐▛▀
22
- ▐█████ ▐███▀ █████
23
- ▐█████ ▐█▀ █████
24
- ▐█████ ▝ █████
25
- ▐█████▄▄▄▄▄▄▄▖ █████
26
- ▐███████████▛ █████
27
- ▐██████████▀ █████
28
-
29
- ```
30
-
31
- </details>
1
+ <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: five armored robots power on one by one, the LitOpenCode robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
32
2
 
33
3
  # LitOpenCode
34
4
 
35
- [English](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/README.md) · [한국어](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/README-Ko-KR.md)
36
-
37
- **Keep the work lit.**
5
+ **LitOpenCode is a workflow plugin for OpenCode. Add `lit` to a prompt and the work gets planned, built, checked and written down, so the next session knows where you stopped.**
38
6
 
39
7
  <p align="center">
40
- <a href="#install"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/readme/badge-version.svg" alt="1.0.18" /></a>
41
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
8
+ <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/assets/readme/badge-version.svg" alt="1.0.20" /></a>
9
+ <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://img.shields.io/npm/dm/@litfamily/litopencode?label=downloads&color=D7F75B" alt="npm downloads per month" /></a>
10
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
11
+ <a href="https://github.com/wjgoarxiv/litopencode"><img src="https://img.shields.io/github/stars/wjgoarxiv/litopencode?style=social" alt="GitHub stars" /></a>
42
12
  </p>
43
13
 
44
- LitOpenCode adds workflow agents, slash commands, and a local evidence ledger to OpenCode.
45
-
46
- Add `lit` to a prompt and it plans, builds, checks, and writes down the next step in your project, so a later session can pick up where this one stopped. OpenCode still runs the model and asks for permission the way it always has.
14
+ LitOpenCode keeps the goal, the plan, what was checked and the next step in `.litopencode/litgoal/` inside your project, so a long task picks up the next day where it stopped.
47
15
 
48
- **[Full guide and skills gallery on GitHub →](https://github.com/wjgoarxiv/litopencode#readme)**
49
-
50
- ## Install
51
-
52
- You need Node.js with npm, and OpenCode.
53
-
54
- ```sh
55
- LIT_PACKAGE='@litfamily/litopencode@latest'
56
- npm exec --package "$LIT_PACKAGE" -- litopencode install
57
- ```
16
+ **[Full guide, screens and skills gallery on GitHub →](https://github.com/wjgoarxiv/litopencode#readme)**
58
17
 
59
- Restart OpenCode, press **Tab**, and pick **lit-loop**. Permissions start at `safe`, where OpenCode asks you before acting. Logins and API keys stay with the provider you already set up in OpenCode.
18
+ ## Quick start
60
19
 
61
- To see the changes first, add `--dry-run`; it writes nothing. To skip the questions, add `--yes`; it takes the defaults and keeps choices you saved before.
20
+ You need Node.js with npm, and OpenCode with a model provider set up.
62
21
 
63
22
  ```sh
64
- npm exec --package @litfamily/litopencode@latest -- litopencode install --dry-run # preview changes
65
- npm exec --package @litfamily/litopencode@latest -- litopencode install --yes # use defaults; preserve saved choices
23
+ npm exec --package @litfamily/litopencode@latest -- litopencode install
66
24
  ```
67
25
 
68
- ## Your first task
69
-
70
- Open an empty folder in OpenCode, pick `lit-loop`, and send:
26
+ Add `--yes` to take the defaults, or `--dry-run` to preview without writing anything. Restart OpenCode, press **Tab**, pick **lit-loop**, open an empty folder and send:
71
27
 
72
28
  ```text
73
29
  lit Build a to-do list in one index.html with no dependencies; check adding and completing items, then leave results and next steps.
74
30
  ```
75
31
 
76
- You get an `index.html` to open and try. Next time, `/lit-recap` shows where you left off.
32
+ You get an `index.html` to try, and next time `/lit-recap` shows where you left off.
77
33
 
78
- ## The routes you'll use
34
+ ## What you get
79
35
 
80
- | Type | What happens |
36
+ | You enter | What happens |
81
37
  | --- | --- |
82
- | `lit` or `/lit` | Start a task with a clear scope and record what was checked. |
83
- | `lit-plan` → `/start-work` → `/review-work` | Plan, approve, execute, and review. The planner can't edit files or run shell commands. |
84
- | `handoff` or `/lit-handoff` | Carry the current result and next step into another session. |
85
- | `/lit-recap` | Read a short recap from local state. |
86
- | `/litresearch` | Research with source evidence. |
87
-
88
- Beyond these, the package installs skills for debugging, refactoring, code review, research, Word reports (`lit-docx`) and PowerPoint decks (`lit-pptx`) that pick and name a design direction before building, diagrams, scientific figures, interfaces, READMEs, and prose editing (`/lit-humanizer`). Each one, with a picture of what it produces, is in the [skills gallery on GitHub](https://github.com/wjgoarxiv/litopencode#skills-at-a-glance).
89
-
90
- ## What install changes
91
-
92
- Not much. Install adds the plugin to OpenCode's config and copies LitOpenCode's commands and skills into your OpenCode config folder. Your routes, which say which model handles each kind of work, are kept in `~/.config/opencode/litopencode.json`. Setting `XDG_CONFIG_HOME` moves that folder, and routes you've already customized are left as they are. Each project keeps its work record in `.litopencode/litgoal/`, and that record is what a later session reads to carry on.
38
+ | `lit` or `/lit` | Plan, build, verify and recap a task, with a note at each step. |
39
+ | `lit-plan` → `/start-work` → `/review-work` | Plan, approve, build and review. The planner cannot edit files. |
40
+ | `handoff` · `/lit-recap` | Leave a continuation file for the next session, and read back what is done and what is next. |
41
+ | `/litresearch` | Research in rounds with a source for each claim. |
42
+ | `lit` + report or slides | An editable Word report or PowerPoint deck with its Markdown source. |
43
+ | `/lit-humanizer` | Rewrite stiff English or Korean prose and keep the facts. |
93
44
 
94
- With the OpenAI provider, a fresh install uses GPT-6 Astra (`gpt-6-astra`) at `xhigh` for planning and review, and GPT-6 Luna (`gpt-6-luna`) at `max` for execution and research.
45
+ Each skill is pictured in the [skills gallery on GitHub](https://github.com/wjgoarxiv/litopencode/blob/main/docs/skills.md).
95
46
 
96
- ## Safety and updates
47
+ ## Updates and removal
97
48
 
98
- - The planner only plans. `lit-plan` has `edit`, `bash` and `task` denied, and it stays that way even if you choose the looser `balanced` or `yolo` mode.
99
- - LitOpenCode can update itself when it starts interactively and after an install or doctor run succeeds. To turn that off, pass `--no-auto-update` or set `LITOPENCODE_NO_AUTO_UPDATE=1`.
100
- - Skill learning has been removed. Learning records an earlier release left in a project's `.litopencode` folder are no longer used; keep or delete them as you like.
101
- - The Jev skill hint is optional and off by default. Turning it on sends eligible prompts to TypeSafe, so read the [Jev skill hint reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/reference.md#jev-skill-hint-optional) first. The GitHub page shows [what the toast and the doctor line look like](https://github.com/wjgoarxiv/litopencode#what-you-will-see).
102
- - If something seems off, `npm exec --package @litfamily/litopencode@latest -- litopencode doctor` checks the package and your configuration.
49
+ LitOpenCode checks for updates on interactive start and after a successful install or doctor run. To turn that off, pass `--no-auto-update` or set `LITOPENCODE_NO_AUTO_UPDATE=1`. `npm exec --package @litfamily/litopencode@latest -- litopencode doctor` checks the setup and changes nothing.
103
50
 
104
- ## Uninstall
105
-
106
- There's no uninstall command. Remove the `@litfamily/litopencode` entry from the `plugin` array in OpenCode's `opencode.jsonc` (or a custom root's `opencode.json`), then restart OpenCode. If you installed it globally:
51
+ To remove it, delete the `@litfamily/litopencode` entry from the `plugin` array in OpenCode's `opencode.jsonc` and restart OpenCode. If you installed it globally:
107
52
 
108
53
  ```sh
109
54
  npm uninstall -g @litfamily/litopencode
110
55
  ```
111
56
 
112
- The [removal reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/reference.md#remove) covers the installed files and records you may want to keep.
113
-
114
57
  ## More
115
58
 
116
- - [Full guide on GitHub](https://github.com/wjgoarxiv/litopencode#readme)
117
- - [Workflow reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/reference.md) · [한국어 상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/reference-Ko-KR.md)
118
- - [Migration notes](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/migration.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/CHANGELOG.md) · [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/privacy.md)
119
- - [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/LICENSE) · [ASCII font license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.18/docs/assets/readme/JetBrainsMono-OFL.txt)
59
+ - [Guide on GitHub](https://github.com/wjgoarxiv/litopencode/blob/main/docs/guide.md): install options, automatic handoff, troubleshooting and removal.
60
+ - [Workflow reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/reference.md)
61
+ - [Migration notes](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/migration.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/CHANGELOG.md) · [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/privacy.md)
62
+ - [LitFamily hub](https://github.com/wjgoarxiv/litfamily) · [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/LICENSE) · [ASCII font license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.20/docs/assets/readme/JetBrainsMono-OFL.txt)
63
+
64
+ <details>
65
+ <summary>Copy ASCII logo</summary>
66
+
67
+ ```text
68
+ ▄▄▄▄
69
+ ▗███▌ ▗██████▖
70
+ ▗▄▄▄▄▄ ▗▟████▌ ▝██████▘
71
+ ▐█████ ▗▟██████▌ ▝▀▜█▀▘
72
+ ▐█████ ▗▟███████▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
73
+ ▐█████ ▗▟█████████████████████████ ▐█▀
74
+ ▐█████ ████████████████████████████▀
75
+ ▐█████ ██▛▘ ▄ ▄▄▄▄▖▄▄▄▄▄▄▄▄▄▄▄▄▄▖
76
+ ▐█████ ▀ ▄██ ████▌█████████████▌
77
+ ▐█████ ▄████ ████▌█████████████▌
78
+ ▐█████ ▄█████▛ litopencode
79
+ ▐█████ ▗▟█████▀▘ ▄▄▄▄▄ ▗▖
80
+ ▐█████ ▐█████▀ █████ ▐▛▀
81
+ ▐█████ ▐███▀ █████
82
+ ▐█████ ▐█▀ █████
83
+ ▐█████ ▝ █████
84
+ ▐█████▄▄▄▄▄▄▄▖ █████
85
+ ▐███████████▛ █████
86
+ ▐██████████▀ █████
87
+
88
+ ```
89
+
90
+ </details>
@@ -62,7 +62,9 @@ const uiHardenCue = /\b(?:harden|stress.test|hold up under|robust to)\b|튼튼
62
62
  const uiReadOnlyCue = /\b(?:read.only|don't (?:touch|change|fix)|just (?:check|tell))\b|수정은 하지|건드리지 말|고치지는 말|목록만/u;
63
63
  const uiBuildCue = /\b(?:redesign|new layout|brand.new|add|implement|build|create|make|wire up)\b|새로|추가|구현|다시 짜|재구성/u;
64
64
  const diagramCreationIntent = /^\s*(?:lit[,:]?\s+)?(?:please\s+)?(?:create|draw|make|design|render|build)\b[^.!?\n]*\b(?:diagrams?|flowcharts?|architecture maps?)\b|^\s*(?:lit\s+)?[^.!?\n]*(?:다이어그램|흐름도|구성도|구조도)[^.!?\n]*(?:그려|만들어|제작해)/mu;
65
- const officeAuthoringVerb = /(?<![a-z])(?:make|create|write|draft|prepare|produce|generate|convert|edit|build)(?![a-z])|만들|작성|써줘|제작|변환|편집/u;
65
+ // "turn X into a deck/report" also authors an Office file. Within the clause, a dot followed by a
66
+ // non-space (notes.md, q3.v2.csv) is part of a file name and does not end the sentence.
67
+ const officeAuthoringVerb = /(?<![a-z])(?:make|create|write|draft|prepare|produce|generate|convert|edit|build)(?![a-z])|(?<![a-z])turn(?=(?:[^.!?\n]|\.(?=\S))*?(?<![a-z])into(?![a-z])(?:[^.!?\n]|\.(?=\S))*?(?<![a-z])(?:slides?|decks?|presentations?|pptx?|reports?|documents?|docx|word)(?![a-z]))|만들|작성|써줘|제작|변환|편집/u;
66
68
  const reportNoun = /(?<![a-z])(?:reports?|documents?|docx|word)(?![a-z])|보고서|리포트|기획서|제안서|문서|워드/u;
67
69
  const slidesNoun = /(?<![a-z])(?:slides?|decks?|presentations?|pptx?)(?![a-z])|발표자료|발표|슬라이드|피피티|덱/u;
68
70
  // lit-typographic-motion (MO-C-18..24). The trigger is a creation verb plus a compound motion-video
package/dist/cli/args.js CHANGED
@@ -66,7 +66,10 @@ export function parseArgs(argv) {
66
66
  throw new Error(`${arg} requires a value`);
67
67
  return cleaned;
68
68
  };
69
- const firstOptionIndex = command === "fetch-public" ? 2 : 1;
69
+ const tuiAction = command === "tui" ? argv[1] : undefined;
70
+ if (command === "tui" && tuiAction !== "remove")
71
+ throw new Error("tui requires an action: remove");
72
+ const firstOptionIndex = command === "fetch-public" || command === "tui" ? 2 : 1;
70
73
  if (command === "fetch-public") {
71
74
  fetchPublicUrl = cleanOption(argv[1], "fetch-public URL");
72
75
  if (!fetchPublicUrl)
@@ -298,6 +301,7 @@ export function parseArgs(argv) {
298
301
  modelSelection: resolveModelSelection(provider, model, effort, subagentModel, subagentEffort),
299
302
  modelPrompt: yes ? "never" : modelPrompt,
300
303
  outputStylePrompt: yes ? "never" : "auto",
304
+ ...(tuiAction === undefined ? {} : { tuiAction }),
301
305
  loop: {
302
306
  projectRoot,
303
307
  json: loopJson,
@@ -255,7 +255,7 @@ async function captureSnapshot(configRoot, homeDir) {
255
255
  const snapshotPath = path.join(stateRoot(homeDir), `.auto-update-backup-${randomUUID()}`);
256
256
  await fs.mkdir(snapshotPath, { recursive: true, mode: 0o700 });
257
257
  const entries = [];
258
- for (const relativePath of ["opencode.json", "opencode.jsonc", "litopencode.json", "command", "skills"]) {
258
+ for (const relativePath of ["opencode.json", "opencode.jsonc", "tui.json", "tui.jsonc", "litopencode.json", "command", "skills"]) {
259
259
  const source = path.join(configRoot, relativePath);
260
260
  const kind = await lstatKind(source);
261
261
  if (kind === undefined) {
@@ -5,6 +5,7 @@ import { defaultGlobalConfigFile, loadConfig, mergeConfigs, readLitOpenCodeConfi
5
5
  import { diagnoseModelRoutes, effectiveAuthoredAgentRoutes, projectModelRoutesForDiagnostics } from "../model-route-policy.js";
6
6
  import { litOpenCodeConfigFileForOpenCodeRoot } from "../state.js";
7
7
  import { isRecord, readJsonObjectIfPresent, readPackageMetadata } from "./json.js";
8
+ import { parseJsonc } from "./jsonc.js";
8
9
  import { inspectCommandAliases } from "./command-aliases.js";
9
10
  import { inspectNativeSkills } from "./native-skills.js";
10
11
  import { openCodeHostCapabilities } from "./host-capabilities.js";
@@ -13,6 +14,7 @@ import { autoHandoffEnvEnabled, autoHandoffEnvPercent, createAutoHandoffSettings
13
14
  import { inspectLspCapability } from "./lsp-capability.js";
14
15
  import { probeMotionRuntime } from "./motion-runtime.js";
15
16
  import { isLitOpenCodeEntry } from "./plugin-mutation.js";
17
+ import { inspectTuiPlugin } from "./tui-config.js";
16
18
  import { jevSkillHintDoctorLine } from "../jev-skill-hint.js";
17
19
  import { autoUpdateInstallLockPath, autoUpdateJournalPath, autoUpdateReceiptPath, isAutoUpdateDisabled, readAutoUpdateReceipt } from "./auto-update.js";
18
20
  function pluginSpec(metadata) {
@@ -25,9 +27,6 @@ function pluginEntryId(value) {
25
27
  return value[0];
26
28
  return "";
27
29
  }
28
- function stripJsonComments(value) {
29
- return value.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
30
- }
31
30
  async function readOpenCodeConfig(root) {
32
31
  const resolvedRoot = path.resolve(root);
33
32
  const candidates = [path.join(resolvedRoot, "opencode.json"), path.join(resolvedRoot, "opencode.jsonc")];
@@ -42,7 +41,7 @@ async function readOpenCodeConfig(root) {
42
41
  throw error;
43
42
  }
44
43
  try {
45
- const parsed = JSON.parse(candidate.endsWith(".jsonc") ? stripJsonComments(raw) : raw);
44
+ const parsed = parseJsonc(raw);
46
45
  return { path: candidate, config: isRecord(parsed) ? parsed : null, ...(isRecord(parsed) ? {} : { error: "expected object" }) };
47
46
  }
48
47
  catch (error) {
@@ -114,6 +113,8 @@ export async function doctor(root) {
114
113
  const installPlugin = await inspectPlugin(root, metadata);
115
114
  const commandAliases = await inspectCommandAliases(root);
116
115
  const nativeSkills = await inspectNativeSkills(root, metadata);
116
+ // The TUI panel is optional, so a missing entry is reported here without failing install.ok.
117
+ const tui = await inspectTuiPlugin(root, installPlugin.present && installPlugin.configured !== null ? installPlugin.configured : installPlugin.expected);
117
118
  const hostConfig = await readOpenCodeConfig(root);
118
119
  const motion = probeMotionRuntime(metadata.packageRoot);
119
120
  const autoHandoff = await createAutoHandoffSettings({
@@ -193,7 +194,8 @@ export async function doctor(root) {
193
194
  ok: installPlugin.ok && commandAliases.ok && nativeSkills.ok,
194
195
  plugin: installPlugin,
195
196
  commandAliases,
196
- nativeSkills
197
+ nativeSkills,
198
+ tui
197
199
  }
198
200
  }, null, 2)
199
201
  };
@@ -1,8 +1,6 @@
1
1
  import { isRecord } from "./json.js";
2
- import { writeTextAtomically } from "./install-config.js";
2
+ import { writeOpenCodeConfig } from "./install-config.js";
3
3
  import { hostLimitModelIds } from "./model-catalog.js";
4
- import fs from "node:fs/promises";
5
- import path from "node:path";
6
4
  export const contextCeilingTokens = 372000;
7
5
  export const compactionTriggerTokens = 334800;
8
6
  export const compactionReserveTokens = contextCeilingTokens - compactionTriggerTokens;
@@ -91,6 +89,5 @@ export async function ensureHostLimits(configPath, config, reconfigure) {
91
89
  const next = applyHostLimits(config);
92
90
  if (JSON.stringify(config) === JSON.stringify(next))
93
91
  return;
94
- await fs.mkdir(path.dirname(configPath), { recursive: true });
95
- await writeTextAtomically(configPath, JSON.stringify(next, null, 2) + "\n");
92
+ await writeOpenCodeConfig(configPath, next);
96
93
  }
@@ -3,6 +3,10 @@ type AtomicWriteOverrides = {
3
3
  readonly rename?: (from: string, to: string) => Promise<void>;
4
4
  };
5
5
  export declare function writeTextAtomically(filePath: string, text: string, overrides?: AtomicWriteOverrides): Promise<void>;
6
+ export declare class OpenCodeConfigCommentError extends Error {
7
+ }
8
+ export declare function renderOpenCodeConfig(filePath: string, currentText: string | null, next: Record<string, unknown>): string;
9
+ export declare function writeOpenCodeConfig(filePath: string, next: Record<string, unknown>): Promise<void>;
6
10
  export declare function readInstallModelClassification(root: string): Promise<ModelConfigClassification>;
7
11
  export declare function readInstallModelSelection(root: string): Promise<InstallModelSelection | undefined>;
8
12
  export declare function assertExistingModelRoutesSafe(root: string): Promise<void>;
@@ -5,7 +5,8 @@ import { litOpenCodeAgents, registerLitOpenCodeAgents } from "../agents.js";
5
5
  import { defaultGlobalConfigFile, defaultGlobalConfigJson, mergeConfigs, readLitOpenCodeConfigFile } from "../config.js";
6
6
  import { diagnoseModelRoutes, effectiveAuthoredAgentRoutes, UnsafeModelRouteError } from "../model-route-policy.js";
7
7
  import { litOpenCodeConfigFileForOpenCodeRoot } from "../state.js";
8
- import { isRecord, readJsonObjectIfPresent } from "./json.js";
8
+ import { isRecord, readJsonObjectIfPresent, readTextIfPresent } from "./json.js";
9
+ import { editJsoncObject, formatJsoncMember, hasJsoncComments, lineEnding } from "./jsonc.js";
9
10
  import { installModelEfforts, isModelProvider, modelMenuRows, stripFastSuffix } from "./model-catalog.js";
10
11
  import { classifyModelConfig, selectedLitConfig } from "./model-policy.js";
11
12
  export async function writeTextAtomically(filePath, text, overrides = {}) {
@@ -19,6 +20,31 @@ export async function writeTextAtomically(filePath, text, overrides = {}) {
19
20
  throw error;
20
21
  }
21
22
  }
23
+ export class OpenCodeConfigCommentError extends Error {
24
+ }
25
+ // A commented OpenCode config is edited in place; when an edit would delete a comment, the user is
26
+ // shown the exact entry to write instead.
27
+ export function renderOpenCodeConfig(filePath, currentText, next) {
28
+ if (currentText === null)
29
+ return JSON.stringify(next, null, 2) + "\n";
30
+ if (!hasJsoncComments(currentText)) {
31
+ const eol = lineEnding(currentText);
32
+ return (currentText.startsWith("\uFEFF") ? "\uFEFF" : "") + (JSON.stringify(next, null, 2) + "\n").replace(/\n/gu, eol);
33
+ }
34
+ const edit = editJsoncObject(currentText, next);
35
+ if (edit.ok)
36
+ return edit.text;
37
+ const action = edit.key in next
38
+ ? `set this entry yourself:\n${formatJsoncMember(edit.key, next[edit.key])}`
39
+ : `remove the "${edit.key}" entry yourself`;
40
+ throw new OpenCodeConfigCommentError(`Refusing to rewrite ${filePath}: changing its "${edit.key}" entry would delete a comment in it. ` +
41
+ `Nothing was written. To continue, ${action}\nthen run the command again.`);
42
+ }
43
+ export async function writeOpenCodeConfig(filePath, next) {
44
+ const text = renderOpenCodeConfig(filePath, await readTextIfPresent(filePath), next);
45
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
46
+ await writeTextAtomically(filePath, text);
47
+ }
22
48
  async function readValidatedConfig(root) {
23
49
  const filePath = litOpenCodeConfigFileForOpenCodeRoot(root);
24
50
  if ((await readLitOpenCodeConfigFile(filePath)) === undefined)
@@ -125,7 +125,8 @@ function actionLines(report) {
125
125
  : "OpenCode context and compaction limits already ready"
126
126
  : "OpenCode context and compaction limits preserved; select LUNA to reconfigure",
127
127
  report.commandAliases.changed ? "command aliases written" : "command aliases preserved",
128
- report.nativeSkills.changed ? "native skills written" : "native skills preserved"
128
+ report.nativeSkills.changed ? "native skills written" : "native skills preserved",
129
+ report.tui.changed ? "TUI mission panel registered" : "TUI mission panel already ready"
129
130
  ];
130
131
  }
131
132
  function progressLine(label) {
@@ -166,6 +167,7 @@ export function renderInstallReport(report) {
166
167
  line("", actions[2]),
167
168
  line("", actions[3]),
168
169
  line("", actions[4]),
170
+ line("", actions[5]),
169
171
  ...section("Routes"),
170
172
  line("Model", modelRoute(report)),
171
173
  line("Effort", report.litopencodeConfig.model.effort ?? "preserve existing"),
@@ -189,6 +191,7 @@ export function renderInstallReport(report) {
189
191
  line("Native skills", report.nativeSkills.write.length + " managed / " + report.nativeSkills.preserve.length + " preserved"),
190
192
  ...nativeSkillsLinkLines(report),
191
193
  line("Plugin entries", report.plugin.currentCount + " -> " + report.plugin.resultCount),
194
+ line("TUI config", report.tui.path),
192
195
  ...section("Progress"),
193
196
  progressLine("Resolve package"),
194
197
  progressLine("Read OpenCode config"),