reasonix 0.16.1 → 0.17.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.
- package/dashboard/app.css +148 -1
- package/dashboard/app.js +226 -0
- package/dist/cli/index.js +537 -280
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.js +89 -63
- package/dist/index.js.map +1 -1
- package/package.json +113 -110
package/dashboard/app.css
CHANGED
|
@@ -665,7 +665,8 @@ button:disabled {
|
|
|
665
665
|
}
|
|
666
666
|
|
|
667
667
|
input[type="text"],
|
|
668
|
-
input[type="search"]
|
|
668
|
+
input[type="search"],
|
|
669
|
+
input[type="number"] {
|
|
669
670
|
background: var(--bg-2);
|
|
670
671
|
border: 1px solid var(--border);
|
|
671
672
|
color: var(--fg-0);
|
|
@@ -678,6 +679,7 @@ input[type="search"] {
|
|
|
678
679
|
|
|
679
680
|
input[type="text"]:focus,
|
|
680
681
|
input[type="search"]:focus,
|
|
682
|
+
input[type="number"]:focus,
|
|
681
683
|
input[type="password"]:focus {
|
|
682
684
|
border-color: var(--primary);
|
|
683
685
|
outline: none;
|
|
@@ -712,7 +714,15 @@ select:focus {
|
|
|
712
714
|
textarea {
|
|
713
715
|
background: var(--bg-2);
|
|
714
716
|
color: var(--fg-0);
|
|
717
|
+
border: 1px solid var(--border);
|
|
718
|
+
border-radius: var(--radius-sm);
|
|
719
|
+
padding: 8px 10px;
|
|
715
720
|
font-family: var(--mono);
|
|
721
|
+
font-size: 12px;
|
|
722
|
+
line-height: 1.5;
|
|
723
|
+
resize: vertical;
|
|
724
|
+
width: 100%;
|
|
725
|
+
box-sizing: border-box;
|
|
716
726
|
}
|
|
717
727
|
textarea:focus {
|
|
718
728
|
border-color: var(--primary);
|
|
@@ -2538,3 +2548,140 @@ code,
|
|
|
2538
2548
|
border: 1px dashed var(--border);
|
|
2539
2549
|
border-radius: var(--radius-md);
|
|
2540
2550
|
}
|
|
2551
|
+
|
|
2552
|
+
/* ---------- Excludes settings (Semantic tab) ---------- */
|
|
2553
|
+
|
|
2554
|
+
.excludes-toggle {
|
|
2555
|
+
margin: 24px 0 12px;
|
|
2556
|
+
display: flex;
|
|
2557
|
+
align-items: baseline;
|
|
2558
|
+
gap: 10px;
|
|
2559
|
+
cursor: pointer;
|
|
2560
|
+
user-select: none;
|
|
2561
|
+
}
|
|
2562
|
+
.excludes-toggle:hover {
|
|
2563
|
+
color: var(--primary);
|
|
2564
|
+
}
|
|
2565
|
+
.excludes-toggle .caret {
|
|
2566
|
+
font-size: 11px;
|
|
2567
|
+
color: var(--fg-3);
|
|
2568
|
+
width: 10px;
|
|
2569
|
+
display: inline-block;
|
|
2570
|
+
}
|
|
2571
|
+
.excludes-toggle .label {
|
|
2572
|
+
font-size: 11px;
|
|
2573
|
+
letter-spacing: 0.1em;
|
|
2574
|
+
text-transform: uppercase;
|
|
2575
|
+
color: var(--fg-3);
|
|
2576
|
+
}
|
|
2577
|
+
.excludes-toggle:hover .label,
|
|
2578
|
+
.excludes-toggle:hover .caret {
|
|
2579
|
+
color: var(--primary);
|
|
2580
|
+
}
|
|
2581
|
+
.excludes-toggle .hint {
|
|
2582
|
+
font-size: 12px;
|
|
2583
|
+
color: var(--fg-3);
|
|
2584
|
+
font-weight: normal;
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
.excludes-card {
|
|
2588
|
+
font-size: 13px;
|
|
2589
|
+
display: flex;
|
|
2590
|
+
flex-direction: column;
|
|
2591
|
+
gap: 12px;
|
|
2592
|
+
}
|
|
2593
|
+
.excludes-card .lead {
|
|
2594
|
+
color: var(--fg-2);
|
|
2595
|
+
font-size: 12px;
|
|
2596
|
+
line-height: 1.5;
|
|
2597
|
+
}
|
|
2598
|
+
.excludes-grid {
|
|
2599
|
+
display: grid;
|
|
2600
|
+
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
|
2601
|
+
gap: 14px;
|
|
2602
|
+
}
|
|
2603
|
+
.excludes-field label {
|
|
2604
|
+
display: block;
|
|
2605
|
+
font-size: 12px;
|
|
2606
|
+
color: var(--fg-2);
|
|
2607
|
+
margin-bottom: 4px;
|
|
2608
|
+
font-weight: 500;
|
|
2609
|
+
}
|
|
2610
|
+
.excludes-field textarea {
|
|
2611
|
+
min-height: 90px;
|
|
2612
|
+
}
|
|
2613
|
+
.excludes-options {
|
|
2614
|
+
display: flex;
|
|
2615
|
+
flex-wrap: wrap;
|
|
2616
|
+
gap: 18px;
|
|
2617
|
+
align-items: center;
|
|
2618
|
+
padding-top: 4px;
|
|
2619
|
+
font-size: 12px;
|
|
2620
|
+
color: var(--fg-1);
|
|
2621
|
+
}
|
|
2622
|
+
.excludes-options label {
|
|
2623
|
+
display: inline-flex;
|
|
2624
|
+
align-items: center;
|
|
2625
|
+
gap: 6px;
|
|
2626
|
+
}
|
|
2627
|
+
.excludes-options input[type="number"] {
|
|
2628
|
+
width: 110px;
|
|
2629
|
+
}
|
|
2630
|
+
.excludes-actions {
|
|
2631
|
+
display: flex;
|
|
2632
|
+
gap: 10px;
|
|
2633
|
+
flex-wrap: wrap;
|
|
2634
|
+
margin-top: 4px;
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
.excludes-preview {
|
|
2638
|
+
margin-top: 4px;
|
|
2639
|
+
padding-top: 12px;
|
|
2640
|
+
border-top: 1px solid var(--border);
|
|
2641
|
+
font-size: 12px;
|
|
2642
|
+
display: flex;
|
|
2643
|
+
flex-direction: column;
|
|
2644
|
+
gap: 8px;
|
|
2645
|
+
}
|
|
2646
|
+
.excludes-preview .summary {
|
|
2647
|
+
font-size: 13px;
|
|
2648
|
+
color: var(--fg-1);
|
|
2649
|
+
}
|
|
2650
|
+
.excludes-preview details {
|
|
2651
|
+
background: var(--bg-2);
|
|
2652
|
+
border: 1px solid var(--border);
|
|
2653
|
+
border-radius: var(--radius-sm);
|
|
2654
|
+
padding: 6px 10px;
|
|
2655
|
+
}
|
|
2656
|
+
.excludes-preview details[open] {
|
|
2657
|
+
background: var(--bg-3);
|
|
2658
|
+
}
|
|
2659
|
+
.excludes-preview summary {
|
|
2660
|
+
cursor: pointer;
|
|
2661
|
+
color: var(--fg-1);
|
|
2662
|
+
font-weight: 500;
|
|
2663
|
+
}
|
|
2664
|
+
.excludes-preview summary:hover {
|
|
2665
|
+
color: var(--primary);
|
|
2666
|
+
}
|
|
2667
|
+
.excludes-preview ul {
|
|
2668
|
+
margin: 6px 0 0 16px;
|
|
2669
|
+
padding: 0;
|
|
2670
|
+
font-size: 11.5px;
|
|
2671
|
+
color: var(--fg-2);
|
|
2672
|
+
}
|
|
2673
|
+
.excludes-preview li {
|
|
2674
|
+
margin: 2px 0;
|
|
2675
|
+
}
|
|
2676
|
+
.excludes-preview li code {
|
|
2677
|
+
font-size: 11.5px;
|
|
2678
|
+
background: transparent;
|
|
2679
|
+
padding: 0;
|
|
2680
|
+
color: var(--fg-1);
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
.skip-buckets {
|
|
2684
|
+
margin-top: 4px;
|
|
2685
|
+
font-size: 12px;
|
|
2686
|
+
color: var(--fg-2);
|
|
2687
|
+
}
|
package/dashboard/app.js
CHANGED
|
@@ -3265,10 +3265,212 @@ function SemanticPanel() {
|
|
|
3265
3265
|
<button disabled=${busy || running || !ready} onClick=${() => start(true)}>Rebuild (wipe + full)</button>
|
|
3266
3266
|
<button disabled=${busy || !running} onClick=${stop}>Stop</button>
|
|
3267
3267
|
</div>
|
|
3268
|
+
|
|
3269
|
+
<${SemanticExcludesCard} />
|
|
3270
|
+
</div>
|
|
3271
|
+
`;
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
function SemanticExcludesCard() {
|
|
3275
|
+
const [data, setData] = useState(null);
|
|
3276
|
+
const [draft, setDraft] = useState(null);
|
|
3277
|
+
const [preview, setPreview] = useState(null);
|
|
3278
|
+
const [busy, setBusy] = useState(false);
|
|
3279
|
+
const [error, setError] = useState(null);
|
|
3280
|
+
const [info, setInfo] = useState(null);
|
|
3281
|
+
const [open, setOpen] = useState(false);
|
|
3282
|
+
|
|
3283
|
+
const load = useCallback(async () => {
|
|
3284
|
+
try {
|
|
3285
|
+
const r = await api("/index-config");
|
|
3286
|
+
setData(r);
|
|
3287
|
+
setDraft(toDraft(r.resolved));
|
|
3288
|
+
} catch (err) {
|
|
3289
|
+
setError(err.message);
|
|
3290
|
+
}
|
|
3291
|
+
}, []);
|
|
3292
|
+
|
|
3293
|
+
useEffect(() => {
|
|
3294
|
+
if (open && !data) load();
|
|
3295
|
+
}, [open, data, load]);
|
|
3296
|
+
|
|
3297
|
+
const reset = useCallback(() => {
|
|
3298
|
+
if (data) setDraft(toDraft(data.defaults));
|
|
3299
|
+
setPreview(null);
|
|
3300
|
+
}, [data]);
|
|
3301
|
+
|
|
3302
|
+
const save = useCallback(async () => {
|
|
3303
|
+
if (!draft) return;
|
|
3304
|
+
setBusy(true);
|
|
3305
|
+
setError(null);
|
|
3306
|
+
setInfo(null);
|
|
3307
|
+
try {
|
|
3308
|
+
const payload = fromDraft(draft);
|
|
3309
|
+
const r = await api("/index-config", { method: "POST", body: payload });
|
|
3310
|
+
setInfo(`saved · ${r.changed.length || 0} fields updated · re-run index to apply`);
|
|
3311
|
+
await load();
|
|
3312
|
+
} catch (err) {
|
|
3313
|
+
setError(err.message);
|
|
3314
|
+
} finally {
|
|
3315
|
+
setBusy(false);
|
|
3316
|
+
}
|
|
3317
|
+
}, [draft, load]);
|
|
3318
|
+
|
|
3319
|
+
const runPreview = useCallback(async () => {
|
|
3320
|
+
if (!draft) return;
|
|
3321
|
+
setBusy(true);
|
|
3322
|
+
setError(null);
|
|
3323
|
+
setInfo("running dry walk against project root…");
|
|
3324
|
+
try {
|
|
3325
|
+
const payload = fromDraft(draft);
|
|
3326
|
+
const r = await api("/index-config/preview", { method: "POST", body: payload });
|
|
3327
|
+
setPreview(r);
|
|
3328
|
+
setInfo(null);
|
|
3329
|
+
} catch (err) {
|
|
3330
|
+
setError(err.message);
|
|
3331
|
+
setInfo(null);
|
|
3332
|
+
} finally {
|
|
3333
|
+
setBusy(false);
|
|
3334
|
+
}
|
|
3335
|
+
}, [draft]);
|
|
3336
|
+
|
|
3337
|
+
return html`
|
|
3338
|
+
<div class="excludes-toggle" onClick=${() => setOpen(!open)}>
|
|
3339
|
+
<span class="caret">${open ? "▼" : "▶"}</span>
|
|
3340
|
+
<span class="label">Excludes</span>
|
|
3341
|
+
<span class="hint">config-driven skip rules applied during indexing</span>
|
|
3268
3342
|
</div>
|
|
3343
|
+
${
|
|
3344
|
+
!open
|
|
3345
|
+
? null
|
|
3346
|
+
: !draft
|
|
3347
|
+
? html`<div class="empty">loading…</div>`
|
|
3348
|
+
: html`
|
|
3349
|
+
<div class="card excludes-card">
|
|
3350
|
+
${info ? html`<div class="notice">${info}</div>` : null}
|
|
3351
|
+
${error ? html`<div class="notice err">${error}</div>` : null}
|
|
3352
|
+
<div class="lead">
|
|
3353
|
+
One value per line. Dirs / files match by basename. Patterns use picomatch syntax (e.g. <code>**/*.generated.ts</code>, <code>vendor/**</code>, <code>!keep-me</code>).
|
|
3354
|
+
</div>
|
|
3355
|
+
<div class="excludes-grid">
|
|
3356
|
+
<${ExcludesField} label="Exclude dirs" value=${draft.excludeDirs} onChange=${(v) => setDraft({ ...draft, excludeDirs: v })} />
|
|
3357
|
+
<${ExcludesField} label="Exclude files" value=${draft.excludeFiles} onChange=${(v) => setDraft({ ...draft, excludeFiles: v })} />
|
|
3358
|
+
<${ExcludesField} label="Exclude extensions" value=${draft.excludeExts} onChange=${(v) => setDraft({ ...draft, excludeExts: v })} />
|
|
3359
|
+
<${ExcludesField} label="Exclude patterns (glob)" value=${draft.excludePatterns} onChange=${(v) => setDraft({ ...draft, excludePatterns: v })} />
|
|
3360
|
+
</div>
|
|
3361
|
+
<div class="excludes-options">
|
|
3362
|
+
<label>
|
|
3363
|
+
<input type="checkbox" checked=${draft.respectGitignore} onChange=${(e) => setDraft({ ...draft, respectGitignore: e.target.checked })} />
|
|
3364
|
+
Respect <code>.gitignore</code>
|
|
3365
|
+
</label>
|
|
3366
|
+
<label>
|
|
3367
|
+
Max file size
|
|
3368
|
+
<input type="number" min="1024" step="1024" value=${draft.maxFileBytes} onChange=${(e) => setDraft({ ...draft, maxFileBytes: Number(e.target.value) || 0 })} />
|
|
3369
|
+
<span class="muted">bytes</span>
|
|
3370
|
+
</label>
|
|
3371
|
+
</div>
|
|
3372
|
+
<div class="excludes-actions">
|
|
3373
|
+
<button class="primary" disabled=${busy} onClick=${save}>Save</button>
|
|
3374
|
+
<button disabled=${busy} onClick=${runPreview}>Preview (dry-walk)</button>
|
|
3375
|
+
<button disabled=${busy} onClick=${reset}>Reset to defaults</button>
|
|
3376
|
+
</div>
|
|
3377
|
+
${preview ? html`<${ExcludesPreview} preview=${preview} />` : null}
|
|
3378
|
+
</div>
|
|
3379
|
+
`
|
|
3380
|
+
}
|
|
3269
3381
|
`;
|
|
3270
3382
|
}
|
|
3271
3383
|
|
|
3384
|
+
function ExcludesPreview({ preview }) {
|
|
3385
|
+
const buckets = preview.skipBuckets || {};
|
|
3386
|
+
const samples = preview.skipSamples || {};
|
|
3387
|
+
const totalSkipped = Object.values(buckets).reduce((a, b) => a + (b || 0), 0);
|
|
3388
|
+
const reasons = [
|
|
3389
|
+
"gitignore",
|
|
3390
|
+
"pattern",
|
|
3391
|
+
"defaultDir",
|
|
3392
|
+
"defaultFile",
|
|
3393
|
+
"binaryExt",
|
|
3394
|
+
"binaryContent",
|
|
3395
|
+
"tooLarge",
|
|
3396
|
+
"readError",
|
|
3397
|
+
].filter((k) => (buckets[k] || 0) > 0);
|
|
3398
|
+
return html`
|
|
3399
|
+
<div class="excludes-preview">
|
|
3400
|
+
<div class="summary">
|
|
3401
|
+
Preview — would index <strong>${preview.filesIncluded}</strong> file(s), skip <strong>${totalSkipped}</strong>
|
|
3402
|
+
</div>
|
|
3403
|
+
${
|
|
3404
|
+
reasons.length === 0
|
|
3405
|
+
? html`<div class="muted">nothing skipped — all walked files would be indexed.</div>`
|
|
3406
|
+
: reasons.map(
|
|
3407
|
+
(r) => html`
|
|
3408
|
+
<details>
|
|
3409
|
+
<summary><strong>${r}: ${buckets[r]}</strong></summary>
|
|
3410
|
+
<ul>
|
|
3411
|
+
${(samples[r] || []).map((p) => html`<li><code>${p}</code></li>`)}
|
|
3412
|
+
${
|
|
3413
|
+
(buckets[r] || 0) > (samples[r] || []).length
|
|
3414
|
+
? html`<li class="muted">…${buckets[r] - samples[r].length} more</li>`
|
|
3415
|
+
: null
|
|
3416
|
+
}
|
|
3417
|
+
</ul>
|
|
3418
|
+
</details>
|
|
3419
|
+
`,
|
|
3420
|
+
)
|
|
3421
|
+
}
|
|
3422
|
+
${
|
|
3423
|
+
preview.sampleIncluded?.length
|
|
3424
|
+
? html`
|
|
3425
|
+
<details>
|
|
3426
|
+
<summary>first ${preview.sampleIncluded.length} included file(s)</summary>
|
|
3427
|
+
<ul>
|
|
3428
|
+
${preview.sampleIncluded.map((p) => html`<li><code>${p}</code></li>`)}
|
|
3429
|
+
</ul>
|
|
3430
|
+
</details>
|
|
3431
|
+
`
|
|
3432
|
+
: null
|
|
3433
|
+
}
|
|
3434
|
+
</div>
|
|
3435
|
+
`;
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
function ExcludesField({ label, value, onChange }) {
|
|
3439
|
+
return html`
|
|
3440
|
+
<div class="excludes-field">
|
|
3441
|
+
<label>${label}</label>
|
|
3442
|
+
<textarea rows="5" value=${value} onChange=${(e) => onChange(e.target.value)}></textarea>
|
|
3443
|
+
</div>
|
|
3444
|
+
`;
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3447
|
+
function toDraft(c) {
|
|
3448
|
+
return {
|
|
3449
|
+
excludeDirs: (c.excludeDirs ?? []).join("\n"),
|
|
3450
|
+
excludeFiles: (c.excludeFiles ?? []).join("\n"),
|
|
3451
|
+
excludeExts: (c.excludeExts ?? []).join("\n"),
|
|
3452
|
+
excludePatterns: (c.excludePatterns ?? []).join("\n"),
|
|
3453
|
+
respectGitignore: c.respectGitignore !== false,
|
|
3454
|
+
maxFileBytes: c.maxFileBytes ?? 262144,
|
|
3455
|
+
};
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3458
|
+
function fromDraft(d) {
|
|
3459
|
+
const lines = (s) =>
|
|
3460
|
+
s
|
|
3461
|
+
.split(/\r?\n/)
|
|
3462
|
+
.map((x) => x.trim())
|
|
3463
|
+
.filter((x) => x.length > 0);
|
|
3464
|
+
return {
|
|
3465
|
+
excludeDirs: lines(d.excludeDirs),
|
|
3466
|
+
excludeFiles: lines(d.excludeFiles),
|
|
3467
|
+
excludeExts: lines(d.excludeExts),
|
|
3468
|
+
excludePatterns: lines(d.excludePatterns),
|
|
3469
|
+
respectGitignore: !!d.respectGitignore,
|
|
3470
|
+
maxFileBytes: d.maxFileBytes,
|
|
3471
|
+
};
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3272
3474
|
function SemanticJobView({ job, running }) {
|
|
3273
3475
|
const phaseLabel =
|
|
3274
3476
|
{
|
|
@@ -3321,10 +3523,34 @@ function SemanticJobView({ job, running }) {
|
|
|
3321
3523
|
} · ${(job.result.durationMs / 1000).toFixed(1)}s</div>`
|
|
3322
3524
|
: null
|
|
3323
3525
|
}
|
|
3526
|
+
${
|
|
3527
|
+
job.result?.skipBuckets
|
|
3528
|
+
? html`<${SkipBucketsView} buckets=${job.result.skipBuckets} />`
|
|
3529
|
+
: null
|
|
3530
|
+
}
|
|
3324
3531
|
</div>
|
|
3325
3532
|
`;
|
|
3326
3533
|
}
|
|
3327
3534
|
|
|
3535
|
+
function SkipBucketsView({ buckets }) {
|
|
3536
|
+
const order = [
|
|
3537
|
+
["gitignore", "gitignore"],
|
|
3538
|
+
["pattern", "pattern"],
|
|
3539
|
+
["defaultDir", "defaultDir"],
|
|
3540
|
+
["defaultFile", "defaultFile"],
|
|
3541
|
+
["binaryExt", "binaryExt"],
|
|
3542
|
+
["binaryContent", "binaryContent"],
|
|
3543
|
+
["tooLarge", "tooLarge"],
|
|
3544
|
+
["readError", "readError"],
|
|
3545
|
+
];
|
|
3546
|
+
const total = order.reduce((a, [k]) => a + (buckets[k] || 0), 0);
|
|
3547
|
+
if (total === 0) return null;
|
|
3548
|
+
const parts = order
|
|
3549
|
+
.filter(([k]) => (buckets[k] || 0) > 0)
|
|
3550
|
+
.map(([k, label]) => `${label}: ${buckets[k]}`);
|
|
3551
|
+
return html`<div><span class="kv-key">skipped</span>${total} files <span class="muted">(${parts.join(", ")})</span></div>`;
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3328
3554
|
function McpPanel() {
|
|
3329
3555
|
const [data, setData] = useState(null);
|
|
3330
3556
|
const [specs, setSpecs] = useState(null);
|