claudeos-core 1.2.3 → 1.3.0

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.

Potentially problematic release.


This version of claudeos-core might be problematic. Click here for more details.

Files changed (44) hide show
  1. package/CHANGELOG.md +66 -1
  2. package/README.de.md +41 -6
  3. package/README.es.md +42 -6
  4. package/README.fr.md +42 -6
  5. package/README.hi.md +42 -6
  6. package/README.ja.md +43 -6
  7. package/README.ko.md +42 -6
  8. package/README.md +53 -11
  9. package/README.ru.md +42 -6
  10. package/README.vi.md +42 -6
  11. package/README.zh-CN.md +42 -6
  12. package/bin/cli.js +171 -36
  13. package/bootstrap.sh +72 -23
  14. package/content-validator/index.js +9 -4
  15. package/health-checker/index.js +4 -3
  16. package/lib/safe-fs.js +110 -0
  17. package/manifest-generator/index.js +16 -20
  18. package/package.json +4 -2
  19. package/pass-json-validator/index.js +3 -5
  20. package/pass-prompts/templates/java-spring/pass1.md +3 -0
  21. package/pass-prompts/templates/java-spring/pass2.md +3 -3
  22. package/pass-prompts/templates/java-spring/pass3.md +17 -0
  23. package/pass-prompts/templates/kotlin-spring/pass1.md +3 -0
  24. package/pass-prompts/templates/kotlin-spring/pass2.md +5 -5
  25. package/pass-prompts/templates/kotlin-spring/pass3.md +17 -0
  26. package/pass-prompts/templates/node-express/pass1.md +3 -0
  27. package/pass-prompts/templates/node-express/pass2.md +4 -1
  28. package/pass-prompts/templates/node-express/pass3.md +20 -1
  29. package/pass-prompts/templates/node-nextjs/pass1.md +13 -3
  30. package/pass-prompts/templates/node-nextjs/pass2.md +6 -4
  31. package/pass-prompts/templates/node-nextjs/pass3.md +20 -1
  32. package/pass-prompts/templates/python-django/pass1.md +3 -1
  33. package/pass-prompts/templates/python-django/pass2.md +4 -4
  34. package/pass-prompts/templates/python-django/pass3.md +18 -0
  35. package/pass-prompts/templates/python-fastapi/pass1.md +3 -0
  36. package/pass-prompts/templates/python-fastapi/pass2.md +4 -4
  37. package/pass-prompts/templates/python-fastapi/pass3.md +18 -0
  38. package/plan-installer/domain-grouper.js +74 -0
  39. package/plan-installer/index.js +43 -1303
  40. package/plan-installer/prompt-generator.js +99 -0
  41. package/plan-installer/stack-detector.js +326 -0
  42. package/plan-installer/structure-scanner.js +783 -0
  43. package/plan-validator/index.js +84 -20
  44. package/sync-checker/index.js +7 -3
package/README.md CHANGED
@@ -105,9 +105,50 @@ npx claudeos-core init
105
105
 
106
106
  # Option D: git clone (for development/contribution)
107
107
  git clone https://github.com/claudeos-core/claudeos-core.git claudeos-core-tools
108
+
109
+ # Cross-platform (PowerShell, CMD, Bash, Zsh — any terminal)
110
+ node claudeos-core-tools/bin/cli.js init
111
+
112
+ # Linux/macOS (Bash only)
108
113
  bash claudeos-core-tools/bootstrap.sh
109
114
  ```
110
115
 
116
+ ### Output Language (10 languages)
117
+
118
+ When you run `init` without `--lang`, an interactive selector appears — use arrow keys to choose:
119
+
120
+ ```
121
+ ╔══════════════════════════════════════════════════╗
122
+ ║ Select generated document language (required) ║
123
+ ╚══════════════════════════════════════════════════╝
124
+
125
+ Generated files (CLAUDE.md, Standards, Rules,
126
+ Skills, Guides) will be written in English.
127
+
128
+ ❯ 1. en — English
129
+ 2. ko — 한국어 (Korean)
130
+ 3. zh-CN — 简体中文 (Chinese Simplified)
131
+ 4. ja — 日本語 (Japanese)
132
+ 5. es — Español (Spanish)
133
+ 6. vi — Tiếng Việt (Vietnamese)
134
+ 7. hi — हिन्दी (Hindi)
135
+ 8. ru — Русский (Russian)
136
+ 9. fr — Français (French)
137
+ 10. de — Deutsch (German)
138
+
139
+ ↑↓ Move ↵ Select
140
+ ```
141
+
142
+ The description changes to the selected language as you navigate. To skip the selector, pass `--lang` directly:
143
+
144
+ ```bash
145
+ npx claudeos-core init --lang ko # Korean
146
+ npx claudeos-core init --lang ja # Japanese
147
+ npx claudeos-core init --lang en # English (default)
148
+ ```
149
+
150
+ > **Note:** This sets the language for generated documentation files only. Code analysis (Pass 1–2) always runs in English; generated output (Pass 3) is written in your chosen language. Code examples inside the generated files remain in their original programming language syntax.
151
+
111
152
  That's it. After 5–18 minutes, all documentation is generated and ready to use.
112
153
 
113
154
  ### Manual Step-by-Step Installation
@@ -168,18 +209,18 @@ cat claudeos-core/generated/domain-groups.json | node -e "
168
209
  g.groups.forEach((g,i) => console.log('Group '+(i+1)+': ['+g.domains.join(', ')+'] ('+g.type+', ~'+g.estimatedFiles+' files)'));
169
210
  "
170
211
 
171
- # Run Pass 1 for each group (replace N with group number)
212
+ # Run Pass 1 for each group (replace domains and group number)
172
213
  # For group 1:
173
- cat claudeos-core/generated/pass1-backend-prompt.md \
174
- | sed "s/{{DOMAIN_GROUP}}/user, order, product/g" \
175
- | sed "s/{{PASS_NUM}}/1/g" \
176
- | claude -p --dangerously-skip-permissions
214
+ cp claudeos-core/generated/pass1-backend-prompt.md /tmp/_pass1.md
215
+ DOMAIN_LIST="user, order, product" PASS_NUM=1 \
216
+ perl -pi -e 's/\{\{DOMAIN_GROUP\}\}/$ENV{DOMAIN_LIST}/g; s/\{\{PASS_NUM\}\}/$ENV{PASS_NUM}/g' /tmp/_pass1.md
217
+ cat /tmp/_pass1.md | claude -p --dangerously-skip-permissions
177
218
 
178
219
  # For group 2 (if exists):
179
- cat claudeos-core/generated/pass1-backend-prompt.md \
180
- | sed "s/{{DOMAIN_GROUP}}/payment, system, delivery/g" \
181
- | sed "s/{{PASS_NUM}}/2/g" \
182
- | claude -p --dangerously-skip-permissions
220
+ cp claudeos-core/generated/pass1-backend-prompt.md /tmp/_pass1.md
221
+ DOMAIN_LIST="payment, system, delivery" PASS_NUM=2 \
222
+ perl -pi -e 's/\{\{DOMAIN_GROUP\}\}/$ENV{DOMAIN_LIST}/g; s/\{\{PASS_NUM\}\}/$ENV{PASS_NUM}/g' /tmp/_pass1.md
223
+ cat /tmp/_pass1.md | claude -p --dangerously-skip-permissions
183
224
 
184
225
  # For frontend groups, use pass1-frontend-prompt.md instead
185
226
  ```
@@ -224,7 +265,7 @@ node claudeos-core-tools/pass-json-validator/index.js # Pass JSON format check
224
265
 
225
266
  ```bash
226
267
  # Count generated files
227
- find .claude claudeos-core -type f | grep -v node_modules | wc -l
268
+ find .claude claudeos-core -type f | grep -v node_modules | grep -v '/generated/' | wc -l
228
269
 
229
270
  # Check CLAUDE.md
230
271
  head -30 CLAUDE.md
@@ -452,6 +493,7 @@ npx claudeos-core restore
452
493
  | **Project-specific output** | ✅ Every file reflects your patterns | ❌ Generic commands | Partially | ❌ One-size-fits-all |
453
494
  | **Multi-stack** | ✅ Auto-detected + separate analysis | ❌ Stack-agnostic | Manual | Varies |
454
495
  | **Kotlin + CQRS/BFF** | ✅ Multi-module, Command/Query split | ❌ | ❌ | ❌ |
496
+ | **Multi-language** | ✅ 10 languages, interactive selector | ❌ | ❌ | ❌ |
455
497
  | **Self-verifying** | ✅ 5 validation tools | ❌ | ❌ | ❌ |
456
498
  | **Backup / Restore** | ✅ Master Plan system | ❌ | ❌ | ❌ |
457
499
  | **Setup time** | ~5–18min (automated) | ~5min (manual config) | Hours–Days | ~5min |
@@ -560,7 +602,7 @@ Contributions are welcome! Areas where help is most needed:
560
602
 
561
603
  - **New stack templates** — Ruby/Rails, Go/Gin, PHP/Laravel, Rust/Axum
562
604
  - **Monorepo deep support** — Separate sub-project roots, workspace detection
563
- - **Test coverage** — Unit tests for all verification tools
605
+ - **Test coverage** — Expanding test suite (currently 57 tests covering stack detection, domain grouping, plan validation, and structure scanning)
564
606
 
565
607
  ---
566
608
 
package/README.ru.md CHANGED
@@ -107,9 +107,44 @@ npx claudeos-core init
107
107
 
108
108
  # Вариант D: git clone (для разработки/контрибуции)
109
109
  git clone https://github.com/claudeos-core/claudeos-core.git claudeos-core-tools
110
+
111
+ # Кроссплатформенный (PowerShell, CMD, Bash, Zsh — любой терминал)
112
+ node claudeos-core-tools/bin/cli.js init
113
+
114
+ # Только Linux/macOS (только Bash)
110
115
  bash claudeos-core-tools/bootstrap.sh
111
116
  ```
112
117
 
118
+ ### Язык вывода (10 языков)
119
+
120
+ При запуске `init` без `--lang` появляется интерактивный селектор со стрелками:
121
+
122
+ ```
123
+ ╔══════════════════════════════════════════════════╗
124
+ ║ Select generated document language (required) ║
125
+ ╚══════════════════════════════════════════════════╝
126
+
127
+ Сгенерированные файлы (CLAUDE.md, Standards, Rules,
128
+ Skills, Guides) будут написаны на русском языке.
129
+
130
+ 1. en — English
131
+ ...
132
+ ❯ 8. ru — Русский (Russian)
133
+ ...
134
+
135
+ ↑↓ Move ↵ Select
136
+ ```
137
+
138
+ При навигации описание переключается на соответствующий язык. Чтобы пропустить селектор:
139
+
140
+ ```bash
141
+ npx claudeos-core init --lang ru # Русский
142
+ npx claudeos-core init --lang en # English
143
+ npx claudeos-core init --lang ko # 한국어
144
+ ```
145
+
146
+ > **Примечание:** Эта настройка изменяет только язык генерируемых файлов документации. Анализ кода (Pass 1–2) всегда выполняется на английском; только результат генерации (Pass 3) пишется на выбранном языке.
147
+
113
148
  Это всё. Через 5–18 минут вся документация сгенерирована и готова к использованию.
114
149
 
115
150
  ### Ручная пошаговая установка
@@ -171,12 +206,12 @@ cat claudeos-core/generated/domain-groups.json | node -e "
171
206
  "
172
207
 
173
208
  # Run Pass 1 for group 1:
174
- cat claudeos-core/generated/pass1-backend-prompt.md \
175
- | sed "s/{{DOMAIN_GROUP}}/user, order, product/g" \
176
- | sed "s/{{PASS_NUM}}/1/g" \
177
- | claude -p --dangerously-skip-permissions
209
+ cp claudeos-core/generated/pass1-backend-prompt.md /tmp/_pass1.md
210
+ DOMAIN_LIST="user, order, product" PASS_NUM=1 \
211
+ perl -pi -e 's/\{\{DOMAIN_GROUP\}\}/$ENV{DOMAIN_LIST}/g; s/\{\{PASS_NUM\}\}/$ENV{PASS_NUM}/g' /tmp/_pass1.md
212
+ cat /tmp/_pass1.md | claude -p --dangerously-skip-permissions
178
213
 
179
- # For frontend groups, use pass1-frontend-prompt.md instead
214
+ # Для фронтенд-групп используйте pass1-frontend-prompt.md
180
215
  ```
181
216
 
182
217
  **Проверка:** `ls claudeos-core/generated/pass1-*.json` должен показать по одному JSON на группу.
@@ -218,7 +253,7 @@ node claudeos-core-tools/pass-json-validator/index.js # JSON format
218
253
  #### Step 8: Проверка результатов
219
254
 
220
255
  ```bash
221
- find .claude claudeos-core -type f | grep -v node_modules | wc -l
256
+ find .claude claudeos-core -type f | grep -v node_modules | grep -v '/generated/' | wc -l
222
257
  head -30 CLAUDE.md
223
258
  ls .claude/rules/*/
224
259
  ```
@@ -421,6 +456,7 @@ npx claudeos-core restore
421
456
  | **Вывод под проект** | ✅ Каждый файл отражает ваши паттерны | ❌ Универсальные команды | Частично | ❌ Один размер для всех |
422
457
  | **Мульти-стек** | ✅ Автоопределение + раздельный анализ | ❌ Стек-агностик | Вручную | Неопределённо |
423
458
  | **Kotlin + CQRS/BFF** | ✅ Мультимодульный, разделение Command/Query | ❌ | ❌ | ❌ |
459
+ | **Многоязычность** | ✅ 10 языков, интерактивный выбор | ❌ | ❌ | ❌ |
424
460
  | **Самопроверка** | ✅ 5 инструментов валидации | ❌ | ❌ | ❌ |
425
461
  | **Бэкап / Восстановление** | ✅ Система Master Plan | ❌ | ❌ | ❌ |
426
462
  | **Время настройки** | ~5–18 мин (автоматически) | ~5 мин (ручная настройка) | Часы–Дни | ~5 мин |
package/README.vi.md CHANGED
@@ -107,9 +107,44 @@ npx claudeos-core init
107
107
 
108
108
  # Cách D: git clone (cho phát triển/đóng góp)
109
109
  git clone https://github.com/claudeos-core/claudeos-core.git claudeos-core-tools
110
+
111
+ # Đa nền tảng (PowerShell, CMD, Bash, Zsh — mọi terminal)
112
+ node claudeos-core-tools/bin/cli.js init
113
+
114
+ # Chỉ Linux/macOS (chỉ Bash)
110
115
  bash claudeos-core-tools/bootstrap.sh
111
116
  ```
112
117
 
118
+ ### Ngôn ngữ đầu ra (10 ngôn ngữ)
119
+
120
+ Khi chạy `init` không có `--lang`, bộ chọn tương tác bằng phím mũi tên sẽ xuất hiện:
121
+
122
+ ```
123
+ ╔══════════════════════════════════════════════════╗
124
+ ║ Select generated document language (required) ║
125
+ ╚══════════════════════════════════════════════════╝
126
+
127
+ Các file được tạo (CLAUDE.md, Standards, Rules,
128
+ Skills, Guides) sẽ được viết bằng tiếng Việt.
129
+
130
+ 1. en — English
131
+ ...
132
+ ❯ 6. vi — Tiếng Việt (Vietnamese)
133
+ ...
134
+
135
+ ↑↓ Move ↵ Select
136
+ ```
137
+
138
+ Mô tả thay đổi sang ngôn ngữ tương ứng khi di chuyển. Bỏ qua bộ chọn:
139
+
140
+ ```bash
141
+ npx claudeos-core init --lang vi # Tiếng Việt
142
+ npx claudeos-core init --lang en # English
143
+ npx claudeos-core init --lang ko # 한국어
144
+ ```
145
+
146
+ > **Lưu ý:** Chỉ thay đổi ngôn ngữ của file tài liệu được tạo. Phân tích mã (Pass 1–2) luôn chạy bằng tiếng Anh; chỉ kết quả tạo (Pass 3) được viết bằng ngôn ngữ đã chọn.
147
+
113
148
  Chỉ vậy thôi. Sau 5–18 phút, tất cả tài liệu được tạo và sẵn sàng sử dụng.
114
149
 
115
150
  ### Cài Đặt Thủ Công Từng Bước
@@ -171,12 +206,12 @@ cat claudeos-core/generated/domain-groups.json | node -e "
171
206
  "
172
207
 
173
208
  # Run Pass 1 for group 1:
174
- cat claudeos-core/generated/pass1-backend-prompt.md \
175
- | sed "s/{{DOMAIN_GROUP}}/user, order, product/g" \
176
- | sed "s/{{PASS_NUM}}/1/g" \
177
- | claude -p --dangerously-skip-permissions
209
+ cp claudeos-core/generated/pass1-backend-prompt.md /tmp/_pass1.md
210
+ DOMAIN_LIST="user, order, product" PASS_NUM=1 \
211
+ perl -pi -e 's/\{\{DOMAIN_GROUP\}\}/$ENV{DOMAIN_LIST}/g; s/\{\{PASS_NUM\}\}/$ENV{PASS_NUM}/g' /tmp/_pass1.md
212
+ cat /tmp/_pass1.md | claude -p --dangerously-skip-permissions
178
213
 
179
- # For frontend groups, use pass1-frontend-prompt.md instead
214
+ # Đối với nhóm frontend, sử dụng pass1-frontend-prompt.md
180
215
  ```
181
216
 
182
217
  **Xác minh:** `ls claudeos-core/generated/pass1-*.json` phải hiển thị một JSON cho mỗi nhóm.
@@ -218,7 +253,7 @@ node claudeos-core-tools/pass-json-validator/index.js # JSON format
218
253
  #### Step 8: Xác minh kết quả
219
254
 
220
255
  ```bash
221
- find .claude claudeos-core -type f | grep -v node_modules | wc -l
256
+ find .claude claudeos-core -type f | grep -v node_modules | grep -v '/generated/' | wc -l
222
257
  head -30 CLAUDE.md
223
258
  ls .claude/rules/*/
224
259
  ```
@@ -421,6 +456,7 @@ npx claudeos-core restore
421
456
  | **Đầu ra riêng dự án** | ✅ Mọi file phản ánh pattern của bạn | ❌ Lệnh chung | Một phần | ❌ Một cỡ cho tất cả |
422
457
  | **Multi-stack** | ✅ Tự phát hiện + phân tích riêng | ❌ Không phân biệt stack | Thủ công | Không chắc |
423
458
  | **Kotlin + CQRS/BFF** | ✅ Multi-module, Command/Query split | ❌ | ❌ | ❌ |
459
+ | **Đa ngôn ngữ** | ✅ 10 ngôn ngữ, bộ chọn tương tác | ❌ | ❌ | ❌ |
424
460
  | **Tự xác thực** | ✅ 5 công cụ validation | ❌ | ❌ | ❌ |
425
461
  | **Backup / Khôi phục** | ✅ Hệ thống Master Plan | ❌ | ❌ | ❌ |
426
462
  | **Thời gian cài đặt** | ~5–18 phút (tự động) | ~5 phút (cấu hình thủ công) | Hàng giờ–ngày | ~5 phút |
package/README.zh-CN.md CHANGED
@@ -107,9 +107,44 @@ npx claudeos-core init
107
107
 
108
108
  # 方式 D:git clone(用于开发/贡献)
109
109
  git clone https://github.com/claudeos-core/claudeos-core.git claudeos-core-tools
110
+
111
+ # 跨平台(PowerShell、CMD、Bash、Zsh — 任何终端)
112
+ node claudeos-core-tools/bin/cli.js init
113
+
114
+ # 仅 Linux/macOS(仅 Bash)
110
115
  bash claudeos-core-tools/bootstrap.sh
111
116
  ```
112
117
 
118
+ ### 输出语言(支持 10 种语言)
119
+
120
+ 不带 `--lang` 运行 `init` 时,会显示箭头键交互选择器:
121
+
122
+ ```
123
+ ╔══════════════════════════════════════════════════╗
124
+ ║ Select generated document language (required) ║
125
+ ╚══════════════════════════════════════════════════╝
126
+
127
+ 生成的文件(CLAUDE.md、Standards、Rules、
128
+ Skills、Guides)将以简体中文编写。
129
+
130
+ 1. en — English
131
+ 2. ko — 한국어 (Korean)
132
+ ❯ 3. zh-CN — 简体中文 (Chinese Simplified)
133
+ ...
134
+
135
+ ↑↓ Move ↵ Select
136
+ ```
137
+
138
+ 移动箭头时说明会切换为对应语言。跳过选择器直接指定:
139
+
140
+ ```bash
141
+ npx claudeos-core init --lang zh-CN # 简体中文
142
+ npx claudeos-core init --lang en # English
143
+ npx claudeos-core init --lang ko # 한국어
144
+ ```
145
+
146
+ > **注意:** 此设置仅更改生成文档的语言。代码分析(Pass 1–2)始终以英文运行,仅生成结果(Pass 3)以所选语言编写。代码示例保持原始编程语言语法。
147
+
113
148
  就这样。5–18 分钟后,所有文档生成完毕,即可使用。
114
149
 
115
150
  ### 手动逐步安装
@@ -171,12 +206,12 @@ cat claudeos-core/generated/domain-groups.json | node -e "
171
206
  "
172
207
 
173
208
  # Run Pass 1 for group 1:
174
- cat claudeos-core/generated/pass1-backend-prompt.md \
175
- | sed "s/{{DOMAIN_GROUP}}/user, order, product/g" \
176
- | sed "s/{{PASS_NUM}}/1/g" \
177
- | claude -p --dangerously-skip-permissions
209
+ cp claudeos-core/generated/pass1-backend-prompt.md /tmp/_pass1.md
210
+ DOMAIN_LIST="user, order, product" PASS_NUM=1 \
211
+ perl -pi -e 's/\{\{DOMAIN_GROUP\}\}/$ENV{DOMAIN_LIST}/g; s/\{\{PASS_NUM\}\}/$ENV{PASS_NUM}/g' /tmp/_pass1.md
212
+ cat /tmp/_pass1.md | claude -p --dangerously-skip-permissions
178
213
 
179
- # For frontend groups, use pass1-frontend-prompt.md instead
214
+ # 前端分组请使用 pass1-frontend-prompt.md
180
215
  ```
181
216
 
182
217
  **验证:** `ls claudeos-core/generated/pass1-*.json` 应显示每组一个 JSON。
@@ -218,7 +253,7 @@ node claudeos-core-tools/pass-json-validator/index.js # JSON format
218
253
  #### Step 8:验证结果
219
254
 
220
255
  ```bash
221
- find .claude claudeos-core -type f | grep -v node_modules | wc -l
256
+ find .claude claudeos-core -type f | grep -v node_modules | grep -v '/generated/' | wc -l
222
257
  head -30 CLAUDE.md
223
258
  ls .claude/rules/*/
224
259
  ```
@@ -421,6 +456,7 @@ npx claudeos-core restore
421
456
  | **项目定制输出** | ✅ 每个文件反映你的模式 | ❌ 通用命令 | 部分 | ❌ 一刀切 |
422
457
  | **多栈支持** | ✅ 自动检测 + 分别分析 | ❌ 栈无关 | 手动 | 不一定 |
423
458
  | **Kotlin + CQRS/BFF** | ✅ 多模块, Command/Query 分离 | ❌ | ❌ | ❌ |
459
+ | **多语言** | ✅ 10 种语言,交互式选择 | ❌ | ❌ | ❌ |
424
460
  | **自验证** | ✅ 5 个验证工具 | ❌ | ❌ | ❌ |
425
461
  | **备份 / 恢复** | ✅ Master Plan 系统 | ❌ | ❌ | ❌ |
426
462
  | **配置时间** | ~5–18分钟(自动) | ~5分钟(手动配置) | 数小时–数天 | ~5分钟 |
package/bin/cli.js CHANGED
@@ -49,42 +49,154 @@ function isValidLang(lang) {
49
49
  return LANG_CODES.includes(lang);
50
50
  }
51
51
 
52
- // Interactive language selection (stdin prompt)
52
+ // Interactive language selection (arrow key selector)
53
53
  function selectLangInteractive() {
54
54
  return new Promise((resolve) => {
55
- const readline = require("readline");
56
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
55
+ // Fallback to number input if stdin is not a TTY (e.g., piped input)
56
+ if (!process.stdin.isTTY) {
57
+ const readline = require("readline");
58
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
59
+ log("");
60
+ log("╔══════════════════════════════════════════════════╗");
61
+ log("║ Select generated document language (required) ║");
62
+ log("╚══════════════════════════════════════════════════╝");
63
+ log("");
64
+ log(" Generated files (CLAUDE.md, Standards, Rules,");
65
+ log(" Skills, Guides) will be written in this language.");
66
+ log("");
67
+ LANG_CODES.forEach((code, i) => {
68
+ log(` ${String(i + 1).padStart(2)}. ${code.padEnd(6)} — ${SUPPORTED_LANGS[code]}`);
69
+ });
70
+ log("");
71
+ rl.question(` Enter number (1-${LANG_CODES.length}) or language code: `, (answer) => {
72
+ rl.close();
73
+ const trimmed = answer.trim();
74
+ const num = parseInt(trimmed);
75
+ if (num >= 1 && num <= LANG_CODES.length) { resolve(LANG_CODES[num - 1]); return; }
76
+ if (isValidLang(trimmed)) { resolve(trimmed); return; }
77
+ log(`\n ❌ Invalid selection: "${trimmed}"`);
78
+ process.exit(1);
79
+ });
80
+ return;
81
+ }
82
+
83
+ // Arrow key interactive selector
84
+ let selected = 0;
85
+ const total = LANG_CODES.length;
86
+
87
+ // Description text per language (shown when hovering)
88
+ const DESC = {
89
+ en: "Generated files (CLAUDE.md, Standards, Rules,\n Skills, Guides) will be written in English.",
90
+ ko: "생성되는 파일(CLAUDE.md, Standards, Rules,\n Skills, Guides)이 한국어로 작성됩니다.",
91
+ "zh-CN": "生成的文件(CLAUDE.md、Standards、Rules、\n Skills、Guides)将以简体中文编写。",
92
+ ja: "生成されるファイル(CLAUDE.md、Standards、Rules、\n Skills、Guides)は日本語で作成されます。",
93
+ es: "Los archivos generados (CLAUDE.md, Standards, Rules,\n Skills, Guides) se escribirán en español.",
94
+ vi: "Các file được tạo (CLAUDE.md, Standards, Rules,\n Skills, Guides) sẽ được viết bằng tiếng Việt.",
95
+ hi: "जनरेट की गई फ़ाइलें (CLAUDE.md, Standards, Rules,\n Skills, Guides) हिन्दी में लिखी जाएंगी।",
96
+ ru: "Сгенерированные файлы (CLAUDE.md, Standards, Rules,\n Skills, Guides) будут написаны на русском языке.",
97
+ fr: "Les fichiers générés (CLAUDE.md, Standards, Rules,\n Skills, Guides) seront rédigés en français.",
98
+ de: "Die generierten Dateien (CLAUDE.md, Standards, Rules,\n Skills, Guides) werden auf Deutsch verfasst.",
99
+ };
100
+
101
+ function render() {
102
+ const output = [];
103
+ // Description line (changes with selection)
104
+ const code = LANG_CODES[selected];
105
+ const descLines = (DESC[code] || DESC.en).split("\n");
106
+ descLines.forEach(l => output.push(` ${l}`));
107
+ output.push("");
108
+ // Language list
109
+ const BOLD_CYAN = "\x1b[1;36m";
110
+ const RESET = "\x1b[0m";
111
+ for (let i = 0; i < total; i++) {
112
+ const c = LANG_CODES[i];
113
+ const label = SUPPORTED_LANGS[c];
114
+ const num = String(i + 1).padStart(2);
115
+ if (i === selected) {
116
+ output.push(` ${BOLD_CYAN}❯ ${num}. ${c.padEnd(6)} — ${label}${RESET}`);
117
+ } else {
118
+ output.push(` ${num}. ${c.padEnd(6)} — ${label}`);
119
+ }
120
+ }
121
+ output.push("");
122
+ const DIM = "\x1b[2m";
123
+ output.push(` \x1b[1m↑↓\x1b[0m${DIM} Move ${RESET} \x1b[1mEnter\x1b[0m${DIM} Select ${RESET} \x1b[1mESC\x1b[0m${DIM} Cancel${RESET}`);
124
+ return output;
125
+ }
57
126
 
127
+ // Print header once
58
128
  log("");
59
129
  log("╔══════════════════════════════════════════════════╗");
60
- log("║ Select output language (required) ║");
130
+ log("║ Select generated document language (required) ║");
61
131
  log("╚══════════════════════════════════════════════════╝");
62
132
  log("");
63
- LANG_CODES.forEach((code, i) => {
64
- log(` ${String(i + 1).padStart(2)}. ${code.padEnd(6)} — ${SUPPORTED_LANGS[code]}`);
65
- });
66
- log("");
67
133
 
68
- rl.question(` Enter number (1-${LANG_CODES.length}) or language code: `, (answer) => {
69
- rl.close();
70
- const trimmed = answer.trim();
134
+ // Initial render
135
+ const lines = render();
136
+ const listHeight = lines.length;
137
+ process.stdout.write(lines.join("\n") + "\n");
71
138
 
72
- // Accept number
73
- const num = parseInt(trimmed);
74
- if (num >= 1 && num <= LANG_CODES.length) {
75
- resolve(LANG_CODES[num - 1]);
76
- return;
139
+ // Raw mode for keypress detection
140
+ process.stdin.setRawMode(true);
141
+ process.stdin.resume();
142
+
143
+ process.stdin.on("data", (key) => {
144
+ const k = key.toString();
145
+
146
+ // Ctrl+C
147
+ if (k === "\x03") {
148
+ process.stdin.setRawMode(false);
149
+ log("\n Cancelled.\n");
150
+ process.exit(0);
151
+ }
152
+
153
+ // ESC (single byte only — arrow keys send \x1b[ which is 3 bytes)
154
+ if (k === "\x1b" && key.length === 1) {
155
+ process.stdin.setRawMode(false);
156
+ log("\n Cancelled.\n");
157
+ process.exit(0);
77
158
  }
78
159
 
79
- // Accept language code
80
- if (isValidLang(trimmed)) {
81
- resolve(trimmed);
160
+ // Up arrow
161
+ if (k === "\x1b[A") {
162
+ selected = (selected - 1 + total) % total;
163
+ }
164
+ // Down arrow
165
+ else if (k === "\x1b[B") {
166
+ selected = (selected + 1) % total;
167
+ }
168
+ // Number keys 1-9 (direct jump)
169
+ else if (k >= "1" && k <= "9" && parseInt(k) <= total) {
170
+ selected = parseInt(k) - 1;
171
+ }
172
+ // 0 for 10
173
+ else if (k === "0" && total >= 10) {
174
+ selected = 9;
175
+ }
176
+ // Enter
177
+ else if (k === "\r" || k === "\n") {
178
+ process.stdin.setRawMode(false);
179
+ process.stdin.pause();
180
+ process.stdin.removeAllListeners("data");
181
+ // Clear the list and reprint final selection
182
+ process.stdout.write(`\x1b[${listHeight}A`);
183
+ for (let i = 0; i < listHeight; i++) {
184
+ process.stdout.write("\x1b[2K\n");
185
+ }
186
+ process.stdout.write(`\x1b[${listHeight}A`);
187
+ log(` ✅ ${LANG_CODES[selected]} — ${SUPPORTED_LANGS[LANG_CODES[selected]]}`);
188
+ log("");
189
+ resolve(LANG_CODES[selected]);
82
190
  return;
83
191
  }
192
+ else {
193
+ return; // Ignore other keys
194
+ }
84
195
 
85
- log(`\n ❌ Invalid selection: "${trimmed}"`);
86
- log(` Supported: ${LANG_CODES.join(", ")}\n`);
87
- process.exit(1);
196
+ // Redraw list (clear each line to prevent ghost text from different-length strings)
197
+ process.stdout.write(`\x1b[${listHeight}A`);
198
+ const updated = render();
199
+ process.stdout.write(updated.map(l => `\x1b[2K${l}`).join("\n") + "\n");
88
200
  });
89
201
  });
90
202
  }
@@ -147,7 +259,9 @@ function readFile(p) {
147
259
  }
148
260
 
149
261
  function injectProjectRoot(text) {
150
- return text.replace(/\{\{PROJECT_ROOT\}\}/g, PROJECT_ROOT);
262
+ // Normalize to forward slashes for prompts (Claude interprets backslashes as escapes)
263
+ const normalizedRoot = PROJECT_ROOT.replace(/\\/g, "/");
264
+ return text.replace(/\{\{PROJECT_ROOT\}\}/g, normalizedRoot);
151
265
  }
152
266
 
153
267
  // ─── Command: init ───────────────────────────────────────
@@ -269,6 +383,11 @@ async function cmdInit() {
269
383
  process.exit(1);
270
384
  }
271
385
  const totalGroups = domainGroups.totalGroups;
386
+ if (!totalGroups || typeof totalGroups !== "number" || totalGroups < 1) {
387
+ log(` ❌ domain-groups.json has invalid totalGroups: ${totalGroups}`);
388
+ log(" Re-run plan-installer or check claudeos-core/generated/");
389
+ process.exit(1);
390
+ }
272
391
 
273
392
  // Load pass1 prompts by type
274
393
  const pass1Prompts = {};
@@ -302,8 +421,14 @@ async function cmdInit() {
302
421
 
303
422
  const pass1Json = path.join(GENERATED_DIR, `pass1-${i}.json`);
304
423
  if (fileExists(pass1Json)) {
305
- log(` ⏭️ pass1-${i}.json already exists, skipping`);
306
- continue;
424
+ try {
425
+ const existing = JSON.parse(readFile(pass1Json));
426
+ if (existing && existing.analysisPerDomain) {
427
+ log(` ⏭️ pass1-${i}.json already exists, skipping`);
428
+ continue;
429
+ }
430
+ } catch { /* malformed — re-run */ }
431
+ log(` ⚠️ pass1-${i}.json exists but is malformed, re-running`);
307
432
  }
308
433
 
309
434
  // Select prompt for this type
@@ -342,9 +467,12 @@ async function cmdInit() {
342
467
  if (fileExists(pass2Json)) {
343
468
  log(" ⏭️ pass2-merged.json already exists, skipping");
344
469
  } else {
345
- let prompt = injectProjectRoot(
346
- readFile(path.join(GENERATED_DIR, "pass2-prompt.md"))
347
- );
470
+ const pass2PromptFile = path.join(GENERATED_DIR, "pass2-prompt.md");
471
+ if (!fileExists(pass2PromptFile)) {
472
+ log(" ❌ pass2-prompt.md not found. Re-run plan-installer.");
473
+ process.exit(1);
474
+ }
475
+ let prompt = injectProjectRoot(readFile(pass2PromptFile));
348
476
 
349
477
  const ok = runClaudePrompt(prompt, { ignoreError: true });
350
478
 
@@ -365,9 +493,12 @@ async function cmdInit() {
365
493
  // ─── [6] Pass 3: Generate + verify ─────────────────────────
366
494
  header("[6] Pass 3 — Generating all files...");
367
495
 
368
- let prompt = injectProjectRoot(
369
- readFile(path.join(GENERATED_DIR, "pass3-prompt.md"))
370
- );
496
+ const pass3PromptFile = path.join(GENERATED_DIR, "pass3-prompt.md");
497
+ if (!fileExists(pass3PromptFile)) {
498
+ log(" ❌ pass3-prompt.md not found. Re-run plan-installer.");
499
+ process.exit(1);
500
+ }
501
+ let prompt = injectProjectRoot(readFile(pass3PromptFile));
371
502
 
372
503
  const ok = runClaudePrompt(prompt, { ignoreError: true });
373
504
 
@@ -453,8 +584,12 @@ function countFiles() {
453
584
  try {
454
585
  let count = 0;
455
586
  const skipDirs = ["node_modules", "generated"];
587
+ const visited = new Set();
456
588
  const scan = (dir) => {
457
589
  if (!fs.existsSync(dir)) return;
590
+ const realDir = fs.realpathSync(dir);
591
+ if (visited.has(realDir)) return;
592
+ visited.add(realDir);
458
593
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
459
594
  if (skipDirs.includes(entry.name)) continue;
460
595
  const full = path.join(dir, entry.name);
@@ -465,7 +600,7 @@ function countFiles() {
465
600
  scan(path.join(PROJECT_ROOT, ".claude"));
466
601
  scan(path.join(PROJECT_ROOT, "claudeos-core"));
467
602
  return count;
468
- } catch {
603
+ } catch (e) {
469
604
  return "?";
470
605
  }
471
606
  }
@@ -475,7 +610,7 @@ function countPass1Files() {
475
610
  return fs
476
611
  .readdirSync(GENERATED_DIR)
477
612
  .filter((f) => f.startsWith("pass1-") && f.endsWith(".json")).length;
478
- } catch {
613
+ } catch (e) {
479
614
  return 0;
480
615
  }
481
616
  }
@@ -535,18 +670,18 @@ const args = process.argv.slice(2);
535
670
  const parsedArgs = parseArgs(args);
536
671
  const command = parsedArgs.command;
537
672
 
538
- if (!command || command === "--help" || command === "-h") {
673
+ if (!command || command === "--help") {
539
674
  showHelp();
540
675
  process.exit(0);
541
676
  }
542
677
 
543
- if (command === "--version" || command === "-v") {
678
+ if (command === "--version") {
544
679
  try {
545
680
  const pkg = JSON.parse(
546
681
  readFile(path.join(TOOLS_DIR, "package.json"))
547
682
  );
548
683
  log(`claudeos-core v${pkg.version}`);
549
- } catch {
684
+ } catch (e) {
550
685
  log("claudeos-core (version unknown)");
551
686
  }
552
687
  process.exit(0);