@sean.holung/minicode 0.3.5 → 0.3.6

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.
@@ -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,49 @@ 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
+
176
238
  <script type="module" src="app.js"></script>
177
239
  </body>
178
240
  </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,10 @@ footer {
773
858
  overflow: hidden;
774
859
  }
775
860
 
861
+ .modal-panel-compact {
862
+ width: min(640px, calc(100vw - 32px));
863
+ }
864
+
776
865
  .modal-header,
777
866
  .modal-footer,
778
867
  .settings-toolbar {
@@ -1009,6 +1098,35 @@ footer {
1009
1098
  gap: 8px;
1010
1099
  }
1011
1100
 
1101
+ .settings-session-banner {
1102
+ display: flex;
1103
+ align-items: center;
1104
+ justify-content: space-between;
1105
+ gap: 14px;
1106
+ margin: 0 0 14px;
1107
+ padding: 12px 14px;
1108
+ border-radius: 10px;
1109
+ border: 1px solid rgba(122, 162, 247, 0.24);
1110
+ background: rgba(122, 162, 247, 0.09);
1111
+ }
1112
+
1113
+ .settings-session-copy {
1114
+ min-width: 0;
1115
+ }
1116
+
1117
+ .settings-session-title {
1118
+ font-size: 13px;
1119
+ color: var(--text);
1120
+ font-weight: 600;
1121
+ margin-bottom: 4px;
1122
+ }
1123
+
1124
+ .settings-session-meta {
1125
+ font-size: 12px;
1126
+ color: var(--text-dim);
1127
+ overflow-wrap: anywhere;
1128
+ }
1129
+
1012
1130
  .settings-help {
1013
1131
  font-size: 11px;
1014
1132
  color: var(--text-dim);
@@ -1040,6 +1158,65 @@ footer {
1040
1158
  flex-shrink: 0;
1041
1159
  }
1042
1160
 
1161
+ .openrouter-connect-body {
1162
+ padding: 20px;
1163
+ display: flex;
1164
+ flex-direction: column;
1165
+ gap: 14px;
1166
+ }
1167
+
1168
+ .openrouter-connect-lead {
1169
+ font-size: 13px;
1170
+ color: var(--text);
1171
+ }
1172
+
1173
+ .openrouter-connect-note {
1174
+ padding: 12px 14px;
1175
+ border-radius: 10px;
1176
+ border: 1px solid rgba(122, 162, 247, 0.22);
1177
+ background: rgba(122, 162, 247, 0.08);
1178
+ }
1179
+
1180
+ .openrouter-connect-note strong {
1181
+ display: block;
1182
+ color: var(--text);
1183
+ font-size: 13px;
1184
+ margin-bottom: 4px;
1185
+ }
1186
+
1187
+ .openrouter-connect-note p,
1188
+ .openrouter-connect-help {
1189
+ font-size: 12px;
1190
+ color: var(--text-dim);
1191
+ }
1192
+
1193
+ .openrouter-persist-toggle {
1194
+ display: flex;
1195
+ align-items: flex-start;
1196
+ gap: 10px;
1197
+ padding: 12px 14px;
1198
+ border-radius: 10px;
1199
+ border: 1px solid var(--border);
1200
+ background: rgba(26, 27, 38, 0.72);
1201
+ cursor: pointer;
1202
+ }
1203
+
1204
+ .openrouter-persist-toggle input {
1205
+ margin-top: 2px;
1206
+ }
1207
+
1208
+ .openrouter-persist-toggle span {
1209
+ font-size: 12px;
1210
+ color: var(--text);
1211
+ }
1212
+
1213
+ @media (max-width: 760px) {
1214
+ .settings-session-banner {
1215
+ flex-direction: column;
1216
+ align-items: flex-start;
1217
+ }
1218
+ }
1219
+
1043
1220
  .settings-list::-webkit-scrollbar,
1044
1221
  .settings-path::-webkit-scrollbar {
1045
1222
  width: 6px;
@@ -1126,6 +1303,7 @@ main::-webkit-scrollbar-thumb {
1126
1303
  display: flex;
1127
1304
  align-items: center;
1128
1305
  justify-content: space-between;
1306
+ gap: 12px;
1129
1307
  padding: 6px 10px;
1130
1308
  cursor: pointer;
1131
1309
  font-size: 12px;
@@ -1136,14 +1314,39 @@ main::-webkit-scrollbar-thumb {
1136
1314
  background: var(--bg-hover);
1137
1315
  }
1138
1316
 
1317
+ .search-result-body {
1318
+ display: flex;
1319
+ flex: 1;
1320
+ flex-direction: column;
1321
+ min-width: 0;
1322
+ }
1323
+
1139
1324
  .search-result-name {
1140
1325
  color: var(--text);
1141
1326
  font-weight: 500;
1327
+ overflow: hidden;
1328
+ text-overflow: ellipsis;
1329
+ white-space: nowrap;
1330
+ }
1331
+
1332
+ .search-result-subtitle {
1333
+ color: var(--text-dim);
1334
+ font-size: 11px;
1335
+ overflow: hidden;
1336
+ text-overflow: ellipsis;
1337
+ white-space: nowrap;
1142
1338
  }
1143
1339
 
1144
1340
  .search-result-kind {
1145
1341
  font-size: 11px;
1146
1342
  opacity: 0.7;
1343
+ flex-shrink: 0;
1344
+ letter-spacing: 0.04em;
1345
+ text-transform: uppercase;
1346
+ }
1347
+
1348
+ .search-result-kind.file {
1349
+ opacity: 0.9;
1147
1350
  }
1148
1351
 
1149
1352
  #cy {
@@ -1799,6 +2002,10 @@ main::-webkit-scrollbar-thumb {
1799
2002
  align-items: stretch;
1800
2003
  }
1801
2004
 
2005
+ .openrouter-persist-toggle {
2006
+ align-items: flex-start;
2007
+ }
2008
+
1802
2009
  .settings-path-group {
1803
2010
  align-items: flex-start;
1804
2011
  }
@@ -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");
@@ -111,16 +111,16 @@ test("POST /api/config persists global settings and returns updated metadata", a
111
111
  const body = await res.json();
112
112
  assert.equal(body.ok, true);
113
113
  assert.equal(body.scope, "global");
114
- assert.equal(body.path, path.join(minicodeHome, "agent.config.json"));
114
+ assert.equal(body.path, path.join(minicodeHome, ".env"));
115
115
  assert.equal(body.restartRequired, true);
116
116
  assert.match(body.message, /Persisted config updated/);
117
117
  assert.deepEqual(body.saved, [
118
118
  { key: "maxSteps", value: 42 },
119
119
  { key: "enableDynamicPrompt", value: false },
120
120
  ]);
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);
121
+ const persisted = await readFile(path.join(minicodeHome, ".env"), "utf8");
122
+ assert.match(persisted, /^MAX_STEPS=42$/m);
123
+ assert.match(persisted, /^ENABLE_DYNAMIC_PROMPT=false$/m);
124
124
  const maxSteps = body.settings.entries.find((entry) => entry.key === "maxSteps");
125
125
  assert.equal(maxSteps?.persistedValue, 42);
126
126
  });