@sean.holung/minicode 0.3.5 → 0.3.7

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 (47) hide show
  1. package/README.md +22 -45
  2. package/dist/scripts/run-benchmarks.js +1 -0
  3. package/dist/src/agent/config.js +53 -66
  4. package/dist/src/agent/editable-config.js +56 -58
  5. package/dist/src/agent/home-env.js +74 -0
  6. package/dist/src/cli/config-slash-command.js +15 -13
  7. package/dist/src/serve/agent-bridge.js +87 -28
  8. package/dist/src/serve/mcp-server.js +19 -13
  9. package/dist/src/serve/server.js +190 -4
  10. package/dist/src/session/session-preview.js +14 -0
  11. package/dist/src/shared/graph-search.js +80 -0
  12. package/dist/src/shared/graph-selection.js +40 -0
  13. package/dist/src/shared/symbol-search.js +156 -0
  14. package/dist/src/tools/search-code-map.js +27 -35
  15. package/dist/src/web/app.js +582 -64
  16. package/dist/src/web/index.html +84 -6
  17. package/dist/src/web/style.css +256 -1
  18. package/dist/tests/config-api.test.js +10 -5
  19. package/dist/tests/config-integration.test.js +130 -56
  20. package/dist/tests/config-slash-command.test.js +12 -11
  21. package/dist/tests/config.test.js +21 -4
  22. package/dist/tests/editable-config.test.js +15 -12
  23. package/dist/tests/graph-onboarding.test.js +22 -1
  24. package/dist/tests/graph-search.test.js +66 -0
  25. package/dist/tests/graph-selection.test.js +58 -0
  26. package/dist/tests/home-env.test.js +56 -0
  27. package/dist/tests/mcp-and-plugin.test.js +3 -0
  28. package/dist/tests/search-code-map.test.js +9 -0
  29. package/dist/tests/serve.integration.test.js +255 -6
  30. package/dist/tests/session-preview.test.js +56 -0
  31. package/dist/tests/session-ui.test.js +2 -0
  32. package/dist/tests/settings-ui.test.js +18 -0
  33. package/dist/tests/system-prompt.test.js +1 -0
  34. package/dist/tests/test-utils.js +1 -0
  35. package/node_modules/@minicode/agent-sdk/dist/src/agent/types.d.ts +1 -0
  36. package/node_modules/@minicode/agent-sdk/dist/src/agent/types.d.ts.map +1 -1
  37. package/node_modules/@minicode/agent-sdk/dist/src/model/client.d.ts +8 -1
  38. package/node_modules/@minicode/agent-sdk/dist/src/model/client.d.ts.map +1 -1
  39. package/node_modules/@minicode/agent-sdk/dist/src/model/client.js +143 -27
  40. package/node_modules/@minicode/agent-sdk/dist/src/model/client.js.map +1 -1
  41. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-openai.test.js +87 -0
  42. package/node_modules/@minicode/agent-sdk/dist/tests/model-client-openai.test.js.map +1 -1
  43. package/node_modules/@minicode/agent-sdk/dist/tests/test-utils.d.ts.map +1 -1
  44. package/node_modules/@minicode/agent-sdk/dist/tests/test-utils.js +1 -0
  45. package/node_modules/@minicode/agent-sdk/dist/tests/test-utils.js.map +1 -1
  46. package/node_modules/@minicode/agent-sdk/dist/tsconfig.tsbuildinfo +1 -1
  47. package/package.json +1 -1
@@ -61,7 +61,18 @@
61
61
  <div class="config-overlay-content">
62
62
  <h2>Agent not connected</h2>
63
63
  <p id="config-missing" class="config-missing hidden"></p>
64
- <p>minicode needs a model provider to run. Configure one of the following:</p>
64
+ <div id="config-overlay-spotlight" class="config-overlay-spotlight">
65
+ <div class="config-overlay-spotlight-copy">
66
+ <span class="config-overlay-spotlight-badge">Fastest way to start</span>
67
+ <strong>Try minicode for free with OpenRouter</strong>
68
+ <p>Create a free OpenRouter account, connect it to this <code>minicode serve</code> session, and use free models without editing config files first.</p>
69
+ </div>
70
+ <div class="config-overlay-actions config-overlay-spotlight-actions">
71
+ <button id="connect-openrouter-btn" class="dropdown-action" type="button" data-openrouter-connect="true">Connect OpenRouter</button>
72
+ <span class="config-overlay-action-note">Session-only. Nothing is written to disk.</span>
73
+ </div>
74
+ </div>
75
+ <p id="config-overlay-intro">minicode needs a model provider to run. Configure one of the following:</p>
65
76
  <div class="config-overlay-options">
66
77
  <div class="config-overlay-option">
67
78
  <strong>Local model (LM Studio, Ollama, etc.)</strong>
@@ -79,13 +90,14 @@ MODEL=claude-sonnet-4-20250514</pre>
79
90
  </div>
80
91
  <div class="config-overlay-option">
81
92
  <strong>OpenRouter / remote OpenAI-compatible</strong>
82
- <p>Set in <code>~/.minicode/.env</code> or your shell environment:</p>
93
+ <p>Set it manually in <code>~/.minicode/.env</code> or your shell environment:</p>
83
94
  <pre>MODEL_PROVIDER=openai-compatible
84
95
  OPENAI_BASE_URL=https://openrouter.ai/api/v1
85
96
  OPENROUTER_API_KEY=sk-or-...
86
97
  MODEL=your-model-name</pre>
87
98
  </div>
88
99
  </div>
100
+ <p id="config-connect-status" class="config-connect-status hidden"></p>
89
101
  <p class="config-overlay-footer">After updating your config, restart <code>minicode serve</code>.</p>
90
102
  </div>
91
103
  </div>
@@ -101,7 +113,7 @@ MODEL=your-model-name</pre>
101
113
  <div id="pane-divider"></div>
102
114
  <div id="graph-pane">
103
115
  <div id="graph-toolbar">
104
- <input id="graph-search" type="text" placeholder="Search symbols..." autocomplete="off" />
116
+ <input id="graph-search" type="text" placeholder="Search symbols or files..." autocomplete="off" />
105
117
  <button id="graph-analyze" class="header-btn">Analyze</button>
106
118
  <button id="graph-fit" class="header-btn">Fit</button>
107
119
  <button id="graph-relayout" class="header-btn">Re-layout</button>
@@ -144,26 +156,33 @@ MODEL=your-model-name</pre>
144
156
  <div class="modal-header">
145
157
  <div>
146
158
  <h2 id="settings-title">Settings</h2>
147
- <p id="settings-subtitle" class="modal-subtitle">Persist non-secret global defaults for future sessions.</p>
159
+ <p id="settings-subtitle" class="modal-subtitle">Persist non-secret global defaults in <code>~/.minicode/.env</code> for future sessions.</p>
148
160
  </div>
149
161
  <button id="settings-close" class="header-btn" type="button">Close</button>
150
162
  </div>
151
163
 
152
164
  <div class="settings-toolbar">
153
165
  <div class="settings-path-group">
154
- <span class="settings-path-label">Config file</span>
166
+ <span class="settings-path-label">Env file</span>
155
167
  <code id="settings-path" class="settings-path"></code>
156
168
  </div>
157
169
  </div>
158
170
 
159
171
  <div id="settings-banner" class="settings-banner hidden" role="status" aria-live="polite"></div>
172
+ <div id="settings-openrouter-session" class="settings-session-banner hidden" role="status" aria-live="polite">
173
+ <div class="settings-session-copy">
174
+ <div class="settings-session-title">OpenRouter is connected for this serve session</div>
175
+ <div id="settings-openrouter-session-meta" class="settings-session-meta"></div>
176
+ </div>
177
+ <button id="disconnect-openrouter-btn" class="header-btn" type="button">Disconnect OpenRouter</button>
178
+ </div>
160
179
  <div id="settings-list" class="settings-list">
161
180
  <div class="dropdown-empty">Loading settings...</div>
162
181
  </div>
163
182
 
164
183
  <div class="modal-footer">
165
184
  <p class="settings-footnote">
166
- Secrets such as API keys stay env-only for now. Saved changes update <code>~/.minicode/agent.config.json</code> and apply to new runs after restart.
185
+ Secrets such as API keys stay env-only for now. Saved changes update <code>~/.minicode/.env</code> and apply to new runs after restart.
167
186
  </p>
168
187
  <div class="settings-actions">
169
188
  <button id="settings-reset" class="header-btn" type="button">Reset changes</button>
@@ -173,6 +192,65 @@ MODEL=your-model-name</pre>
173
192
  </section>
174
193
  </div>
175
194
 
195
+ <div id="openrouter-connect-modal" class="modal hidden" aria-hidden="true">
196
+ <div id="openrouter-connect-backdrop" class="modal-backdrop"></div>
197
+ <section class="modal-panel modal-panel-compact" role="dialog" aria-modal="true" aria-labelledby="openrouter-connect-title">
198
+ <div class="modal-header">
199
+ <div>
200
+ <h2 id="openrouter-connect-title">Connect OpenRouter</h2>
201
+ <p class="modal-subtitle">Authorize this <code>minicode serve</code> session and optionally save the resulting setup for future runs.</p>
202
+ </div>
203
+ <button id="openrouter-connect-close" class="header-btn" type="button">Close</button>
204
+ </div>
205
+
206
+ <div class="openrouter-connect-body">
207
+ <p class="openrouter-connect-lead">
208
+ minicode will redirect you to OpenRouter so you can sign in and approve access. By default, the resulting API key is used only for this live serve session.
209
+ </p>
210
+
211
+ <div class="openrouter-connect-note">
212
+ <strong>Optional persistence</strong>
213
+ <p>If you opt in below, minicode will update <code>~/.minicode/.env</code> after sign-in so future runs can reuse this OpenRouter setup.</p>
214
+ </div>
215
+
216
+ <label class="openrouter-persist-toggle" for="openrouter-persist-checkbox">
217
+ <input id="openrouter-persist-checkbox" type="checkbox" />
218
+ <span>Save the OpenRouter credentials and provider defaults to <code>~/.minicode/.env</code> after sign-in.</span>
219
+ </label>
220
+
221
+ <p class="openrouter-connect-help">
222
+ When enabled, minicode will store <code>OPENROUTER_API_KEY</code> and set <code>MODEL_PROVIDER</code> plus <code>OPENAI_BASE_URL</code> for OpenRouter. Nothing is written unless you check the box.
223
+ </p>
224
+ </div>
225
+
226
+ <div class="modal-footer">
227
+ <p class="settings-footnote">
228
+ OpenRouter handles the sign-in. minicode only receives the API key after you approve access.
229
+ </p>
230
+ <div class="settings-actions">
231
+ <button id="openrouter-connect-cancel" class="header-btn" type="button">Cancel</button>
232
+ <button id="openrouter-connect-continue" class="dropdown-action" type="button">Continue</button>
233
+ </div>
234
+ </div>
235
+ </section>
236
+ </div>
237
+
238
+ <div id="file-preview-modal" class="modal hidden" aria-hidden="true">
239
+ <div id="file-preview-backdrop" class="modal-backdrop"></div>
240
+ <section class="modal-panel modal-panel-file-preview" role="dialog" aria-modal="true" aria-labelledby="file-preview-title">
241
+ <div class="modal-header">
242
+ <div>
243
+ <h2 id="file-preview-title">File preview</h2>
244
+ <p id="file-preview-path" class="modal-subtitle">Loading…</p>
245
+ </div>
246
+ <button id="file-preview-close" class="header-btn" type="button">Close</button>
247
+ </div>
248
+ <div class="file-preview-body">
249
+ <pre id="file-preview-code" class="file-preview-code">Loading...</pre>
250
+ </div>
251
+ </section>
252
+ </div>
253
+
176
254
  <script type="module" src="app.js"></script>
177
255
  </body>
178
256
  </html>
@@ -335,7 +335,7 @@ h1 {
335
335
  inset: 0;
336
336
  z-index: 50;
337
337
  display: flex;
338
- align-items: center;
338
+ align-items: flex-start;
339
339
  justify-content: center;
340
340
  background: rgba(26, 27, 38, 0.92);
341
341
  backdrop-filter: blur(4px);
@@ -348,6 +348,7 @@ h1 {
348
348
  .config-overlay-content {
349
349
  max-width: 520px;
350
350
  width: 100%;
351
+ margin: 0 auto;
351
352
  }
352
353
 
353
354
  .config-overlay-content h2 {
@@ -362,6 +363,52 @@ h1 {
362
363
  margin-bottom: 16px;
363
364
  }
364
365
 
366
+ .config-overlay-spotlight {
367
+ display: flex;
368
+ flex-direction: column;
369
+ gap: 12px;
370
+ margin-bottom: 16px;
371
+ padding: 14px 16px;
372
+ border-radius: 8px;
373
+ border: 1px solid rgba(122, 162, 247, 0.35);
374
+ background:
375
+ linear-gradient(135deg, rgba(122, 162, 247, 0.18), rgba(158, 206, 106, 0.08)),
376
+ var(--bg-surface);
377
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
378
+ }
379
+
380
+ .config-overlay-spotlight-copy {
381
+ display: flex;
382
+ flex-direction: column;
383
+ gap: 6px;
384
+ }
385
+
386
+ .config-overlay-spotlight-badge {
387
+ align-self: flex-start;
388
+ padding: 2px 8px;
389
+ border-radius: 999px;
390
+ background: rgba(122, 162, 247, 0.18);
391
+ color: var(--accent);
392
+ font-size: 11px;
393
+ font-weight: 600;
394
+ letter-spacing: 0.02em;
395
+ }
396
+
397
+ .config-overlay-spotlight strong {
398
+ color: var(--text);
399
+ font-size: 14px;
400
+ }
401
+
402
+ .config-overlay-spotlight p {
403
+ margin: 0;
404
+ color: var(--text);
405
+ font-size: 12px;
406
+ }
407
+
408
+ .config-overlay-spotlight-actions {
409
+ margin-bottom: 0;
410
+ }
411
+
365
412
  .config-missing {
366
413
  color: var(--red, #f7768e);
367
414
  font-size: 13px;
@@ -408,6 +455,44 @@ h1 {
408
455
  word-break: break-all;
409
456
  }
410
457
 
458
+ .config-overlay-actions {
459
+ display: flex;
460
+ align-items: center;
461
+ gap: 10px;
462
+ margin-bottom: 10px;
463
+ flex-wrap: wrap;
464
+ }
465
+
466
+ .config-overlay-action-note {
467
+ color: var(--text-dim);
468
+ font-size: 12px;
469
+ }
470
+
471
+ .config-connect-status {
472
+ font-size: 12px;
473
+ padding: 8px 12px;
474
+ border-radius: 4px;
475
+ margin-top: 14px;
476
+ }
477
+
478
+ .config-connect-status.info {
479
+ color: var(--text);
480
+ background: rgba(122, 162, 247, 0.14);
481
+ border: 1px solid rgba(122, 162, 247, 0.28);
482
+ }
483
+
484
+ .config-connect-status.success {
485
+ color: var(--green);
486
+ background: rgba(158, 206, 106, 0.12);
487
+ border: 1px solid rgba(158, 206, 106, 0.28);
488
+ }
489
+
490
+ .config-connect-status.error {
491
+ color: var(--red, #f7768e);
492
+ background: rgba(247, 118, 142, 0.12);
493
+ border: 1px solid rgba(247, 118, 142, 0.28);
494
+ }
495
+
411
496
  .config-overlay-footer {
412
497
  margin-top: 16px;
413
498
  color: var(--text-dim);
@@ -773,6 +858,17 @@ footer {
773
858
  overflow: hidden;
774
859
  }
775
860
 
861
+ .modal-panel-compact {
862
+ width: min(640px, calc(100vw - 32px));
863
+ }
864
+
865
+ .modal-panel-file-preview {
866
+ width: min(1200px, 80vw);
867
+ height: 80vh;
868
+ max-width: 80vw;
869
+ max-height: 80vh;
870
+ }
871
+
776
872
  .modal-header,
777
873
  .modal-footer,
778
874
  .settings-toolbar {
@@ -798,6 +894,29 @@ footer {
798
894
  color: var(--text-dim);
799
895
  }
800
896
 
897
+ .file-preview-body {
898
+ flex: 1;
899
+ min-height: 0;
900
+ padding: 0 20px 20px;
901
+ overflow: auto;
902
+ background: rgba(15, 16, 26, 0.92);
903
+ }
904
+
905
+ .file-preview-code {
906
+ margin: 0;
907
+ min-height: 100%;
908
+ padding: 18px;
909
+ border-radius: 10px;
910
+ background: var(--bg);
911
+ border: 1px solid var(--border);
912
+ color: var(--text);
913
+ font-size: 12px;
914
+ line-height: 1.6;
915
+ overflow: auto;
916
+ white-space: pre;
917
+ font-family: var(--font-mono);
918
+ }
919
+
801
920
  .settings-toolbar {
802
921
  display: flex;
803
922
  align-items: flex-end;
@@ -1009,6 +1128,35 @@ footer {
1009
1128
  gap: 8px;
1010
1129
  }
1011
1130
 
1131
+ .settings-session-banner {
1132
+ display: flex;
1133
+ align-items: center;
1134
+ justify-content: space-between;
1135
+ gap: 14px;
1136
+ margin: 0 0 14px;
1137
+ padding: 12px 14px;
1138
+ border-radius: 10px;
1139
+ border: 1px solid rgba(122, 162, 247, 0.24);
1140
+ background: rgba(122, 162, 247, 0.09);
1141
+ }
1142
+
1143
+ .settings-session-copy {
1144
+ min-width: 0;
1145
+ }
1146
+
1147
+ .settings-session-title {
1148
+ font-size: 13px;
1149
+ color: var(--text);
1150
+ font-weight: 600;
1151
+ margin-bottom: 4px;
1152
+ }
1153
+
1154
+ .settings-session-meta {
1155
+ font-size: 12px;
1156
+ color: var(--text-dim);
1157
+ overflow-wrap: anywhere;
1158
+ }
1159
+
1012
1160
  .settings-help {
1013
1161
  font-size: 11px;
1014
1162
  color: var(--text-dim);
@@ -1040,6 +1188,65 @@ footer {
1040
1188
  flex-shrink: 0;
1041
1189
  }
1042
1190
 
1191
+ .openrouter-connect-body {
1192
+ padding: 20px;
1193
+ display: flex;
1194
+ flex-direction: column;
1195
+ gap: 14px;
1196
+ }
1197
+
1198
+ .openrouter-connect-lead {
1199
+ font-size: 13px;
1200
+ color: var(--text);
1201
+ }
1202
+
1203
+ .openrouter-connect-note {
1204
+ padding: 12px 14px;
1205
+ border-radius: 10px;
1206
+ border: 1px solid rgba(122, 162, 247, 0.22);
1207
+ background: rgba(122, 162, 247, 0.08);
1208
+ }
1209
+
1210
+ .openrouter-connect-note strong {
1211
+ display: block;
1212
+ color: var(--text);
1213
+ font-size: 13px;
1214
+ margin-bottom: 4px;
1215
+ }
1216
+
1217
+ .openrouter-connect-note p,
1218
+ .openrouter-connect-help {
1219
+ font-size: 12px;
1220
+ color: var(--text-dim);
1221
+ }
1222
+
1223
+ .openrouter-persist-toggle {
1224
+ display: flex;
1225
+ align-items: flex-start;
1226
+ gap: 10px;
1227
+ padding: 12px 14px;
1228
+ border-radius: 10px;
1229
+ border: 1px solid var(--border);
1230
+ background: rgba(26, 27, 38, 0.72);
1231
+ cursor: pointer;
1232
+ }
1233
+
1234
+ .openrouter-persist-toggle input {
1235
+ margin-top: 2px;
1236
+ }
1237
+
1238
+ .openrouter-persist-toggle span {
1239
+ font-size: 12px;
1240
+ color: var(--text);
1241
+ }
1242
+
1243
+ @media (max-width: 760px) {
1244
+ .settings-session-banner {
1245
+ flex-direction: column;
1246
+ align-items: flex-start;
1247
+ }
1248
+ }
1249
+
1043
1250
  .settings-list::-webkit-scrollbar,
1044
1251
  .settings-path::-webkit-scrollbar {
1045
1252
  width: 6px;
@@ -1126,6 +1333,7 @@ main::-webkit-scrollbar-thumb {
1126
1333
  display: flex;
1127
1334
  align-items: center;
1128
1335
  justify-content: space-between;
1336
+ gap: 12px;
1129
1337
  padding: 6px 10px;
1130
1338
  cursor: pointer;
1131
1339
  font-size: 12px;
@@ -1136,14 +1344,39 @@ main::-webkit-scrollbar-thumb {
1136
1344
  background: var(--bg-hover);
1137
1345
  }
1138
1346
 
1347
+ .search-result-body {
1348
+ display: flex;
1349
+ flex: 1;
1350
+ flex-direction: column;
1351
+ min-width: 0;
1352
+ }
1353
+
1139
1354
  .search-result-name {
1140
1355
  color: var(--text);
1141
1356
  font-weight: 500;
1357
+ overflow: hidden;
1358
+ text-overflow: ellipsis;
1359
+ white-space: nowrap;
1360
+ }
1361
+
1362
+ .search-result-subtitle {
1363
+ color: var(--text-dim);
1364
+ font-size: 11px;
1365
+ overflow: hidden;
1366
+ text-overflow: ellipsis;
1367
+ white-space: nowrap;
1142
1368
  }
1143
1369
 
1144
1370
  .search-result-kind {
1145
1371
  font-size: 11px;
1146
1372
  opacity: 0.7;
1373
+ flex-shrink: 0;
1374
+ letter-spacing: 0.04em;
1375
+ text-transform: uppercase;
1376
+ }
1377
+
1378
+ .search-result-kind.file {
1379
+ opacity: 0.9;
1147
1380
  }
1148
1381
 
1149
1382
  #cy {
@@ -1578,6 +1811,24 @@ main::-webkit-scrollbar-thumb {
1578
1811
  margin-bottom: 12px;
1579
1812
  }
1580
1813
 
1814
+ .detail-file-link {
1815
+ padding: 0;
1816
+ border: 0;
1817
+ background: none;
1818
+ color: inherit;
1819
+ font: inherit;
1820
+ text-align: left;
1821
+ text-decoration: underline;
1822
+ text-decoration-color: rgba(124, 92, 255, 0.35);
1823
+ text-underline-offset: 2px;
1824
+ cursor: pointer;
1825
+ }
1826
+
1827
+ .detail-file-link:hover {
1828
+ color: var(--text);
1829
+ text-decoration-color: var(--accent);
1830
+ }
1831
+
1581
1832
  .detail-signature {
1582
1833
  background: var(--bg);
1583
1834
  border: 1px solid var(--border);
@@ -1799,6 +2050,10 @@ main::-webkit-scrollbar-thumb {
1799
2050
  align-items: stretch;
1800
2051
  }
1801
2052
 
2053
+ .openrouter-persist-toggle {
2054
+ align-items: flex-start;
2055
+ }
2056
+
1802
2057
  .settings-path-group {
1803
2058
  align-items: flex-start;
1804
2059
  }
@@ -80,7 +80,7 @@ test("GET /api/config returns structured editable settings payload", async () =>
80
80
  assert.match(body.config, /workspaceRoot/);
81
81
  assert.equal(body.restartRequired, true);
82
82
  assert.equal(body.secretsUiSupported, false);
83
- assert.equal(body.settings.configPath, path.join(minicodeHome, "agent.config.json"));
83
+ assert.equal(body.settings.configPath, path.join(minicodeHome, ".env"));
84
84
  const maxSteps = body.settings.entries.find((entry) => entry.key === "maxSteps");
85
85
  assert.equal(maxSteps?.type, "number");
86
86
  assert.equal(maxSteps?.envVar, "MAX_STEPS");
@@ -89,6 +89,11 @@ test("GET /api/config returns structured editable settings payload", async () =>
89
89
  assert.equal(maxSteps?.envValue, null);
90
90
  assert.equal(maxSteps?.overriddenByEnv, false);
91
91
  assert.match(maxSteps?.description ?? "", /Turn call limit/);
92
+ const modelTimeout = body.settings.entries.find((entry) => entry.key === "modelTimeoutSeconds");
93
+ assert.equal(modelTimeout?.type, "number");
94
+ assert.equal(modelTimeout?.envVar, "MODEL_TIMEOUT_SECONDS");
95
+ assert.equal(modelTimeout?.effectiveValue, 60);
96
+ assert.match(modelTimeout?.description ?? "", /start responding/);
92
97
  });
93
98
  });
94
99
  test("POST /api/config persists global settings and returns updated metadata", async () => {
@@ -111,16 +116,16 @@ test("POST /api/config persists global settings and returns updated metadata", a
111
116
  const body = await res.json();
112
117
  assert.equal(body.ok, true);
113
118
  assert.equal(body.scope, "global");
114
- assert.equal(body.path, path.join(minicodeHome, "agent.config.json"));
119
+ assert.equal(body.path, path.join(minicodeHome, ".env"));
115
120
  assert.equal(body.restartRequired, true);
116
121
  assert.match(body.message, /Persisted config updated/);
117
122
  assert.deepEqual(body.saved, [
118
123
  { key: "maxSteps", value: 42 },
119
124
  { key: "enableDynamicPrompt", value: false },
120
125
  ]);
121
- const persisted = JSON.parse(await readFile(path.join(minicodeHome, "agent.config.json"), "utf8"));
122
- assert.equal(persisted.maxSteps, 42);
123
- assert.equal(persisted.enableDynamicPrompt, false);
126
+ const persisted = await readFile(path.join(minicodeHome, ".env"), "utf8");
127
+ assert.match(persisted, /^MAX_STEPS=42$/m);
128
+ assert.match(persisted, /^ENABLE_DYNAMIC_PROMPT=false$/m);
124
129
  const maxSteps = body.settings.entries.find((entry) => entry.key === "maxSteps");
125
130
  assert.equal(maxSteps?.persistedValue, 42);
126
131
  });