@kkelly-offical/kkcode 0.1.7 → 0.2.3-preview.1

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 (166) hide show
  1. package/LICENSE +674 -674
  2. package/README.md +474 -387
  3. package/package.json +50 -46
  4. package/src/agent/agent.mjs +228 -220
  5. package/src/agent/custom-agent-loader.mjs +6 -3
  6. package/src/agent/generator.mjs +2 -2
  7. package/src/agent/prompt/assistant.txt +12 -0
  8. package/src/agent/prompt/bug-hunter.txt +89 -89
  9. package/src/agent/prompt/frontend-designer.txt +58 -58
  10. package/src/agent/prompt/guide.txt +1 -1
  11. package/src/agent/prompt/longagent-blueprint-agent.txt +83 -83
  12. package/src/agent/prompt/longagent-coding-agent.txt +37 -37
  13. package/src/agent/prompt/longagent-debugging-agent.txt +46 -46
  14. package/src/agent/prompt/longagent-preview-agent.txt +63 -63
  15. package/src/command/custom-commands.mjs +2 -2
  16. package/src/commands/agent.mjs +1 -1
  17. package/src/commands/background.mjs +145 -4
  18. package/src/commands/chat.mjs +117 -76
  19. package/src/commands/config.mjs +148 -1
  20. package/src/commands/doctor.mjs +30 -6
  21. package/src/commands/init.mjs +32 -6
  22. package/src/commands/longagent.mjs +117 -0
  23. package/src/commands/mcp.mjs +275 -43
  24. package/src/commands/permission.mjs +1 -1
  25. package/src/commands/session.mjs +195 -140
  26. package/src/commands/skill.mjs +63 -0
  27. package/src/commands/theme.mjs +1 -1
  28. package/src/commands/update.mjs +32 -0
  29. package/src/config/defaults.mjs +289 -260
  30. package/src/config/import-config.mjs +1 -1
  31. package/src/config/load-config.mjs +61 -4
  32. package/src/config/schema.mjs +604 -574
  33. package/src/context.mjs +4 -1
  34. package/src/core/constants.mjs +97 -91
  35. package/src/core/types.mjs +1 -1
  36. package/src/github/api.mjs +78 -78
  37. package/src/github/auth.mjs +294 -286
  38. package/src/github/flow.mjs +298 -298
  39. package/src/github/workspace.mjs +225 -212
  40. package/src/index.mjs +87 -82
  41. package/src/knowledge/frontend-aesthetics.txt +38 -38
  42. package/src/mcp/client-http.mjs +139 -141
  43. package/src/mcp/client-sse.mjs +297 -288
  44. package/src/mcp/client-stdio.mjs +534 -533
  45. package/src/mcp/constants.mjs +4 -2
  46. package/src/mcp/registry.mjs +498 -479
  47. package/src/mcp/stdio-framing.mjs +135 -133
  48. package/src/mcp/tool-result.mjs +24 -24
  49. package/src/observability/edit-diagnostics.mjs +449 -0
  50. package/src/observability/index.mjs +42 -42
  51. package/src/observability/metrics.mjs +165 -137
  52. package/src/observability/tracer.mjs +137 -137
  53. package/src/onboarding.mjs +209 -0
  54. package/src/orchestration/background-manager.mjs +567 -372
  55. package/src/orchestration/background-worker.mjs +419 -305
  56. package/src/orchestration/interruption-reason.mjs +21 -0
  57. package/src/orchestration/longagent-manager.mjs +197 -171
  58. package/src/orchestration/stage-scheduler.mjs +733 -728
  59. package/src/orchestration/subagent-router.mjs +7 -1
  60. package/src/orchestration/task-scheduler.mjs +219 -7
  61. package/src/permission/engine.mjs +1 -1
  62. package/src/permission/exec-policy.mjs +370 -370
  63. package/src/permission/file-edit-policy.mjs +108 -0
  64. package/src/permission/prompt.mjs +1 -1
  65. package/src/permission/rules.mjs +116 -7
  66. package/src/plugin/builtin-hooks/post-edit-format.mjs +2 -1
  67. package/src/plugin/builtin-hooks/post-edit-typecheck.mjs +104 -40
  68. package/src/plugin/hook-bus.mjs +19 -5
  69. package/src/plugin/manifest-loader.mjs +222 -0
  70. package/src/provider/anthropic.mjs +396 -390
  71. package/src/provider/ollama.mjs +7 -1
  72. package/src/provider/openai.mjs +382 -340
  73. package/src/provider/retry-policy.mjs +74 -68
  74. package/src/provider/router.mjs +242 -241
  75. package/src/provider/sse.mjs +104 -104
  76. package/src/provider/wizard.mjs +556 -0
  77. package/src/repl/capability-facade.mjs +30 -0
  78. package/src/repl/command-surface.mjs +23 -0
  79. package/src/repl/controller-entry.mjs +40 -0
  80. package/src/repl/core-shell.mjs +208 -0
  81. package/src/repl/dialog-router.mjs +87 -0
  82. package/src/repl/input-engine.mjs +76 -0
  83. package/src/repl/keymap.mjs +7 -0
  84. package/src/repl/operator-surface.mjs +15 -0
  85. package/src/repl/permission-flow.mjs +49 -0
  86. package/src/repl/runtime-facade.mjs +36 -0
  87. package/src/repl/slash-router.mjs +62 -0
  88. package/src/repl/state-store.mjs +29 -0
  89. package/src/repl/turn-controller.mjs +58 -0
  90. package/src/repl/verification.mjs +23 -0
  91. package/src/repl.mjs +3371 -2981
  92. package/src/rules/load-rules.mjs +3 -3
  93. package/src/runtime.mjs +1 -1
  94. package/src/session/agent-transaction.mjs +86 -0
  95. package/src/session/checkpoint.mjs +302 -302
  96. package/src/session/compaction.mjs +298 -298
  97. package/src/session/engine.mjs +417 -232
  98. package/src/session/longagent-4stage.mjs +467 -460
  99. package/src/session/longagent-hybrid.mjs +1344 -1097
  100. package/src/session/longagent-plan.mjs +376 -365
  101. package/src/session/longagent-project-memory.mjs +53 -53
  102. package/src/session/longagent-scaffold.mjs +291 -291
  103. package/src/session/longagent-task-bus.mjs +138 -54
  104. package/src/session/longagent-utils.mjs +828 -472
  105. package/src/session/longagent.mjs +911 -900
  106. package/src/session/loop.mjs +1005 -930
  107. package/src/session/prompt/agent.txt +25 -25
  108. package/src/session/prompt/anthropic.txt +150 -150
  109. package/src/session/prompt/beast.txt +1 -1
  110. package/src/session/prompt/plan.txt +31 -31
  111. package/src/session/prompt/qwen.txt +46 -46
  112. package/src/session/recovery.mjs +21 -0
  113. package/src/session/rollback.mjs +196 -195
  114. package/src/session/routing-observability.mjs +72 -0
  115. package/src/session/runtime-state.mjs +47 -0
  116. package/src/session/store.mjs +523 -519
  117. package/src/session/system-prompt.mjs +308 -273
  118. package/src/session/task-validator.mjs +267 -267
  119. package/src/session/usability-gates.mjs +2 -2
  120. package/src/skill/builtin/commit.mjs +64 -64
  121. package/src/skill/builtin/design.mjs +76 -76
  122. package/src/skill/generator.mjs +18 -2
  123. package/src/skill/registry.mjs +642 -390
  124. package/src/storage/audit-store.mjs +18 -11
  125. package/src/storage/event-log.mjs +7 -1
  126. package/src/storage/ghost-commit-store.mjs +243 -245
  127. package/src/storage/paths.mjs +17 -0
  128. package/src/theme/default-theme.mjs +1 -1
  129. package/src/theme/markdown.mjs +4 -0
  130. package/src/theme/schema.mjs +1 -1
  131. package/src/theme/status-bar.mjs +162 -158
  132. package/src/tool/audit-wrapper.mjs +18 -2
  133. package/src/tool/edit-transaction.mjs +23 -0
  134. package/src/tool/executor.mjs +26 -1
  135. package/src/tool/file-read-state.mjs +65 -0
  136. package/src/tool/git-auto.mjs +526 -526
  137. package/src/tool/git-full-auto.mjs +487 -478
  138. package/src/tool/mutation-guard.mjs +54 -0
  139. package/src/tool/prompt/edit.txt +3 -3
  140. package/src/tool/prompt/multiedit.txt +1 -0
  141. package/src/tool/prompt/notebookedit.txt +2 -1
  142. package/src/tool/prompt/patch.txt +25 -24
  143. package/src/tool/prompt/read.txt +3 -3
  144. package/src/tool/prompt/sysinfo.txt +29 -0
  145. package/src/tool/prompt/task.txt +66 -4
  146. package/src/tool/prompt/write.txt +2 -2
  147. package/src/tool/question-prompt.mjs +99 -93
  148. package/src/tool/registry.mjs +1701 -1343
  149. package/src/tool/task-tool.mjs +14 -6
  150. package/src/ui/activity-renderer.mjs +667 -664
  151. package/src/ui/repl-background-panel.mjs +7 -0
  152. package/src/ui/repl-capability-panel.mjs +9 -0
  153. package/src/ui/repl-dashboard.mjs +54 -4
  154. package/src/ui/repl-help.mjs +110 -0
  155. package/src/ui/repl-operator-panel.mjs +12 -0
  156. package/src/ui/repl-route-feedback.mjs +35 -0
  157. package/src/ui/repl-status-view.mjs +76 -0
  158. package/src/ui/repl-task-panel.mjs +5 -0
  159. package/src/ui/repl-transcript-panel.mjs +56 -0
  160. package/src/ui/repl-turn-summary.mjs +135 -0
  161. package/src/update/checker.mjs +184 -0
  162. package/src/usage/pricing.mjs +122 -121
  163. package/src/usage/usage-meter.mjs +1 -0
  164. package/src/util/git.mjs +562 -519
  165. package/src/util/template.mjs +6 -1
  166. package/src/version.mjs +3 -0
@@ -1,260 +1,289 @@
1
- export const DEFAULT_CONFIG = {
2
- language: "en",
3
- provider: {
4
- default: "openai",
5
- strict_mode: false,
6
- openai: {
7
- base_url: "https://api.openai.com/v1",
8
- api_key_env: "OPENAI_API_KEY",
9
- default_model: "gpt-5.3-codex",
10
- models: ["gpt-5.3-codex", "gpt-5.2"],
11
- timeout_ms: 120000,
12
- stream_idle_timeout_ms: 120000,
13
- max_tokens: 32768,
14
- context_limit: null,
15
- retry_attempts: 3,
16
- retry_base_delay_ms: 800,
17
- stream: true,
18
- thinking: null
19
- },
20
- anthropic: {
21
- base_url: "https://api.anthropic.com/v1",
22
- api_key_env: "ANTHROPIC_API_KEY",
23
- default_model: "claude-opus-4-6",
24
- models: ["claude-sonnet-4-5", "claude-sonnet-4-6", "claude-haiku-4-5-20251001", "claude-opus-4-6"],
25
- timeout_ms: 120000,
26
- stream_idle_timeout_ms: 120000,
27
- max_tokens: 32768,
28
- context_limit: null,
29
- retry_attempts: 3,
30
- retry_base_delay_ms: 800,
31
- stream: true,
32
- thinking: null
33
- },
34
- ollama: {
35
- base_url: "http://localhost:11434",
36
- api_key_env: "",
37
- default_model: "llama3.1",
38
- timeout_ms: 300000,
39
- stream_idle_timeout_ms: 300000,
40
- max_tokens: 32768,
41
- context_limit: null,
42
- retry_attempts: 1,
43
- retry_base_delay_ms: 1000,
44
- stream: true,
45
- thinking: null
46
- },
47
- model_context: {}
48
- },
49
- agent: {
50
- default_mode: "agent",
51
- max_steps: 8,
52
- longagent: {
53
- max_iterations: 0,
54
- no_progress_warning: 3,
55
- no_progress_limit: 5,
56
- max_stage_recoveries: 3,
57
- heartbeat_timeout_ms: 120000,
58
- checkpoint_interval: 5,
59
- lock_timeout_ms: 5000,
60
- parallel: {
61
- enabled: true,
62
- max_concurrency: 3,
63
- stage_pass_rule: "all_success",
64
- task_timeout_ms: 600000,
65
- task_max_retries: 2,
66
- poll_interval_ms: 300
67
- },
68
- planner: {
69
- intake_questions: {
70
- enabled: true,
71
- max_rounds: 6
72
- },
73
- ask_user_after_plan_frozen: false
74
- },
75
- four_stage: {
76
- enabled: false,
77
- preview_max_iterations: 10,
78
- blueprint_max_iterations: 10,
79
- coding_max_iterations: 50,
80
- debugging_max_iterations: 20,
81
- separate_models: {
82
- enabled: false,
83
- preview_model: null,
84
- blueprint_model: null,
85
- coding_model: null,
86
- debugging_model: null
87
- }
88
- },
89
- hybrid: {
90
- enabled: true,
91
- intake: true,
92
- completion_validation: true,
93
- debugging_max_iterations: 20,
94
- max_coding_rollbacks: 2,
95
- parallel_preview: true,
96
- blueprint_review: false,
97
- blueprint_validation: true,
98
- tdd_mode: false,
99
- cross_review: true,
100
- incremental_gates: true,
101
- context_pressure_limit: 8000,
102
- budget_awareness: true,
103
- checkpoint_resume: true,
104
- project_memory: true,
105
- task_bus: true,
106
- coding_phase_timeout_ms: 1800000,
107
- debugging_phase_timeout_ms: 600000,
108
- checkpoint_max_keep: 10,
109
- checkpoint_cleanup: true,
110
- degradation: {
111
- enabled: true,
112
- fallback_model: null,
113
- skip_non_critical: true
114
- },
115
- separate_models: {
116
- enabled: false,
117
- preview_model: null,
118
- blueprint_model: null,
119
- debugging_model: null
120
- },
121
- adaptive_models: {
122
- enabled: false,
123
- low: null,
124
- medium: null,
125
- high: null
126
- }
127
- },
128
- resume_incomplete_files: true,
129
- scaffold: {
130
- enabled: true
131
- },
132
- git: {
133
- enabled: "ask",
134
- auto_branch: true,
135
- auto_commit_stages: true,
136
- auto_merge: true,
137
- branch_prefix: "kkcode"
138
- },
139
- usability_gates: {
140
- prompt_user: "first_run",
141
- build: { enabled: true },
142
- test: { enabled: true },
143
- review: { enabled: true },
144
- health: { enabled: true },
145
- budget: { enabled: true }
146
- }
147
- },
148
- subagents: {},
149
- routing: {
150
- categories: {}
151
- }
152
- },
153
- mcp: {
154
- servers: {},
155
- auto_discover: true,
156
- timeout_ms: 30000,
157
- max_reconnect_attempts: 5,
158
- circuit_reset_ms: 60000,
159
- health_check_interval_ms: 0,
160
- max_buffer_bytes: 16777216,
161
- shutdown_timeout_ms: 5000,
162
- max_sse_buffer_bytes: 4194304
163
- },
164
- skills: {
165
- enabled: true,
166
- dirs: [".kkcode/skills"],
167
- allowed_commands: []
168
- },
169
- permission: {
170
- default_policy: "ask",
171
- non_tty_default: "deny",
172
- rules: []
173
- },
174
- storage: {
175
- session_shard_enabled: true,
176
- flush_interval_ms: 1000,
177
- event_rotate_mb: 32,
178
- event_retain_days: 14
179
- },
180
- background: {
181
- mode: "worker_process",
182
- worker_timeout_ms: 900000,
183
- max_parallel: 2,
184
- max_log_lines: 300
185
- },
186
- runtime: {
187
- tool_registry_cache_ttl_ms: 30000,
188
- mcp_refresh_ttl_ms: 60000
189
- },
190
- tool: {
191
- sources: {
192
- builtin: true,
193
- local: true,
194
- mcp: true,
195
- plugin: true
196
- },
197
- bash_timeout_ms: 120000,
198
- write_lock: {
199
- mode: "file_lock",
200
- wait_timeout_ms: 120000
201
- },
202
- local_dirs: [".kkcode/tools", ".kkcode/tool"],
203
- plugin_dirs: [".kkcode/plugins", ".kkcode/plugin"]
204
- },
205
- session: {
206
- max_history: 30,
207
- recovery: true,
208
- compaction_threshold_ratio: 0.7,
209
- compaction_threshold_messages: 50,
210
- context_cache_points: true
211
- },
212
- review: {
213
- sort: "risk_first",
214
- default_lines: 80,
215
- max_expand_lines: 1200,
216
- risk_weights: {
217
- sensitive_path: 4,
218
- large_change: 3,
219
- medium_change: 2,
220
- small_change: 1,
221
- executable_script: 2,
222
- command_pattern: 3
223
- }
224
- },
225
- usage: {
226
- pricing_file: null,
227
- aggregation: ["turn", "session", "global"],
228
- budget: {
229
- session_usd: null,
230
- global_usd: null,
231
- warn_at_percent: 80,
232
- strategy: "warn"
233
- }
234
- },
235
- ui: {
236
- theme_file: null,
237
- mode_colors: {
238
- ask: "#8da3b9",
239
- plan: "#00b7c2",
240
- agent: "#2ac26f",
241
- longagent: "#ff7a33"
242
- },
243
- layout: "compact",
244
- markdown_render: true,
245
- status: {
246
- show_cost: true,
247
- show_token_meter: true
248
- }
249
- }
250
- }
251
-
252
- export const VALID_PROVIDER_TYPES = ["openai", "anthropic", "ollama", "openai-compatible"]
253
-
254
- import { listProviders } from "../provider/router.mjs"
255
- export function getValidProviderTypes() {
256
- return listProviders()
257
- }
258
- export const VALID_MODES = ["ask", "plan", "agent", "longagent"]
259
- export const VALID_REVIEW_SORT = ["risk_first", "time_order", "file_order"]
260
- export const VALID_LANGUAGES = ["en", "zh"]
1
+ export const DEFAULT_CONFIG = {
2
+ language: "en",
3
+ provider: {
4
+ default: "openai",
5
+ strict_mode: false,
6
+ openai: {
7
+ base_url: "https://api.openai.com/v1",
8
+ api_key_env: "OPENAI_API_KEY",
9
+ default_model: "gpt-5.3-codex",
10
+ models: ["gpt-5.3-codex", "gpt-5.2"],
11
+ timeout_ms: 120000,
12
+ stream_idle_timeout_ms: 120000,
13
+ max_tokens: 32768,
14
+ context_limit: null,
15
+ retry_attempts: 3,
16
+ retry_base_delay_ms: 800,
17
+ stream: true,
18
+ thinking: null
19
+ },
20
+ anthropic: {
21
+ base_url: "https://api.anthropic.com/v1",
22
+ api_key_env: "ANTHROPIC_API_KEY",
23
+ default_model: "claude-opus-4-6",
24
+ models: ["claude-sonnet-4-5", "claude-sonnet-4-6", "claude-haiku-4-5-20251001", "claude-opus-4-6"],
25
+ timeout_ms: 120000,
26
+ stream_idle_timeout_ms: 120000,
27
+ max_tokens: 32768,
28
+ context_limit: null,
29
+ retry_attempts: 3,
30
+ retry_base_delay_ms: 800,
31
+ stream: true,
32
+ thinking: null
33
+ },
34
+ ollama: {
35
+ base_url: "http://localhost:11434",
36
+ api_key_env: "",
37
+ default_model: "llama3.1",
38
+ timeout_ms: 300000,
39
+ stream_idle_timeout_ms: 300000,
40
+ max_tokens: 32768,
41
+ context_limit: null,
42
+ retry_attempts: 1,
43
+ retry_base_delay_ms: 1000,
44
+ stream: true,
45
+ thinking: null
46
+ },
47
+ model_context: {}
48
+ },
49
+ agent: {
50
+ default_mode: "assistant",
51
+ max_steps: 8,
52
+ longagent: {
53
+ max_iterations: 0,
54
+ no_progress_warning: 3,
55
+ no_progress_limit: 5,
56
+ max_stage_recoveries: 3,
57
+ heartbeat_timeout_ms: 120000,
58
+ checkpoint_interval: 5,
59
+ lock_timeout_ms: 5000,
60
+ parallel: {
61
+ enabled: true,
62
+ max_concurrency: 3,
63
+ stage_pass_rule: "all_success",
64
+ task_timeout_ms: 600000,
65
+ task_max_retries: 2,
66
+ poll_interval_ms: 300
67
+ },
68
+ planner: {
69
+ intake_questions: {
70
+ enabled: true,
71
+ max_rounds: 6
72
+ },
73
+ ask_user_after_plan_frozen: false
74
+ },
75
+ four_stage: {
76
+ enabled: false,
77
+ preview_max_iterations: 10,
78
+ blueprint_max_iterations: 10,
79
+ coding_max_iterations: 50,
80
+ debugging_max_iterations: 20,
81
+ separate_models: {
82
+ enabled: false,
83
+ preview_model: null,
84
+ blueprint_model: null,
85
+ coding_model: null,
86
+ debugging_model: null
87
+ }
88
+ },
89
+ hybrid: {
90
+ enabled: true,
91
+ intake: true,
92
+ completion_validation: true,
93
+ debugging_max_iterations: 20,
94
+ max_coding_rollbacks: 2,
95
+ parallel_preview: true,
96
+ blueprint_review: false,
97
+ blueprint_validation: true,
98
+ tdd_mode: false,
99
+ cross_review: true,
100
+ incremental_gates: true,
101
+ context_pressure_limit: 8000,
102
+ budget_awareness: true,
103
+ checkpoint_resume: true,
104
+ project_memory: true,
105
+ task_bus: true,
106
+ coding_phase_timeout_ms: 1800000,
107
+ debugging_phase_timeout_ms: 600000,
108
+ checkpoint_max_keep: 10,
109
+ checkpoint_cleanup: true,
110
+ degradation: {
111
+ enabled: true,
112
+ fallback_model: null,
113
+ skip_non_critical: true
114
+ },
115
+ separate_models: {
116
+ enabled: false,
117
+ preview_model: null,
118
+ blueprint_model: null,
119
+ debugging_model: null
120
+ },
121
+ adaptive_models: {
122
+ enabled: false,
123
+ low: null,
124
+ medium: null,
125
+ high: null
126
+ }
127
+ },
128
+ resume_incomplete_files: true,
129
+ scaffold: {
130
+ enabled: true
131
+ },
132
+ git: {
133
+ enabled: "ask",
134
+ auto_branch: true,
135
+ auto_commit_stages: true,
136
+ auto_merge: true,
137
+ branch_prefix: "kkcode"
138
+ },
139
+ usability_gates: {
140
+ prompt_user: "first_run",
141
+ build: { enabled: true },
142
+ test: { enabled: true },
143
+ review: { enabled: true },
144
+ health: { enabled: true },
145
+ budget: { enabled: true }
146
+ }
147
+ },
148
+ subagents: {},
149
+ routing: {
150
+ categories: {}
151
+ }
152
+ },
153
+ mcp: {
154
+ servers: {},
155
+ auto_discover: true,
156
+ timeout_ms: 30000,
157
+ max_reconnect_attempts: 5,
158
+ circuit_reset_ms: 60000,
159
+ health_check_interval_ms: 0,
160
+ max_buffer_bytes: 16777216,
161
+ shutdown_timeout_ms: 5000,
162
+ max_sse_buffer_bytes: 4194304
163
+ },
164
+ skills: {
165
+ enabled: true,
166
+ auto_seed: true,
167
+ dirs: [".kkcode/skills"],
168
+ allowed_commands: []
169
+ },
170
+ permission: {
171
+ mode: "auto",
172
+ default_policy: "ask",
173
+ non_tty_default: "deny",
174
+ rules: []
175
+ },
176
+ storage: {
177
+ session_shard_enabled: true,
178
+ flush_interval_ms: 1000,
179
+ event_rotate_mb: 32,
180
+ event_retain_days: 14
181
+ },
182
+ background: {
183
+ mode: "worker_process",
184
+ worker_timeout_ms: 900000,
185
+ max_parallel: 2,
186
+ max_log_lines: 300
187
+ },
188
+ runtime: {
189
+ tool_registry_cache_ttl_ms: 30000,
190
+ mcp_refresh_ttl_ms: 60000
191
+ },
192
+ tool: {
193
+ sources: {
194
+ builtin: true,
195
+ local: true,
196
+ mcp: true,
197
+ plugin: true
198
+ },
199
+ bash_timeout_ms: 120000,
200
+ write_lock: {
201
+ mode: "file_lock",
202
+ wait_timeout_ms: 120000
203
+ },
204
+ sensitive_file_patterns: [
205
+ "AGENTS.md",
206
+ "**/AGENTS.md",
207
+ "KKCODE.md",
208
+ "**/KKCODE.md",
209
+ ".kkcode/**",
210
+ "**/.kkcode/**",
211
+ "kkcode.config.yaml",
212
+ "**/kkcode.config.yaml",
213
+ ".mcp.json",
214
+ "**/.mcp.json",
215
+ ".env",
216
+ ".env.*",
217
+ "**/.env",
218
+ "**/.env.*",
219
+ ".github/workflows/**",
220
+ "**/.github/workflows/**"
221
+ ],
222
+ local_dirs: [".kkcode/tools", ".kkcode/tool"],
223
+ plugin_dirs: [".kkcode/plugins", ".kkcode/plugin"]
224
+ },
225
+ session: {
226
+ max_history: 30,
227
+ recovery: true,
228
+ compaction_threshold_ratio: 0.7,
229
+ compaction_threshold_messages: 50,
230
+ context_cache_points: true
231
+ },
232
+ review: {
233
+ sort: "risk_first",
234
+ default_lines: 80,
235
+ max_expand_lines: 1200,
236
+ risk_weights: {
237
+ sensitive_path: 4,
238
+ large_change: 3,
239
+ medium_change: 2,
240
+ small_change: 1,
241
+ executable_script: 2,
242
+ command_pattern: 3
243
+ }
244
+ },
245
+ usage: {
246
+ pricing_file: null,
247
+ aggregation: ["turn", "session", "global"],
248
+ budget: {
249
+ session_usd: null,
250
+ global_usd: null,
251
+ warn_at_percent: 80,
252
+ strategy: "warn"
253
+ }
254
+ },
255
+ update: {
256
+ enabled: true,
257
+ notify_on_startup: true,
258
+ auto_install: false,
259
+ channel: "latest",
260
+ check_interval_hours: 12,
261
+ registry: "https://registry.npmjs.org",
262
+ timeout_ms: 2500
263
+ },
264
+ ui: {
265
+ theme_file: null,
266
+ mode_colors: {
267
+ assistant: "#22d3ee",
268
+ plan: "#00b7c2",
269
+ agent: "#2ac26f",
270
+ longagent: "#ff7a33"
271
+ },
272
+ layout: "compact",
273
+ markdown_render: true,
274
+ status: {
275
+ show_cost: true,
276
+ show_token_meter: true
277
+ }
278
+ }
279
+ }
280
+
281
+ export const VALID_PROVIDER_TYPES = ["openai", "anthropic", "ollama", "openai-compatible"]
282
+
283
+ import { listProviders } from "../provider/router.mjs"
284
+ export function getValidProviderTypes() {
285
+ return listProviders()
286
+ }
287
+ export const VALID_MODES = ["assistant", "plan", "agent", "code", "coding", "longagent"]
288
+ export const VALID_REVIEW_SORT = ["risk_first", "time_order", "file_order"]
289
+ export const VALID_LANGUAGES = ["en", "zh"]
@@ -73,4 +73,4 @@ export function importConfig(input = {}) {
73
73
  }
74
74
 
75
75
  return next
76
- }
76
+ }
@@ -3,7 +3,7 @@ import { access, readFile } from "node:fs/promises"
3
3
  import YAML from "yaml"
4
4
  import { DEFAULT_CONFIG } from "./defaults.mjs"
5
5
  import { validateConfig } from "./schema.mjs"
6
- import { projectConfigCandidates, userConfigCandidates } from "../storage/paths.mjs"
6
+ import { projectConfigCandidates, userConfigCandidates, envFileCandidates } from "../storage/paths.mjs"
7
7
 
8
8
  async function exists(file) {
9
9
  try {
@@ -38,6 +38,46 @@ async function firstExisting(candidates) {
38
38
  return null
39
39
  }
40
40
 
41
+ /**
42
+ * Parse .env file — only extract KKCODE_ prefixed vars into nested config.
43
+ * Uses __ (double underscore) as nesting separator, single _ stays in key name.
44
+ *
45
+ * KKCODE_PROVIDER__DEFAULT=anthropic → { provider: { default: "anthropic" } }
46
+ * KKCODE_AGENT__LONGAGENT__PARALLEL__MAX_CONCURRENCY=5 → { agent: { longagent: { parallel: { max_concurrency: 5 } } } }
47
+ * KKCODE_LANGUAGE=zh → { language: "zh" }
48
+ */
49
+ export function parseEnvOverlay(content) {
50
+ const config = {}
51
+ for (const line of content.split("\n")) {
52
+ const trimmed = line.trim()
53
+ if (!trimmed || trimmed.startsWith("#")) continue
54
+ const eqIdx = trimmed.indexOf("=")
55
+ if (eqIdx <= 0) continue
56
+ const key = trimmed.slice(0, eqIdx).trim()
57
+ if (!key.startsWith("KKCODE_")) continue
58
+ let val = trimmed.slice(eqIdx + 1).trim()
59
+ // strip surrounding quotes
60
+ if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
61
+ val = val.slice(1, -1)
62
+ }
63
+ // split on __ for nesting, lowercase each part
64
+ const parts = key.slice(7).split("__").map(p => p.toLowerCase())
65
+ // coerce types
66
+ let typed = val
67
+ if (val === "true") typed = true
68
+ else if (val === "false") typed = false
69
+ else if (val !== "" && !isNaN(val)) typed = Number(val)
70
+
71
+ let cursor = config
72
+ for (let i = 0; i < parts.length - 1; i++) {
73
+ if (!cursor[parts[i]] || typeof cursor[parts[i]] !== "object") cursor[parts[i]] = {}
74
+ cursor = cursor[parts[i]]
75
+ }
76
+ cursor[parts[parts.length - 1]] = typed
77
+ }
78
+ return config
79
+ }
80
+
41
81
  async function loadOne(filePath) {
42
82
  if (!filePath) return { config: {}, errors: [] }
43
83
  try {
@@ -57,7 +97,22 @@ export async function loadConfig(cwd = process.cwd()) {
57
97
 
58
98
  const userLoaded = await loadOne(userPath)
59
99
  const projectLoaded = await loadOne(projectPath)
60
- const merged = mergeObject(mergeObject(DEFAULT_CONFIG, userLoaded.config), projectLoaded.config)
100
+ let merged = mergeObject(mergeObject(DEFAULT_CONFIG, userLoaded.config), projectLoaded.config)
101
+
102
+ // .env overlay — highest priority, KKCODE_ prefixed vars
103
+ let envPath = null
104
+ let envOverlay = {}
105
+ const envCandidate = await firstExisting(envFileCandidates(cwd))
106
+ if (envCandidate) {
107
+ try {
108
+ const raw = await readFile(envCandidate, "utf8")
109
+ envOverlay = parseEnvOverlay(raw)
110
+ if (Object.keys(envOverlay).length > 0) {
111
+ envPath = envCandidate
112
+ merged = mergeObject(merged, envOverlay)
113
+ }
114
+ } catch { /* ignore unreadable .env */ }
115
+ }
61
116
 
62
117
  const source = {
63
118
  userPath,
@@ -65,7 +120,9 @@ export async function loadConfig(cwd = process.cwd()) {
65
120
  userRaw: userLoaded.config,
66
121
  projectPath,
67
122
  projectDir: projectPath ? path.dirname(projectPath) : null,
68
- projectRaw: projectLoaded.config
123
+ projectRaw: projectLoaded.config,
124
+ envPath,
125
+ envOverlay
69
126
  }
70
127
 
71
128
  return {
@@ -73,4 +130,4 @@ export async function loadConfig(cwd = process.cwd()) {
73
130
  source,
74
131
  errors: [...userLoaded.errors, ...projectLoaded.errors]
75
132
  }
76
- }
133
+ }