@codyswann/lisa 2.235.0 → 2.237.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/dist/cli/check-learnings-budget-cmd.d.ts +20 -0
- package/dist/cli/check-learnings-budget-cmd.d.ts.map +1 -0
- package/dist/cli/check-learnings-budget-cmd.js +59 -0
- package/dist/cli/check-learnings-budget-cmd.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +29 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/ui-automations-adapters.d.ts +50 -0
- package/dist/cli/ui-automations-adapters.d.ts.map +1 -0
- package/dist/cli/ui-automations-adapters.js +162 -0
- package/dist/cli/ui-automations-adapters.js.map +1 -0
- package/dist/cli/ui-automations.d.ts +89 -0
- package/dist/cli/ui-automations.d.ts.map +1 -0
- package/dist/cli/ui-automations.js +148 -0
- package/dist/cli/ui-automations.js.map +1 -0
- package/dist/cli/ui-cmd.d.ts +5 -0
- package/dist/cli/ui-cmd.d.ts.map +1 -1
- package/dist/cli/ui-cmd.js +17 -0
- package/dist/cli/ui-cmd.js.map +1 -1
- package/dist/cli/ui-deploy-pipeline-model.d.ts +70 -0
- package/dist/cli/ui-deploy-pipeline-model.d.ts.map +1 -0
- package/dist/cli/ui-deploy-pipeline-model.js +127 -0
- package/dist/cli/ui-deploy-pipeline-model.js.map +1 -0
- package/dist/cli/ui-deploy-pipeline.d.ts +25 -0
- package/dist/cli/ui-deploy-pipeline.d.ts.map +1 -0
- package/dist/cli/ui-deploy-pipeline.js +192 -0
- package/dist/cli/ui-deploy-pipeline.js.map +1 -0
- package/dist/cli/ui-detected-stacks.d.ts +20 -0
- package/dist/cli/ui-detected-stacks.d.ts.map +1 -0
- package/dist/cli/ui-detected-stacks.js +28 -0
- package/dist/cli/ui-detected-stacks.js.map +1 -0
- package/dist/cli/ui-enabled-plugins.d.ts +52 -0
- package/dist/cli/ui-enabled-plugins.d.ts.map +1 -0
- package/dist/cli/ui-enabled-plugins.js +253 -0
- package/dist/cli/ui-enabled-plugins.js.map +1 -0
- package/dist/cli/ui-github-repo-expected.d.ts +36 -0
- package/dist/cli/ui-github-repo-expected.d.ts.map +1 -0
- package/dist/cli/ui-github-repo-expected.js +118 -0
- package/dist/cli/ui-github-repo-expected.js.map +1 -0
- package/dist/cli/ui-github-repo-gh.d.ts +36 -0
- package/dist/cli/ui-github-repo-gh.d.ts.map +1 -0
- package/dist/cli/ui-github-repo-gh.js +112 -0
- package/dist/cli/ui-github-repo-gh.js.map +1 -0
- package/dist/cli/ui-github-repo-map.d.ts +25 -0
- package/dist/cli/ui-github-repo-map.d.ts.map +1 -0
- package/dist/cli/ui-github-repo-map.js +150 -0
- package/dist/cli/ui-github-repo-map.js.map +1 -0
- package/dist/cli/ui-github-repo.d.ts +51 -0
- package/dist/cli/ui-github-repo.d.ts.map +1 -0
- package/dist/cli/ui-github-repo.js +117 -0
- package/dist/cli/ui-github-repo.js.map +1 -0
- package/dist/cli/ui-observability-checks.d.ts +23 -0
- package/dist/cli/ui-observability-checks.d.ts.map +1 -0
- package/dist/cli/ui-observability-checks.js +156 -0
- package/dist/cli/ui-observability-checks.js.map +1 -0
- package/dist/cli/ui-observability-providers.d.ts +69 -0
- package/dist/cli/ui-observability-providers.d.ts.map +1 -0
- package/dist/cli/ui-observability-providers.js +99 -0
- package/dist/cli/ui-observability-providers.js.map +1 -0
- package/dist/core/learnings-budget-check.d.ts +42 -0
- package/dist/core/learnings-budget-check.d.ts.map +1 -0
- package/dist/core/learnings-budget-check.js +210 -0
- package/dist/core/learnings-budget-check.js.map +1 -0
- package/dist/core/learnings-document.js +6 -1
- package/dist/core/learnings-document.js.map +1 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/scripts/check-learnings-budget.ts +29 -163
- package/ui/README.md +5 -3
- package/ui/index.html +1097 -432
package/ui/index.html
CHANGED
|
@@ -326,6 +326,29 @@
|
|
|
326
326
|
font-family: var(--mono);
|
|
327
327
|
font-size: 11px;
|
|
328
328
|
}
|
|
329
|
+
.plugins-state {
|
|
330
|
+
padding: 18px 20px;
|
|
331
|
+
border: 1px dashed var(--line);
|
|
332
|
+
border-radius: 10px;
|
|
333
|
+
background: var(--surface2);
|
|
334
|
+
color: var(--muted);
|
|
335
|
+
font-size: 13px;
|
|
336
|
+
line-height: 1.45;
|
|
337
|
+
}
|
|
338
|
+
.plugins-state.unknown {
|
|
339
|
+
border-style: solid;
|
|
340
|
+
border-color: var(--warn, #c9872a);
|
|
341
|
+
}
|
|
342
|
+
.plugins-state.unknown b {
|
|
343
|
+
display: block;
|
|
344
|
+
color: var(--text);
|
|
345
|
+
margin-bottom: 4px;
|
|
346
|
+
}
|
|
347
|
+
.plugins-live-block .plugin-status-badge.available {
|
|
348
|
+
background: transparent;
|
|
349
|
+
border: 1px solid var(--line);
|
|
350
|
+
color: var(--muted);
|
|
351
|
+
}
|
|
329
352
|
.live-status-item .status-why {
|
|
330
353
|
display: inline;
|
|
331
354
|
margin: 0;
|
|
@@ -854,6 +877,23 @@
|
|
|
854
877
|
grid-template-columns: 1fr;
|
|
855
878
|
}
|
|
856
879
|
}
|
|
880
|
+
.stacks-state {
|
|
881
|
+
border: 1px dashed var(--line);
|
|
882
|
+
border-radius: 10px;
|
|
883
|
+
background: var(--surface);
|
|
884
|
+
padding: 16px 18px;
|
|
885
|
+
color: var(--muted);
|
|
886
|
+
display: flex;
|
|
887
|
+
flex-direction: column;
|
|
888
|
+
gap: 4px;
|
|
889
|
+
}
|
|
890
|
+
.stacks-state.unknown {
|
|
891
|
+
border-color: var(--warn, #c99a2e);
|
|
892
|
+
border-style: solid;
|
|
893
|
+
}
|
|
894
|
+
.stacks-state.unknown b {
|
|
895
|
+
color: var(--ink);
|
|
896
|
+
}
|
|
857
897
|
.stack-card {
|
|
858
898
|
border: 1px solid var(--line);
|
|
859
899
|
border-radius: 10px;
|
|
@@ -3134,77 +3174,22 @@
|
|
|
3134
3174
|
},
|
|
3135
3175
|
{
|
|
3136
3176
|
type: "table",
|
|
3177
|
+
id: "deploy-pipeline-stages",
|
|
3137
3178
|
card: "Deploy pipeline stages",
|
|
3138
|
-
note: "
|
|
3179
|
+
note: "Live from deploy.yml + GitHub environment protection rules — never fabricated",
|
|
3139
3180
|
cols: ["Stage", "What it does", "Active"],
|
|
3140
3181
|
rows: [
|
|
3141
3182
|
[
|
|
3142
|
-
{ t: "mono", v: "
|
|
3143
|
-
{
|
|
3144
|
-
t: "text",
|
|
3145
|
-
v: "Builds the artifacts for the environment mapped to the pushed branch",
|
|
3146
|
-
},
|
|
3147
|
-
{ t: "status", v: true },
|
|
3148
|
-
],
|
|
3149
|
-
[
|
|
3150
|
-
{ t: "mono", v: "🚦 Release approval — production" },
|
|
3151
|
-
{
|
|
3152
|
-
t: "text",
|
|
3153
|
-
v: "Pauses at the release_approval job until a configured reviewer approves the GitHub environment",
|
|
3154
|
-
},
|
|
3155
|
-
{ t: "status", v: true },
|
|
3156
|
-
],
|
|
3157
|
-
[
|
|
3158
|
-
{ t: "mono", v: "🚦 Release approval — staging" },
|
|
3183
|
+
{ t: "mono", v: "…" },
|
|
3159
3184
|
{
|
|
3160
3185
|
t: "text",
|
|
3161
|
-
v: "
|
|
3186
|
+
v: "Loading live deploy pipeline stages…",
|
|
3162
3187
|
},
|
|
3163
3188
|
{
|
|
3164
3189
|
t: "status",
|
|
3165
|
-
v:
|
|
3166
|
-
reason: "
|
|
3167
|
-
},
|
|
3168
|
-
],
|
|
3169
|
-
[
|
|
3170
|
-
{ t: "mono", v: "🚀 Deploy" },
|
|
3171
|
-
{
|
|
3172
|
-
t: "text",
|
|
3173
|
-
v: "Runs the stack’s deploy (EAS / Serverless / CDK / Fabric) against the target environment",
|
|
3174
|
-
},
|
|
3175
|
-
{ t: "status", v: true },
|
|
3176
|
-
],
|
|
3177
|
-
[
|
|
3178
|
-
{ t: "mono", v: "📝 Version bump & changelog" },
|
|
3179
|
-
{
|
|
3180
|
-
t: "text",
|
|
3181
|
-
v: "standard-version tags the release and pushes the chore(release) commit through the deploy key’s ruleset bypass",
|
|
3182
|
-
},
|
|
3183
|
-
{ t: "status", v: true },
|
|
3184
|
-
],
|
|
3185
|
-
[
|
|
3186
|
-
{ t: "mono", v: "🔄 Sync-down branches" },
|
|
3187
|
-
{
|
|
3188
|
-
t: "text",
|
|
3189
|
-
v: "After a promotion merges, back-merges higher environment branches into lower ones",
|
|
3190
|
-
},
|
|
3191
|
-
{ t: "status", v: true },
|
|
3192
|
-
],
|
|
3193
|
-
[
|
|
3194
|
-
{ t: "mono", v: "🧯 Deploy auto-fix" },
|
|
3195
|
-
{
|
|
3196
|
-
t: "text",
|
|
3197
|
-
v: "On a failed deploy, a Claude workflow investigates and opens a fix PR",
|
|
3198
|
-
},
|
|
3199
|
-
{ t: "status", v: true },
|
|
3200
|
-
],
|
|
3201
|
-
[
|
|
3202
|
-
{ t: "mono", v: "🎫 Issue on failure" },
|
|
3203
|
-
{
|
|
3204
|
-
t: "text",
|
|
3205
|
-
v: "Files a tracker issue when a deploy run fails",
|
|
3190
|
+
v: "unknown",
|
|
3191
|
+
reason: "waiting for deploy-pipeline-stages probe",
|
|
3206
3192
|
},
|
|
3207
|
-
{ t: "status", v: true },
|
|
3208
3193
|
],
|
|
3209
3194
|
],
|
|
3210
3195
|
},
|
|
@@ -3392,183 +3377,14 @@
|
|
|
3392
3377
|
{
|
|
3393
3378
|
type: "table",
|
|
3394
3379
|
card: "Scheduled jobs",
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
"
|
|
3401
|
-
"
|
|
3402
|
-
|
|
3403
|
-
"Enabled",
|
|
3404
|
-
],
|
|
3405
|
-
rows: [
|
|
3406
|
-
[
|
|
3407
|
-
{ t: "mono", v: "intake-tickets" },
|
|
3408
|
-
{
|
|
3409
|
-
t: "text",
|
|
3410
|
-
v: "Claims the next build-ready ticket and runs the full build flow on it \u2014 one item per cycle",
|
|
3411
|
-
},
|
|
3412
|
-
{ t: "badge", v: "harness", cls: "default" },
|
|
3413
|
-
{
|
|
3414
|
-
t: "select",
|
|
3415
|
-
v: "every 10 min",
|
|
3416
|
-
options: ["every 10 min", "every 30 min", "every 60 min"],
|
|
3417
|
-
},
|
|
3418
|
-
{
|
|
3419
|
-
t: "run",
|
|
3420
|
-
ok: true,
|
|
3421
|
-
v: "12:50",
|
|
3422
|
-
reason: "ok \u2014 queue empty, exited clean",
|
|
3423
|
-
},
|
|
3424
|
-
{ t: "mono", v: "13:00" },
|
|
3425
|
-
{ t: "toggle", v: true },
|
|
3426
|
-
],
|
|
3427
|
-
[
|
|
3428
|
-
{ t: "mono", v: "intake-prd" },
|
|
3429
|
-
{
|
|
3430
|
-
t: "text",
|
|
3431
|
-
v: "Validates the next ready PRD at the gate and either creates its work units or blocks it with clarifying questions",
|
|
3432
|
-
},
|
|
3433
|
-
{ t: "badge", v: "harness", cls: "default" },
|
|
3434
|
-
{
|
|
3435
|
-
t: "select",
|
|
3436
|
-
v: "every 60 min",
|
|
3437
|
-
options: ["every 10 min", "every 30 min", "every 60 min"],
|
|
3438
|
-
},
|
|
3439
|
-
{
|
|
3440
|
-
t: "run",
|
|
3441
|
-
ok: true,
|
|
3442
|
-
v: "12:04",
|
|
3443
|
-
reason: "ok \u2014 1 PRD ticketed",
|
|
3444
|
-
},
|
|
3445
|
-
{ t: "mono", v: "13:04" },
|
|
3446
|
-
{ t: "toggle", v: true },
|
|
3447
|
-
],
|
|
3448
|
-
[
|
|
3449
|
-
{ t: "mono", v: "intake-repair" },
|
|
3450
|
-
{
|
|
3451
|
-
t: "text",
|
|
3452
|
-
v: "Finds stuck work \u2014 stalled builds, half-closed items, unreconciled rollups \u2014 and repairs each one",
|
|
3453
|
-
},
|
|
3454
|
-
{ t: "badge", v: "harness", cls: "default" },
|
|
3455
|
-
{
|
|
3456
|
-
t: "select",
|
|
3457
|
-
v: "every 60 min",
|
|
3458
|
-
options: ["every 30 min", "every 60 min", "every 3 h"],
|
|
3459
|
-
},
|
|
3460
|
-
{
|
|
3461
|
-
t: "run",
|
|
3462
|
-
ok: true,
|
|
3463
|
-
v: "12:04",
|
|
3464
|
-
reason: "ok \u2014 0 stuck items",
|
|
3465
|
-
},
|
|
3466
|
-
{ t: "mono", v: "13:04" },
|
|
3467
|
-
{ t: "toggle", v: true },
|
|
3468
|
-
],
|
|
3469
|
-
[
|
|
3470
|
-
{ t: "mono", v: "exploratory-bugs" },
|
|
3471
|
-
{
|
|
3472
|
-
t: "text",
|
|
3473
|
-
v: "QA loop: explores the product like a first-time user (respecting the mutation policy) and files bugs build-ready",
|
|
3474
|
-
},
|
|
3475
|
-
{ t: "badge", v: "harness", cls: "default" },
|
|
3476
|
-
{ t: "select", v: "daily", options: ["daily", "weekly"] },
|
|
3477
|
-
{
|
|
3478
|
-
t: "run",
|
|
3479
|
-
ok: true,
|
|
3480
|
-
v: "04:00",
|
|
3481
|
-
reason: "ok \u2014 2 bugs filed",
|
|
3482
|
-
},
|
|
3483
|
-
{ t: "mono", v: "tomorrow 04:00" },
|
|
3484
|
-
{ t: "toggle", v: true },
|
|
3485
|
-
],
|
|
3486
|
-
[
|
|
3487
|
-
{ t: "mono", v: "exploratory-prds" },
|
|
3488
|
-
{
|
|
3489
|
-
t: "text",
|
|
3490
|
-
v: "Product Planning loop: ideates improvements and writes PRDs prd-ready",
|
|
3491
|
-
},
|
|
3492
|
-
{ t: "badge", v: "harness", cls: "default" },
|
|
3493
|
-
{ t: "select", v: "daily", options: ["daily", "weekly"] },
|
|
3494
|
-
{
|
|
3495
|
-
t: "run",
|
|
3496
|
-
ok: "warn",
|
|
3497
|
-
v: "04:15",
|
|
3498
|
-
reason: "skipped \u2014 queue pressure (1 prd-ready waiting)",
|
|
3499
|
-
},
|
|
3500
|
-
{ t: "mono", v: "tomorrow 04:15" },
|
|
3501
|
-
{ t: "toggle", v: true },
|
|
3502
|
-
],
|
|
3503
|
-
[
|
|
3504
|
-
{ t: "mono", v: "monitor" },
|
|
3505
|
-
{
|
|
3506
|
-
t: "text",
|
|
3507
|
-
v: "Monitoring loop: audits the connected observability providers against the alert thresholds and files regression tickets",
|
|
3508
|
-
},
|
|
3509
|
-
{ t: "badge", v: "harness", cls: "default" },
|
|
3510
|
-
{ t: "select", v: "daily", options: ["daily", "weekly"] },
|
|
3511
|
-
{
|
|
3512
|
-
t: "run",
|
|
3513
|
-
ok: true,
|
|
3514
|
-
v: "05:00",
|
|
3515
|
-
reason: "ok \u2014 1 regression filed",
|
|
3516
|
-
},
|
|
3517
|
-
{ t: "mono", v: "tomorrow 05:00" },
|
|
3518
|
-
{ t: "toggle", v: true },
|
|
3519
|
-
],
|
|
3520
|
-
[
|
|
3521
|
-
{ t: "mono", v: "nightly-test-coverage" },
|
|
3522
|
-
{
|
|
3523
|
-
t: "text",
|
|
3524
|
-
v: "Raises test coverage toward the thresholds and opens a PR \u2014 one module per run",
|
|
3525
|
-
},
|
|
3526
|
-
{ t: "badge", v: "pipeline host", cls: "default" },
|
|
3527
|
-
{ t: "mono", v: "03:00 Mon\u2013Fri" },
|
|
3528
|
-
{
|
|
3529
|
-
t: "run",
|
|
3530
|
-
ok: true,
|
|
3531
|
-
v: "03:00",
|
|
3532
|
-
reason: "ok \u2014 PR opened",
|
|
3533
|
-
},
|
|
3534
|
-
{ t: "mono", v: "tomorrow 03:00" },
|
|
3535
|
-
{ t: "toggle", v: true },
|
|
3536
|
-
],
|
|
3537
|
-
[
|
|
3538
|
-
{ t: "mono", v: "nightly-test-improvement" },
|
|
3539
|
-
{
|
|
3540
|
-
t: "text",
|
|
3541
|
-
v: "Strengthens weak assertions in existing tests and opens a PR",
|
|
3542
|
-
},
|
|
3543
|
-
{ t: "badge", v: "pipeline host", cls: "default" },
|
|
3544
|
-
{ t: "mono", v: "04:00 Mon\u2013Fri" },
|
|
3545
|
-
{
|
|
3546
|
-
t: "run",
|
|
3547
|
-
ok: true,
|
|
3548
|
-
v: "04:00",
|
|
3549
|
-
reason: "ok \u2014 PR opened",
|
|
3550
|
-
},
|
|
3551
|
-
{ t: "mono", v: "tomorrow 04:00" },
|
|
3552
|
-
{ t: "toggle", v: true },
|
|
3553
|
-
],
|
|
3554
|
-
[
|
|
3555
|
-
{ t: "mono", v: "nightly-code-complexity" },
|
|
3556
|
-
{
|
|
3557
|
-
t: "text",
|
|
3558
|
-
v: "Refactors a complexity hotspot without changing behavior and opens a PR",
|
|
3559
|
-
},
|
|
3560
|
-
{ t: "badge", v: "pipeline host", cls: "default" },
|
|
3561
|
-
{ t: "mono", v: "05:00 Mon\u2013Fri" },
|
|
3562
|
-
{
|
|
3563
|
-
t: "run",
|
|
3564
|
-
ok: true,
|
|
3565
|
-
v: "05:00",
|
|
3566
|
-
reason: "ok \u2014 no hotspot over budget",
|
|
3567
|
-
},
|
|
3568
|
-
{ t: "mono", v: "tomorrow 05:00" },
|
|
3569
|
-
{ t: "toggle", v: true },
|
|
3570
|
-
],
|
|
3571
|
-
],
|
|
3380
|
+
id: "automations-scheduled-jobs",
|
|
3381
|
+
note: "Live from the harness scheduler (Codex automations or Claude /schedule). Only lisa-auto-<project>-* jobs are listed. Cadence is read-only in this phase \u2014 change jobs with /lisa:setup-automations. Nightly agents are pipeline files, inventoried under Pipelines.",
|
|
3382
|
+
cols: ["Automation", "Runs on", "Cadence", "Last run", "Status"],
|
|
3383
|
+
rows: [],
|
|
3384
|
+
empty: {
|
|
3385
|
+
state: "loading",
|
|
3386
|
+
message: "Loading harness scheduler\u2026",
|
|
3387
|
+
},
|
|
3572
3388
|
},
|
|
3573
3389
|
{
|
|
3574
3390
|
card: "Fleet controls",
|
|
@@ -3672,7 +3488,7 @@
|
|
|
3672
3488
|
t: "text",
|
|
3673
3489
|
v: "Error events, issue volume, release health",
|
|
3674
3490
|
},
|
|
3675
|
-
{ t: "status", v: true },
|
|
3491
|
+
{ t: "status", v: true, provider: "sentry" },
|
|
3676
3492
|
],
|
|
3677
3493
|
[
|
|
3678
3494
|
{ t: "mono", v: "AWS CloudWatch (Alarms)" },
|
|
@@ -3680,7 +3496,11 @@
|
|
|
3680
3496
|
t: "text",
|
|
3681
3497
|
v: "Alarm states, error-rate and latency metrics",
|
|
3682
3498
|
},
|
|
3683
|
-
{
|
|
3499
|
+
{
|
|
3500
|
+
t: "status",
|
|
3501
|
+
v: true,
|
|
3502
|
+
provider: "cloudwatch-alarms",
|
|
3503
|
+
},
|
|
3684
3504
|
],
|
|
3685
3505
|
[
|
|
3686
3506
|
{ t: "mono", v: "AWS X-Ray" },
|
|
@@ -3688,6 +3508,7 @@
|
|
|
3688
3508
|
{
|
|
3689
3509
|
t: "status",
|
|
3690
3510
|
v: false,
|
|
3511
|
+
provider: "x-ray",
|
|
3691
3512
|
reason: "no traces found — X-Ray is not instrumented",
|
|
3692
3513
|
},
|
|
3693
3514
|
],
|
|
@@ -5113,175 +4934,83 @@
|
|
|
5113
4934
|
};
|
|
5114
4935
|
|
|
5115
4936
|
/* ---------------------------------- Plugins ---------------------------------- */
|
|
4937
|
+
/* Catalog copy only — enablement comes from the enabled-plugins probe.
|
|
4938
|
+
Demo toggles are never rendered as a fallback. */
|
|
5116
4939
|
DATA.sections.plugins = {
|
|
5117
4940
|
title: "Plugins & MCP",
|
|
5118
4941
|
desc: "Lisa’s own plugin set (base + one per detected stack), curated third-party plugins, and the MCP servers each one carries. Non-Claude agents receive generated equivalents of everything here.",
|
|
5119
4942
|
src: ".claude/settings.json enabledPlugins · plugins/src/",
|
|
5120
4943
|
blocks: [
|
|
5121
4944
|
{
|
|
5122
|
-
type: "
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
{
|
|
5166
|
-
t: "text",
|
|
5167
|
-
v: "20 skills, rubocop-on-edit, Rails conventions",
|
|
5168
|
-
},
|
|
5169
|
-
{ t: "toggle", v: false },
|
|
5170
|
-
],
|
|
5171
|
-
[
|
|
5172
|
-
{ t: "mono", v: "lisa-harper-fabric@lisa" },
|
|
5173
|
-
{ t: "text", v: "12 skills, generated-artifact protection" },
|
|
5174
|
-
{ t: "toggle", v: false },
|
|
5175
|
-
],
|
|
5176
|
-
[
|
|
5177
|
-
{ t: "mono", v: "lisa-wiki@lisa" },
|
|
5178
|
-
{
|
|
5179
|
-
t: "text",
|
|
5180
|
-
v: "In-repo LLM wiki: 22 skills, ingestion connectors (default-on for phaser only)",
|
|
5181
|
-
},
|
|
5182
|
-
{ t: "toggle", v: false },
|
|
5183
|
-
],
|
|
5184
|
-
[
|
|
5185
|
-
{ t: "mono", v: "lisa-openclaw@lisa" },
|
|
5186
|
-
{
|
|
5187
|
-
t: "text",
|
|
5188
|
-
v: "Telegram/Slack routing via OpenClaw (opt-in)",
|
|
5189
|
-
},
|
|
5190
|
-
{ t: "toggle", v: false },
|
|
5191
|
-
],
|
|
5192
|
-
],
|
|
5193
|
-
},
|
|
5194
|
-
{
|
|
5195
|
-
type: "table",
|
|
5196
|
-
card: "Curated third-party plugins",
|
|
5197
|
-
note: "Flip an entry to enable or disable; Lisa never touches third-party keys on re-apply",
|
|
5198
|
-
cols: ["Plugin", "Purpose", "Enabled"],
|
|
5199
|
-
rows: [
|
|
5200
|
-
[
|
|
5201
|
-
{ t: "mono", v: "code-review@claude-plugins-official" },
|
|
5202
|
-
{ t: "text", v: "Pre-ship diff defect hunt" },
|
|
5203
|
-
{ t: "toggle", v: true },
|
|
5204
|
-
],
|
|
5205
|
-
[
|
|
5206
|
-
{ t: "mono", v: "code-simplifier@claude-plugins-official" },
|
|
5207
|
-
{ t: "text", v: "Behavior-preserving cleanup" },
|
|
5208
|
-
{ t: "toggle", v: true },
|
|
5209
|
-
],
|
|
5210
|
-
[
|
|
5211
|
-
{ t: "mono", v: "coderabbit@claude-plugins-official" },
|
|
5212
|
-
{ t: "text", v: "AI PR review (also a required check)" },
|
|
5213
|
-
{ t: "toggle", v: true },
|
|
5214
|
-
],
|
|
5215
|
-
[
|
|
5216
|
-
{ t: "mono", v: "sentry@claude-plugins-official" },
|
|
5217
|
-
{ t: "text", v: "Issue debugging, Seer, instrumentation" },
|
|
5218
|
-
{ t: "toggle", v: true },
|
|
5219
|
-
],
|
|
5220
|
-
[
|
|
5221
|
-
{ t: "mono", v: "safety-net@cc-marketplace" },
|
|
5222
|
-
{ t: "text", v: "Guardrail rulebooks" },
|
|
5223
|
-
{ t: "toggle", v: false },
|
|
5224
|
-
],
|
|
5225
|
-
[
|
|
5226
|
-
{ t: "mono", v: "typescript-lsp@claude-plugins-official" },
|
|
5227
|
-
{ t: "text", v: "TypeScript language server" },
|
|
5228
|
-
{ t: "toggle", v: true },
|
|
5229
|
-
],
|
|
5230
|
-
[
|
|
5231
|
-
{ t: "mono", v: "playwright@claude-plugins-official" },
|
|
5232
|
-
{ t: "text", v: "Browser automation MCP" },
|
|
5233
|
-
{ t: "toggle", v: true },
|
|
5234
|
-
],
|
|
5235
|
-
[
|
|
5236
|
-
{ t: "mono", v: "posthog@claude-plugins-official" },
|
|
5237
|
-
{ t: "text", v: "Product analytics" },
|
|
5238
|
-
{ t: "toggle", v: true },
|
|
5239
|
-
],
|
|
5240
|
-
[
|
|
5241
|
-
{ t: "mono", v: "impeccable@impeccable" },
|
|
5242
|
-
{ t: "text", v: "UI design polish" },
|
|
5243
|
-
{ t: "toggle", v: true },
|
|
5244
|
-
],
|
|
5245
|
-
[
|
|
5246
|
-
{ t: "mono", v: "skill-creator@claude-plugins-official" },
|
|
5247
|
-
{ t: "text", v: "Author and optimize skills" },
|
|
5248
|
-
{ t: "toggle", v: false },
|
|
5249
|
-
],
|
|
5250
|
-
[
|
|
5251
|
-
{ t: "mono", v: "ruby-lsp@claude-plugins-official" },
|
|
5252
|
-
{ t: "text", v: "Ruby language server (rails stacks)" },
|
|
5253
|
-
{ t: "toggle", v: false },
|
|
5254
|
-
],
|
|
5255
|
-
],
|
|
4945
|
+
type: "plugins-live",
|
|
4946
|
+
lisaCatalog: {
|
|
4947
|
+
"lisa@lisa":
|
|
4948
|
+
"Base governance: ~145 skills, 26 agents, 12 hooks, /lisa:* commands, rules, Sentry MCP",
|
|
4949
|
+
"lisa-typescript@lisa":
|
|
4950
|
+
"format-on-edit, lint-on-edit, ast-grep-on-edit, suppress-directive blocking",
|
|
4951
|
+
"lisa-expo@lisa":
|
|
4952
|
+
"~40 skills (EAS, expo-router, gluestack), ops-specialist agent, Expo MCP",
|
|
4953
|
+
"lisa-nestjs@lisa":
|
|
4954
|
+
"GraphQL/TypeORM skills, migration-edit blocking",
|
|
4955
|
+
"lisa-phaser@lisa": "8 skills, 20 game-dev agents, Phaser rules",
|
|
4956
|
+
"lisa-rails@lisa":
|
|
4957
|
+
"20 skills, rubocop-on-edit, Rails conventions",
|
|
4958
|
+
"lisa-harper-fabric@lisa":
|
|
4959
|
+
"12 skills, generated-artifact protection",
|
|
4960
|
+
"lisa-wiki@lisa":
|
|
4961
|
+
"In-repo LLM wiki: 22 skills, ingestion connectors (default-on for phaser only)",
|
|
4962
|
+
"lisa-openclaw@lisa":
|
|
4963
|
+
"Telegram/Slack routing via OpenClaw (opt-in)",
|
|
4964
|
+
"lisa-cdk@lisa": "AWS CDK plugin",
|
|
4965
|
+
},
|
|
4966
|
+
thirdPartyCatalog: {
|
|
4967
|
+
"code-review@claude-plugins-official":
|
|
4968
|
+
"Pre-ship diff defect hunt",
|
|
4969
|
+
"code-simplifier@claude-plugins-official":
|
|
4970
|
+
"Behavior-preserving cleanup",
|
|
4971
|
+
"coderabbit@claude-plugins-official":
|
|
4972
|
+
"AI PR review (also a required check)",
|
|
4973
|
+
"sentry@claude-plugins-official":
|
|
4974
|
+
"Issue debugging, Seer, instrumentation",
|
|
4975
|
+
"safety-net@cc-marketplace": "Guardrail rulebooks",
|
|
4976
|
+
"typescript-lsp@claude-plugins-official":
|
|
4977
|
+
"TypeScript language server",
|
|
4978
|
+
"playwright@claude-plugins-official": "Browser automation MCP",
|
|
4979
|
+
"posthog@claude-plugins-official": "Product analytics",
|
|
4980
|
+
"impeccable@impeccable": "UI design polish",
|
|
4981
|
+
"skill-creator@claude-plugins-official":
|
|
4982
|
+
"Author and optimize skills",
|
|
4983
|
+
"ruby-lsp@claude-plugins-official":
|
|
4984
|
+
"Ruby language server (rails stacks)",
|
|
4985
|
+
"atlassian@claude-plugins-official": "Jira / Confluence access",
|
|
4986
|
+
"hookify@claude-plugins-official": "Custom hook authoring",
|
|
4987
|
+
},
|
|
5256
4988
|
},
|
|
5257
4989
|
{
|
|
5258
4990
|
type: "table",
|
|
5259
4991
|
card: "MCP servers",
|
|
5260
|
-
|
|
4992
|
+
note: "Catalog of MCP servers commonly carried by plugins — enablement follows the hosting plugin (read-only in this phase).",
|
|
4993
|
+
cols: ["Server", "Transport", "Enablement"],
|
|
5261
4994
|
rows: [
|
|
5262
4995
|
[
|
|
5263
4996
|
{ t: "mono", v: "sentry" },
|
|
5264
4997
|
{ t: "mono", v: "http" },
|
|
5265
4998
|
{ t: "text", v: "always-on via base plugin" },
|
|
5266
|
-
{ t: "toggle", v: true },
|
|
5267
4999
|
],
|
|
5268
5000
|
[
|
|
5269
5001
|
{ t: "mono", v: "expo" },
|
|
5270
5002
|
{ t: "mono", v: "http" },
|
|
5271
5003
|
{ t: "text", v: "on when the expo stack is detected" },
|
|
5272
|
-
{ t: "toggle", v: true },
|
|
5273
5004
|
],
|
|
5274
5005
|
[
|
|
5275
5006
|
{ t: "mono", v: "linear-server" },
|
|
5276
5007
|
{ t: "mono", v: "http" },
|
|
5277
5008
|
{ t: "text", v: "project-level .mcp.json" },
|
|
5278
|
-
{ t: "toggle", v: false },
|
|
5279
5009
|
],
|
|
5280
5010
|
[
|
|
5281
5011
|
{ t: "mono", v: "phaser-editor" },
|
|
5282
5012
|
{ t: "mono", v: "stdio" },
|
|
5283
5013
|
{ t: "text", v: "shipped disabled (disabledMcpjsonServers)" },
|
|
5284
|
-
{ t: "toggle", v: false },
|
|
5285
5014
|
],
|
|
5286
5015
|
],
|
|
5287
5016
|
},
|
|
@@ -5391,6 +5120,18 @@
|
|
|
5391
5120
|
const $toast = document.getElementById("toast");
|
|
5392
5121
|
const dirty = new Set();
|
|
5393
5122
|
|
|
5123
|
+
/* Live enabled-plugins state, hydrated from GET /api/status. Starts as
|
|
5124
|
+
"loading" so the Plugins section never presents demo toggles as truth. */
|
|
5125
|
+
let pluginsLive = {
|
|
5126
|
+
status: "loading",
|
|
5127
|
+
settingsPresent: false,
|
|
5128
|
+
plugins: [],
|
|
5129
|
+
};
|
|
5130
|
+
/* Live detected-stacks state, hydrated from GET /api/status. Starts as
|
|
5131
|
+
"detecting" so the Stacks section never presents the metadata catalog
|
|
5132
|
+
as if it were the detection result. */
|
|
5133
|
+
let stackDetection = { status: "detecting", ids: [] };
|
|
5134
|
+
|
|
5394
5135
|
function el(tag, cls, html) {
|
|
5395
5136
|
const node = document.createElement(tag);
|
|
5396
5137
|
if (cls) node.className = cls;
|
|
@@ -5599,35 +5340,116 @@
|
|
|
5599
5340
|
return card;
|
|
5600
5341
|
}
|
|
5601
5342
|
|
|
5602
|
-
function
|
|
5603
|
-
|
|
5604
|
-
if (
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
if (block.note) head.appendChild(el("span", "note", esc(block.note)));
|
|
5608
|
-
card.appendChild(head);
|
|
5343
|
+
function paintProviderStatus(td, cell) {
|
|
5344
|
+
td.innerHTML = "";
|
|
5345
|
+
if (cell.v === true || cell.v === "connected") {
|
|
5346
|
+
td.appendChild(el("span", "badge pass", "✓ connected"));
|
|
5347
|
+
return;
|
|
5609
5348
|
}
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
block.cols.forEach(c => {
|
|
5615
|
-
// Cols accept either a plain string or {v, tip} — tip renders as a
|
|
5616
|
-
// hover tooltip on the header for jargon that needs a definition.
|
|
5617
|
-
if (typeof c === "string") {
|
|
5618
|
-
trh.appendChild(el("th", "", esc(c)));
|
|
5619
|
-
return;
|
|
5349
|
+
if (cell.v === "connected-but-empty") {
|
|
5350
|
+
td.appendChild(el("span", "badge warn", "◐ connected-but-empty"));
|
|
5351
|
+
if (cell.reason) {
|
|
5352
|
+
td.appendChild(el("span", "status-why", esc(cell.reason)));
|
|
5620
5353
|
}
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5354
|
+
return;
|
|
5355
|
+
}
|
|
5356
|
+
if (cell.v === "pending") {
|
|
5357
|
+
td.appendChild(el("span", "badge default", "checking…"));
|
|
5358
|
+
return;
|
|
5359
|
+
}
|
|
5360
|
+
if (cell.v === "unknown") {
|
|
5361
|
+
td.appendChild(el("span", "badge warn", "unknown"));
|
|
5362
|
+
if (cell.reason) {
|
|
5363
|
+
td.appendChild(el("span", "status-why", esc(cell.reason)));
|
|
5364
|
+
}
|
|
5365
|
+
return;
|
|
5366
|
+
}
|
|
5367
|
+
if (cell.v) {
|
|
5368
|
+
td.appendChild(el("span", "badge pass", "✓ active"));
|
|
5369
|
+
return;
|
|
5370
|
+
}
|
|
5371
|
+
td.appendChild(el("span", "badge fail", "✗ off"));
|
|
5372
|
+
if (cell.reason) {
|
|
5373
|
+
td.appendChild(el("span", "status-why", esc(cell.reason)));
|
|
5374
|
+
}
|
|
5375
|
+
}
|
|
5376
|
+
function buildStatusCell(cell) {
|
|
5377
|
+
const td = el("td");
|
|
5378
|
+
if (cell.provider) td.dataset.provider = cell.provider;
|
|
5379
|
+
if (cell.jobId) td.dataset.jobId = cell.jobId;
|
|
5380
|
+
if (cell.provider) {
|
|
5381
|
+
paintProviderStatus(td, cell);
|
|
5382
|
+
return td;
|
|
5383
|
+
}
|
|
5384
|
+
if (cell.v === "unknown" || cell.v === null || cell.v === undefined) {
|
|
5385
|
+
td.appendChild(el("span", "badge warn", "unknown"));
|
|
5386
|
+
if (cell.reason) {
|
|
5387
|
+
td.appendChild(el("span", "status-why", esc(cell.reason)));
|
|
5388
|
+
}
|
|
5389
|
+
return td;
|
|
5390
|
+
}
|
|
5391
|
+
if (cell.v) {
|
|
5392
|
+
td.appendChild(el("span", "badge pass", "✓ active"));
|
|
5393
|
+
return td;
|
|
5394
|
+
}
|
|
5395
|
+
td.appendChild(el("span", "badge fail", "✗ off"));
|
|
5396
|
+
if (cell.reason) {
|
|
5397
|
+
td.appendChild(el("span", "status-why", esc(cell.reason)));
|
|
5398
|
+
}
|
|
5399
|
+
return td;
|
|
5400
|
+
}
|
|
5401
|
+
|
|
5402
|
+
function buildTable(block) {
|
|
5403
|
+
const card = el("div", "card");
|
|
5404
|
+
if (block.id) {
|
|
5405
|
+
card.id = block.id;
|
|
5406
|
+
card.dataset.testid = block.id;
|
|
5407
|
+
}
|
|
5408
|
+
if (block.card) {
|
|
5409
|
+
const head = el("div", "card-head");
|
|
5410
|
+
head.appendChild(el("h2", "", esc(block.card)));
|
|
5411
|
+
if (block.note) head.appendChild(el("span", "note", esc(block.note)));
|
|
5412
|
+
card.appendChild(head);
|
|
5413
|
+
}
|
|
5414
|
+
if (block.empty && (!block.rows || block.rows.length === 0)) {
|
|
5415
|
+
const empty = el(
|
|
5416
|
+
"div",
|
|
5417
|
+
"status-why automations-empty",
|
|
5418
|
+
esc(block.empty.message || "No rows")
|
|
5419
|
+
);
|
|
5420
|
+
empty.dataset.testid = (block.id || "table") + "-empty";
|
|
5421
|
+
if (block.empty.state) empty.dataset.state = block.empty.state;
|
|
5422
|
+
card.appendChild(empty);
|
|
5423
|
+
return card;
|
|
5424
|
+
}
|
|
5425
|
+
const scroll = el("div", "table-scroll");
|
|
5426
|
+
const table = el("table", "grid");
|
|
5427
|
+
const thead = el("thead");
|
|
5428
|
+
const trh = el("tr");
|
|
5429
|
+
block.cols.forEach(c => {
|
|
5430
|
+
// Cols accept either a plain string or {v, tip} — tip renders as a
|
|
5431
|
+
// hover tooltip on the header for jargon that needs a definition.
|
|
5432
|
+
if (typeof c === "string") {
|
|
5433
|
+
trh.appendChild(el("th", "", esc(c)));
|
|
5434
|
+
return;
|
|
5435
|
+
}
|
|
5436
|
+
const th = el("th", c.tip ? "th-help" : "", esc(c.v));
|
|
5437
|
+
if (c.tip) th.title = c.tip;
|
|
5438
|
+
trh.appendChild(th);
|
|
5624
5439
|
});
|
|
5625
5440
|
thead.appendChild(trh);
|
|
5626
5441
|
table.appendChild(thead);
|
|
5627
5442
|
const tbody = el("tbody");
|
|
5628
5443
|
block.rows.forEach(cells => {
|
|
5629
5444
|
const tr = el("tr");
|
|
5445
|
+
if (cells && cells._automationId) {
|
|
5446
|
+
tr.dataset.automationId = cells._automationId;
|
|
5447
|
+
}
|
|
5630
5448
|
cells.forEach(cell => {
|
|
5449
|
+
if (cell.t === "status") {
|
|
5450
|
+
tr.appendChild(buildStatusCell(cell));
|
|
5451
|
+
return;
|
|
5452
|
+
}
|
|
5631
5453
|
const td = el("td");
|
|
5632
5454
|
if (cell.t === "mono") td.className = "mono";
|
|
5633
5455
|
if (cell.t === "text" || cell.t === "mono") {
|
|
@@ -5718,46 +5540,118 @@
|
|
|
5718
5540
|
return card;
|
|
5719
5541
|
}
|
|
5720
5542
|
|
|
5721
|
-
|
|
5722
|
-
|
|
5543
|
+
/* The 8-entry stack metadata, keyed by id. This is a catalog of what each
|
|
5544
|
+
stack installs — NOT the detection result; only ids the probe reports
|
|
5545
|
+
are rendered from it. */
|
|
5546
|
+
function stackCatalog(block) {
|
|
5547
|
+
const catalog = new Map();
|
|
5723
5548
|
block.items.forEach(s => {
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5549
|
+
catalog.set(s.id, s);
|
|
5550
|
+
});
|
|
5551
|
+
return catalog;
|
|
5552
|
+
}
|
|
5553
|
+
|
|
5554
|
+
/* A detected id with no catalog entry still gets a card (id as name) so
|
|
5555
|
+
detection truth is never dropped just because copy is missing. */
|
|
5556
|
+
function minimalStackMeta(id) {
|
|
5557
|
+
return {
|
|
5558
|
+
id,
|
|
5559
|
+
glyph: id.slice(0, 2).toUpperCase(),
|
|
5560
|
+
name: id,
|
|
5561
|
+
tagline: "",
|
|
5562
|
+
detect: "",
|
|
5563
|
+
badges: [],
|
|
5564
|
+
more: [],
|
|
5565
|
+
};
|
|
5566
|
+
}
|
|
5567
|
+
|
|
5568
|
+
function stackCard(s) {
|
|
5569
|
+
const card = el("div", "stack-card on");
|
|
5570
|
+
const head = el("div", "head");
|
|
5571
|
+
head.appendChild(el("span", "glyph", esc(s.glyph)));
|
|
5572
|
+
const t = el("div", "t");
|
|
5573
|
+
t.appendChild(el("b", "", esc(s.name)));
|
|
5574
|
+
t.appendChild(el("i", "", esc(s.tagline)));
|
|
5575
|
+
head.appendChild(t);
|
|
5576
|
+
const toggleWrap = buildControl(
|
|
5577
|
+
{ type: "toggle", value: true },
|
|
5578
|
+
"stack." + s.id
|
|
5579
|
+
);
|
|
5580
|
+
toggleWrap.querySelector("input").addEventListener("change", e => {
|
|
5581
|
+
card.classList.toggle("on", e.target.checked);
|
|
5582
|
+
markDirty("stack." + s.id, null);
|
|
5583
|
+
});
|
|
5584
|
+
head.appendChild(toggleWrap);
|
|
5585
|
+
card.appendChild(head);
|
|
5586
|
+
const meta = el("div", "meta");
|
|
5587
|
+
(s.badges || []).forEach(b =>
|
|
5588
|
+
meta.appendChild(el("span", "tag", esc(b)))
|
|
5589
|
+
);
|
|
5590
|
+
card.appendChild(meta);
|
|
5591
|
+
card.appendChild(el("div", "detect", s.detect || ""));
|
|
5592
|
+
if (s.more && s.more.length) {
|
|
5593
|
+
const det = el("details", "stack-more");
|
|
5594
|
+
det.appendChild(el("summary", "", "What this stack installs"));
|
|
5595
|
+
const ul = el("ul");
|
|
5596
|
+
s.more.forEach(m => ul.appendChild(el("li", "", esc(m))));
|
|
5597
|
+
det.appendChild(ul);
|
|
5598
|
+
card.appendChild(det);
|
|
5599
|
+
}
|
|
5600
|
+
return card;
|
|
5601
|
+
}
|
|
5602
|
+
|
|
5603
|
+
function renderStacksInto(wrap, block) {
|
|
5604
|
+
wrap.innerHTML = "";
|
|
5605
|
+
const d = stackDetection;
|
|
5606
|
+
if (d.status === "detecting") {
|
|
5607
|
+
wrap.appendChild(
|
|
5608
|
+
el("div", "stacks-state detecting", "Detecting project stacks…")
|
|
5734
5609
|
);
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5610
|
+
return;
|
|
5611
|
+
}
|
|
5612
|
+
if (d.status === "unknown") {
|
|
5613
|
+
const box = el("div", "stacks-state unknown");
|
|
5614
|
+
box.appendChild(el("b", "", "Stack detection unavailable"));
|
|
5615
|
+
box.appendChild(
|
|
5616
|
+
el("span", "status-why", esc(d.reason + ": " + d.message))
|
|
5617
|
+
);
|
|
5618
|
+
wrap.appendChild(box);
|
|
5619
|
+
return;
|
|
5620
|
+
}
|
|
5621
|
+
if (!d.ids || d.ids.length === 0) {
|
|
5622
|
+
wrap.appendChild(
|
|
5623
|
+
el(
|
|
5624
|
+
"div",
|
|
5625
|
+
"stacks-state empty",
|
|
5626
|
+
"No Lisa stack detected in this project."
|
|
5627
|
+
)
|
|
5628
|
+
);
|
|
5629
|
+
return;
|
|
5630
|
+
}
|
|
5631
|
+
const catalog = stackCatalog(block);
|
|
5632
|
+
const grid = el("div", "stack-grid");
|
|
5633
|
+
d.ids.forEach(id => {
|
|
5634
|
+
grid.appendChild(stackCard(catalog.get(id) || minimalStackMeta(id)));
|
|
5754
5635
|
});
|
|
5755
|
-
const wrap = el("div");
|
|
5756
5636
|
wrap.appendChild(grid);
|
|
5757
5637
|
wrap.appendChild(el("div", "", " "));
|
|
5638
|
+
}
|
|
5639
|
+
|
|
5640
|
+
function buildStacks(block) {
|
|
5641
|
+
const wrap = el("div", "stacks-block");
|
|
5642
|
+
renderStacksInto(wrap, block);
|
|
5758
5643
|
return wrap;
|
|
5759
5644
|
}
|
|
5760
5645
|
|
|
5646
|
+
/* Re-render the Stacks section in place after detection hydrates. */
|
|
5647
|
+
function refreshStacks() {
|
|
5648
|
+
const block = DATA.sections.stacks.blocks.find(
|
|
5649
|
+
b => b.type === "stacks"
|
|
5650
|
+
);
|
|
5651
|
+
const wrap = document.querySelector(".stacks-block");
|
|
5652
|
+
if (block && wrap) renderStacksInto(wrap, block);
|
|
5653
|
+
}
|
|
5654
|
+
|
|
5761
5655
|
function buildHooks(block) {
|
|
5762
5656
|
const grid = el("div", "hooks-grid");
|
|
5763
5657
|
block.items.forEach(h => {
|
|
@@ -5897,6 +5791,131 @@
|
|
|
5897
5791
|
return card;
|
|
5898
5792
|
}
|
|
5899
5793
|
|
|
5794
|
+
function pluginDescription(id, probeDescription, catalog) {
|
|
5795
|
+
if (Object.hasOwn(catalog, id) && typeof catalog[id] === "string") {
|
|
5796
|
+
return catalog[id];
|
|
5797
|
+
}
|
|
5798
|
+
return typeof probeDescription === "string" ? probeDescription : "";
|
|
5799
|
+
}
|
|
5800
|
+
|
|
5801
|
+
function pluginTableRows(entries, catalog) {
|
|
5802
|
+
return entries.map(plugin => {
|
|
5803
|
+
const enabled = plugin.status === "enabled";
|
|
5804
|
+
return [
|
|
5805
|
+
{ t: "mono", v: plugin.id },
|
|
5806
|
+
{
|
|
5807
|
+
t: "text",
|
|
5808
|
+
v: pluginDescription(plugin.id, plugin.description, catalog),
|
|
5809
|
+
},
|
|
5810
|
+
{
|
|
5811
|
+
t: "badge",
|
|
5812
|
+
v: enabled ? "enabled" : "available, not enabled",
|
|
5813
|
+
cls: enabled ? "on" : "plugin-status-badge available",
|
|
5814
|
+
},
|
|
5815
|
+
];
|
|
5816
|
+
});
|
|
5817
|
+
}
|
|
5818
|
+
|
|
5819
|
+
function renderPluginsInto(wrap, block) {
|
|
5820
|
+
wrap.innerHTML = "";
|
|
5821
|
+
const state = pluginsLive;
|
|
5822
|
+
if (state.status === "loading") {
|
|
5823
|
+
wrap.appendChild(
|
|
5824
|
+
el("div", "plugins-state loading", "Loading plugin enablement…")
|
|
5825
|
+
);
|
|
5826
|
+
return;
|
|
5827
|
+
}
|
|
5828
|
+
if (state.status === "unknown") {
|
|
5829
|
+
const box = el("div", "plugins-state unknown");
|
|
5830
|
+
box.appendChild(el("b", "", "Plugin enablement unavailable"));
|
|
5831
|
+
box.appendChild(
|
|
5832
|
+
el(
|
|
5833
|
+
"span",
|
|
5834
|
+
"status-why",
|
|
5835
|
+
esc((state.reason || "unknown") + ": " + (state.message || ""))
|
|
5836
|
+
)
|
|
5837
|
+
);
|
|
5838
|
+
wrap.appendChild(box);
|
|
5839
|
+
return;
|
|
5840
|
+
}
|
|
5841
|
+
if (!state.settingsPresent) {
|
|
5842
|
+
wrap.appendChild(
|
|
5843
|
+
el(
|
|
5844
|
+
"div",
|
|
5845
|
+
"plugins-state empty",
|
|
5846
|
+
"No .claude/settings.json in this project — plugin enablement is not configured."
|
|
5847
|
+
)
|
|
5848
|
+
);
|
|
5849
|
+
return;
|
|
5850
|
+
}
|
|
5851
|
+
const plugins = Array.isArray(state.plugins) ? state.plugins : [];
|
|
5852
|
+
const lisaCatalog =
|
|
5853
|
+
block.lisaCatalog && typeof block.lisaCatalog === "object"
|
|
5854
|
+
? block.lisaCatalog
|
|
5855
|
+
: {};
|
|
5856
|
+
const thirdPartyCatalog =
|
|
5857
|
+
block.thirdPartyCatalog && typeof block.thirdPartyCatalog === "object"
|
|
5858
|
+
? block.thirdPartyCatalog
|
|
5859
|
+
: {};
|
|
5860
|
+
const lisa = [];
|
|
5861
|
+
const thirdParty = [];
|
|
5862
|
+
plugins.forEach(plugin => {
|
|
5863
|
+
if (!plugin || typeof plugin !== "object") return;
|
|
5864
|
+
const id = plugin.id;
|
|
5865
|
+
if (typeof id !== "string" || id.trim().length === 0) return;
|
|
5866
|
+
if (id.endsWith("@lisa") || Object.hasOwn(lisaCatalog, id)) {
|
|
5867
|
+
lisa.push(plugin);
|
|
5868
|
+
} else {
|
|
5869
|
+
thirdParty.push(plugin);
|
|
5870
|
+
}
|
|
5871
|
+
});
|
|
5872
|
+
if (lisa.length === 0 && thirdParty.length === 0) {
|
|
5873
|
+
wrap.appendChild(
|
|
5874
|
+
el(
|
|
5875
|
+
"div",
|
|
5876
|
+
"plugins-state empty",
|
|
5877
|
+
"No plugins enabled or available in the marketplace catalog."
|
|
5878
|
+
)
|
|
5879
|
+
);
|
|
5880
|
+
return;
|
|
5881
|
+
}
|
|
5882
|
+
if (lisa.length > 0) {
|
|
5883
|
+
wrap.appendChild(
|
|
5884
|
+
buildTable({
|
|
5885
|
+
type: "table",
|
|
5886
|
+
card: "Lisa plugins",
|
|
5887
|
+
cols: ["Plugin", "Bundles", "Status"],
|
|
5888
|
+
rows: pluginTableRows(lisa, lisaCatalog),
|
|
5889
|
+
})
|
|
5890
|
+
);
|
|
5891
|
+
}
|
|
5892
|
+
if (thirdParty.length > 0) {
|
|
5893
|
+
wrap.appendChild(
|
|
5894
|
+
buildTable({
|
|
5895
|
+
type: "table",
|
|
5896
|
+
card: "Third-party plugins",
|
|
5897
|
+
note: "Read-only: enablement comes from .claude/settings.json enabledPlugins",
|
|
5898
|
+
cols: ["Plugin", "Purpose", "Status"],
|
|
5899
|
+
rows: pluginTableRows(thirdParty, thirdPartyCatalog),
|
|
5900
|
+
})
|
|
5901
|
+
);
|
|
5902
|
+
}
|
|
5903
|
+
}
|
|
5904
|
+
|
|
5905
|
+
function buildPluginsLive(block) {
|
|
5906
|
+
const wrap = el("div", "plugins-live-block");
|
|
5907
|
+
renderPluginsInto(wrap, block);
|
|
5908
|
+
return wrap;
|
|
5909
|
+
}
|
|
5910
|
+
|
|
5911
|
+
function refreshPlugins() {
|
|
5912
|
+
const block = DATA.sections.plugins.blocks.find(
|
|
5913
|
+
b => b.type === "plugins-live"
|
|
5914
|
+
);
|
|
5915
|
+
const wrap = document.querySelector(".plugins-live-block");
|
|
5916
|
+
if (block && wrap) renderPluginsInto(wrap, block);
|
|
5917
|
+
}
|
|
5918
|
+
|
|
5900
5919
|
function buildBlock(block) {
|
|
5901
5920
|
if (block.type === "tiles") return buildTiles(block);
|
|
5902
5921
|
if (block.type === "flow") return buildFlow(block);
|
|
@@ -5906,6 +5925,7 @@
|
|
|
5906
5925
|
if (block.type === "table") return buildTable(block);
|
|
5907
5926
|
if (block.type === "tabs") return buildTabs(block);
|
|
5908
5927
|
if (block.type === "checklist") return buildChecklist(block);
|
|
5928
|
+
if (block.type === "plugins-live") return buildPluginsLive(block);
|
|
5909
5929
|
return buildCard(block);
|
|
5910
5930
|
}
|
|
5911
5931
|
|
|
@@ -6097,6 +6117,7 @@
|
|
|
6097
6117
|
if (value !== undefined) hydrateControl(row.control, value);
|
|
6098
6118
|
});
|
|
6099
6119
|
});
|
|
6120
|
+
markObservabilityProvidersPending();
|
|
6100
6121
|
}
|
|
6101
6122
|
configureRemoteEnvironment(cfg);
|
|
6102
6123
|
const repoEl = document.querySelector(".project-switch .repo");
|
|
@@ -6156,6 +6177,115 @@
|
|
|
6156
6177
|
}
|
|
6157
6178
|
return snapshot.probes;
|
|
6158
6179
|
}
|
|
6180
|
+
function connectedProvidersBlock() {
|
|
6181
|
+
const blocks = DATA.sections.monitor && DATA.sections.monitor.blocks;
|
|
6182
|
+
if (!Array.isArray(blocks)) return null;
|
|
6183
|
+
return (
|
|
6184
|
+
blocks.find(
|
|
6185
|
+
block =>
|
|
6186
|
+
block &&
|
|
6187
|
+
block.type === "table" &&
|
|
6188
|
+
block.card === "Connected providers"
|
|
6189
|
+
) || null
|
|
6190
|
+
);
|
|
6191
|
+
}
|
|
6192
|
+
function setProviderStatusCell(provider, cell) {
|
|
6193
|
+
const block = connectedProvidersBlock();
|
|
6194
|
+
if (!block || !Array.isArray(block.rows)) return;
|
|
6195
|
+
block.rows.forEach(row => {
|
|
6196
|
+
if (!Array.isArray(row)) return;
|
|
6197
|
+
row.forEach((entry, index) => {
|
|
6198
|
+
if (entry && entry.t === "status" && entry.provider === provider) {
|
|
6199
|
+
row[index] = cell;
|
|
6200
|
+
}
|
|
6201
|
+
});
|
|
6202
|
+
});
|
|
6203
|
+
const td = document.querySelector(
|
|
6204
|
+
'td[data-provider="' + provider + '"]'
|
|
6205
|
+
);
|
|
6206
|
+
if (td) paintProviderStatus(td, cell);
|
|
6207
|
+
}
|
|
6208
|
+
function markObservabilityProvidersPending() {
|
|
6209
|
+
["sentry", "cloudwatch-alarms", "x-ray"].forEach(provider => {
|
|
6210
|
+
setProviderStatusCell(provider, {
|
|
6211
|
+
t: "status",
|
|
6212
|
+
v: "pending",
|
|
6213
|
+
provider,
|
|
6214
|
+
});
|
|
6215
|
+
});
|
|
6216
|
+
}
|
|
6217
|
+
/**
|
|
6218
|
+
* Paint Connected providers from sentry / cloudwatch-alarms / x-ray probes.
|
|
6219
|
+
* unknown ≠ disconnected — never invent an unevidenced "off" claim.
|
|
6220
|
+
* @param probes - Live-status probe map from /api/status
|
|
6221
|
+
*/
|
|
6222
|
+
function applyObservabilityProviders(probes) {
|
|
6223
|
+
const specs = [
|
|
6224
|
+
{
|
|
6225
|
+
id: "sentry",
|
|
6226
|
+
emptyReason: "reachable — no Sentry data observed",
|
|
6227
|
+
},
|
|
6228
|
+
{
|
|
6229
|
+
id: "cloudwatch-alarms",
|
|
6230
|
+
emptyReason: "connected — no alarms",
|
|
6231
|
+
},
|
|
6232
|
+
{
|
|
6233
|
+
id: "x-ray",
|
|
6234
|
+
emptyReason: "connected — no traces",
|
|
6235
|
+
},
|
|
6236
|
+
];
|
|
6237
|
+
specs.forEach(spec => {
|
|
6238
|
+
const raw =
|
|
6239
|
+
probes && Object.hasOwn(probes, spec.id)
|
|
6240
|
+
? probes[spec.id]
|
|
6241
|
+
: undefined;
|
|
6242
|
+
const result =
|
|
6243
|
+
raw === undefined
|
|
6244
|
+
? {
|
|
6245
|
+
state: "unknown",
|
|
6246
|
+
reason: "missing-probe",
|
|
6247
|
+
message: "The " + spec.id + " probe was not reported",
|
|
6248
|
+
}
|
|
6249
|
+
: normalizeLiveStatusResult(raw);
|
|
6250
|
+
if (
|
|
6251
|
+
result.state === "value" &&
|
|
6252
|
+
result.value &&
|
|
6253
|
+
typeof result.value === "object" &&
|
|
6254
|
+
result.value.status === "connected"
|
|
6255
|
+
) {
|
|
6256
|
+
setProviderStatusCell(spec.id, {
|
|
6257
|
+
t: "status",
|
|
6258
|
+
v: "connected",
|
|
6259
|
+
provider: spec.id,
|
|
6260
|
+
});
|
|
6261
|
+
return;
|
|
6262
|
+
}
|
|
6263
|
+
if (
|
|
6264
|
+
result.state === "value" &&
|
|
6265
|
+
result.value &&
|
|
6266
|
+
typeof result.value === "object" &&
|
|
6267
|
+
result.value.status === "connected-but-empty"
|
|
6268
|
+
) {
|
|
6269
|
+
setProviderStatusCell(spec.id, {
|
|
6270
|
+
t: "status",
|
|
6271
|
+
v: "connected-but-empty",
|
|
6272
|
+
provider: spec.id,
|
|
6273
|
+
reason: spec.emptyReason,
|
|
6274
|
+
});
|
|
6275
|
+
return;
|
|
6276
|
+
}
|
|
6277
|
+
const reason =
|
|
6278
|
+
result.state === "unknown"
|
|
6279
|
+
? result.reason + ": " + result.message
|
|
6280
|
+
: "invalid-value: provider probe returned an unusable value";
|
|
6281
|
+
setProviderStatusCell(spec.id, {
|
|
6282
|
+
t: "status",
|
|
6283
|
+
v: "unknown",
|
|
6284
|
+
provider: spec.id,
|
|
6285
|
+
reason,
|
|
6286
|
+
});
|
|
6287
|
+
});
|
|
6288
|
+
}
|
|
6159
6289
|
function renderLiveStatuses(probes) {
|
|
6160
6290
|
const list = document.getElementById("liveStatusList");
|
|
6161
6291
|
list.innerHTML = "";
|
|
@@ -6190,6 +6320,101 @@
|
|
|
6190
6320
|
list.appendChild(item);
|
|
6191
6321
|
});
|
|
6192
6322
|
}
|
|
6323
|
+
/**
|
|
6324
|
+
* Map the enabled-plugins probe into pluginsLive. A missing probe or an
|
|
6325
|
+
* unusable value becomes an explicit unknown state — never demo toggles.
|
|
6326
|
+
* @param probes - Live-status probe map from /api/status
|
|
6327
|
+
*/
|
|
6328
|
+
function applyEnabledPlugins(probes) {
|
|
6329
|
+
const raw =
|
|
6330
|
+
probes && Object.hasOwn(probes, "enabled-plugins")
|
|
6331
|
+
? probes["enabled-plugins"]
|
|
6332
|
+
: undefined;
|
|
6333
|
+
if (raw === undefined) {
|
|
6334
|
+
pluginsLive = {
|
|
6335
|
+
status: "unknown",
|
|
6336
|
+
settingsPresent: false,
|
|
6337
|
+
plugins: [],
|
|
6338
|
+
reason: "missing-probe",
|
|
6339
|
+
message: "The enabled-plugins probe was not reported",
|
|
6340
|
+
};
|
|
6341
|
+
refreshPlugins();
|
|
6342
|
+
return;
|
|
6343
|
+
}
|
|
6344
|
+
const result = normalizeLiveStatusResult(raw);
|
|
6345
|
+
if (
|
|
6346
|
+
result.state === "value" &&
|
|
6347
|
+
result.value &&
|
|
6348
|
+
typeof result.value === "object"
|
|
6349
|
+
) {
|
|
6350
|
+
const settingsPresent = result.value.settingsPresent === true;
|
|
6351
|
+
const plugins = result.value.plugins;
|
|
6352
|
+
if (!Array.isArray(plugins)) {
|
|
6353
|
+
pluginsLive = {
|
|
6354
|
+
status: "unknown",
|
|
6355
|
+
settingsPresent: false,
|
|
6356
|
+
plugins: [],
|
|
6357
|
+
reason: "invalid-value",
|
|
6358
|
+
message: "enabled-plugins did not return a plugins array",
|
|
6359
|
+
};
|
|
6360
|
+
} else {
|
|
6361
|
+
const rows = [];
|
|
6362
|
+
let invalid = false;
|
|
6363
|
+
for (const entry of plugins) {
|
|
6364
|
+
if (
|
|
6365
|
+
!entry ||
|
|
6366
|
+
typeof entry !== "object" ||
|
|
6367
|
+
typeof entry.id !== "string" ||
|
|
6368
|
+
entry.id.trim().length === 0 ||
|
|
6369
|
+
(entry.status !== "enabled" && entry.status !== "available")
|
|
6370
|
+
) {
|
|
6371
|
+
invalid = true;
|
|
6372
|
+
break;
|
|
6373
|
+
}
|
|
6374
|
+
rows.push({
|
|
6375
|
+
id: entry.id,
|
|
6376
|
+
status: entry.status,
|
|
6377
|
+
description:
|
|
6378
|
+
typeof entry.description === "string"
|
|
6379
|
+
? entry.description
|
|
6380
|
+
: "",
|
|
6381
|
+
});
|
|
6382
|
+
}
|
|
6383
|
+
if (invalid) {
|
|
6384
|
+
pluginsLive = {
|
|
6385
|
+
status: "unknown",
|
|
6386
|
+
settingsPresent: false,
|
|
6387
|
+
plugins: [],
|
|
6388
|
+
reason: "invalid-value",
|
|
6389
|
+
message: "enabled-plugins returned an unusable plugin row",
|
|
6390
|
+
};
|
|
6391
|
+
} else {
|
|
6392
|
+
pluginsLive = {
|
|
6393
|
+
status: "value",
|
|
6394
|
+
settingsPresent,
|
|
6395
|
+
plugins: rows,
|
|
6396
|
+
};
|
|
6397
|
+
}
|
|
6398
|
+
}
|
|
6399
|
+
} else if (result.state === "not-applicable") {
|
|
6400
|
+
pluginsLive = {
|
|
6401
|
+
status: "unknown",
|
|
6402
|
+
settingsPresent: false,
|
|
6403
|
+
plugins: [],
|
|
6404
|
+
reason: "not-applicable",
|
|
6405
|
+
message: "enabled-plugins reported not applicable",
|
|
6406
|
+
};
|
|
6407
|
+
} else {
|
|
6408
|
+
pluginsLive = {
|
|
6409
|
+
status: "unknown",
|
|
6410
|
+
settingsPresent: false,
|
|
6411
|
+
plugins: [],
|
|
6412
|
+
reason: result.reason || "unknown",
|
|
6413
|
+
message: result.message || "Plugin enablement unavailable",
|
|
6414
|
+
};
|
|
6415
|
+
}
|
|
6416
|
+
refreshPlugins();
|
|
6417
|
+
}
|
|
6193
6418
|
/**
|
|
6194
6419
|
* Paint `#healthChip` from the lisa-version probe.
|
|
6195
6420
|
* green=ok (up to date), amber=warn (behind), unknown≠ok.
|
|
@@ -6309,6 +6534,413 @@
|
|
|
6309
6534
|
renderAll();
|
|
6310
6535
|
showSection(current);
|
|
6311
6536
|
}
|
|
6537
|
+
function githubRepoHostBlock() {
|
|
6538
|
+
return {
|
|
6539
|
+
card: "Host",
|
|
6540
|
+
rows: [
|
|
6541
|
+
{
|
|
6542
|
+
key: "",
|
|
6543
|
+
label: "Git host",
|
|
6544
|
+
desc: "Which host this repository lives on. GitHub is the only host Lisa provisions today.",
|
|
6545
|
+
control: sel("GitHub", ["GitHub", "GitLab", "Bitbucket"]),
|
|
6546
|
+
},
|
|
6547
|
+
],
|
|
6548
|
+
};
|
|
6549
|
+
}
|
|
6550
|
+
function githubRepoSettingsRows(settings) {
|
|
6551
|
+
return [
|
|
6552
|
+
{
|
|
6553
|
+
label: "Allow merge commits",
|
|
6554
|
+
desc: "The fleet is merge-only — squash and rebase are disabled because squashing flattens release commits and breaks promotion.",
|
|
6555
|
+
control: tog(!!settings.allow_merge_commit),
|
|
6556
|
+
},
|
|
6557
|
+
{
|
|
6558
|
+
label: "Allow squash merge",
|
|
6559
|
+
desc: "",
|
|
6560
|
+
control: tog(!!settings.allow_squash_merge),
|
|
6561
|
+
},
|
|
6562
|
+
{
|
|
6563
|
+
label: "Allow rebase merge",
|
|
6564
|
+
desc: "",
|
|
6565
|
+
control: tog(!!settings.allow_rebase_merge),
|
|
6566
|
+
},
|
|
6567
|
+
{
|
|
6568
|
+
label: "Auto-merge",
|
|
6569
|
+
desc: "Lisa PRs enable auto-merge and drive themselves to green.",
|
|
6570
|
+
control: tog(!!settings.allow_auto_merge),
|
|
6571
|
+
},
|
|
6572
|
+
{
|
|
6573
|
+
label: "Suggest updating PR branches",
|
|
6574
|
+
desc: "",
|
|
6575
|
+
control: tog(!!settings.allow_update_branch),
|
|
6576
|
+
},
|
|
6577
|
+
{
|
|
6578
|
+
label: "Delete head branch on merge",
|
|
6579
|
+
desc: "Environment branches survive via the prevent-delete ruleset.",
|
|
6580
|
+
control: tog(!!settings.delete_branch_on_merge),
|
|
6581
|
+
},
|
|
6582
|
+
{
|
|
6583
|
+
label: "Merge commit title",
|
|
6584
|
+
desc: "",
|
|
6585
|
+
control: sel(String(settings.merge_commit_title || ""), [
|
|
6586
|
+
"MERGE_MESSAGE",
|
|
6587
|
+
"PR_TITLE",
|
|
6588
|
+
]),
|
|
6589
|
+
},
|
|
6590
|
+
{
|
|
6591
|
+
label: "Issues tab",
|
|
6592
|
+
desc: "",
|
|
6593
|
+
control: tog(!!settings.has_issues),
|
|
6594
|
+
},
|
|
6595
|
+
{
|
|
6596
|
+
label: "Wiki tab",
|
|
6597
|
+
desc: "Off — Lisa uses the in-repo wiki/ directory instead.",
|
|
6598
|
+
control: tog(!!settings.has_wiki),
|
|
6599
|
+
},
|
|
6600
|
+
{
|
|
6601
|
+
label: "Secret scanning + push protection",
|
|
6602
|
+
desc: "Best-effort; skipped when the plan lacks GitHub Advanced Security.",
|
|
6603
|
+
control: tog(!!settings.secret_scanning),
|
|
6604
|
+
},
|
|
6605
|
+
{
|
|
6606
|
+
label: "Default branch",
|
|
6607
|
+
desc: "Live default branch from the repository.",
|
|
6608
|
+
control: txt(String(settings.default_branch || ""), {
|
|
6609
|
+
mono: true,
|
|
6610
|
+
}),
|
|
6611
|
+
},
|
|
6612
|
+
];
|
|
6613
|
+
}
|
|
6614
|
+
function applyGithubRepoPanel(result) {
|
|
6615
|
+
const host = githubRepoHostBlock();
|
|
6616
|
+
if (result.state !== "value") {
|
|
6617
|
+
const reason =
|
|
6618
|
+
result.state === "unknown"
|
|
6619
|
+
? result.reason + ": " + result.message
|
|
6620
|
+
: "not applicable";
|
|
6621
|
+
DATA.sections.repository.blocks = [
|
|
6622
|
+
host,
|
|
6623
|
+
{
|
|
6624
|
+
type: "callout",
|
|
6625
|
+
variant: "warn",
|
|
6626
|
+
text:
|
|
6627
|
+
"<b>unknown — not authenticated / unavailable</b> — " +
|
|
6628
|
+
esc(reason) +
|
|
6629
|
+
". No setting, ruleset, label, or secret state is shown as a concrete value.",
|
|
6630
|
+
},
|
|
6631
|
+
];
|
|
6632
|
+
return;
|
|
6633
|
+
}
|
|
6634
|
+
const value = result.value || {};
|
|
6635
|
+
const settings = value.settings || {};
|
|
6636
|
+
const rulesets = Array.isArray(value.rulesets) ? value.rulesets : [];
|
|
6637
|
+
const labels = Array.isArray(value.labels) ? value.labels : [];
|
|
6638
|
+
const secrets = Array.isArray(value.secrets) ? value.secrets : [];
|
|
6639
|
+
DATA.sections.repository.blocks = [
|
|
6640
|
+
host,
|
|
6641
|
+
{
|
|
6642
|
+
card: "Repository settings",
|
|
6643
|
+
note:
|
|
6644
|
+
"Live from gh api repos/" +
|
|
6645
|
+
String(value.owner || "") +
|
|
6646
|
+
"/" +
|
|
6647
|
+
String(value.repo || ""),
|
|
6648
|
+
rows: githubRepoSettingsRows(settings),
|
|
6649
|
+
},
|
|
6650
|
+
{
|
|
6651
|
+
type: "table",
|
|
6652
|
+
card: "Rulesets",
|
|
6653
|
+
note: "Live from gh api repos/{owner}/{repo}/rulesets",
|
|
6654
|
+
cols: ["Ruleset", "Applies to", "Enforces", "Active"],
|
|
6655
|
+
rows: rulesets.map(row => [
|
|
6656
|
+
{ t: "mono", v: String(row.name || "") },
|
|
6657
|
+
{ t: "text", v: String(row.appliesTo || "—") },
|
|
6658
|
+
{ t: "text", v: String(row.enforces || "—") },
|
|
6659
|
+
{
|
|
6660
|
+
t: "badge",
|
|
6661
|
+
v: row.active ? "active" : "disabled",
|
|
6662
|
+
cls: row.active ? "on" : "off",
|
|
6663
|
+
},
|
|
6664
|
+
]),
|
|
6665
|
+
},
|
|
6666
|
+
{
|
|
6667
|
+
type: "table",
|
|
6668
|
+
card: "Lifecycle labels",
|
|
6669
|
+
note: "Config-expected labels vs live gh label list",
|
|
6670
|
+
cols: ["Label", "Role", "Color", "Status"],
|
|
6671
|
+
rows: labels.map(row => [
|
|
6672
|
+
{ t: "mono", v: String(row.name || "") },
|
|
6673
|
+
{ t: "text", v: String(row.role || "") },
|
|
6674
|
+
row.present && row.color
|
|
6675
|
+
? {
|
|
6676
|
+
t: "color",
|
|
6677
|
+
v: "#" + String(row.color).replace(/^#/u, ""),
|
|
6678
|
+
}
|
|
6679
|
+
: { t: "text", v: "—" },
|
|
6680
|
+
{
|
|
6681
|
+
t: "badge",
|
|
6682
|
+
v: row.present ? "present" : "missing",
|
|
6683
|
+
cls: row.present ? "on" : "off",
|
|
6684
|
+
},
|
|
6685
|
+
]),
|
|
6686
|
+
},
|
|
6687
|
+
{
|
|
6688
|
+
type: "table",
|
|
6689
|
+
card: "Secrets",
|
|
6690
|
+
note: "Presence only from gh secret list — values are never read",
|
|
6691
|
+
cols: ["Secret", "Purpose", "Status"],
|
|
6692
|
+
rows: secrets.map(row => [
|
|
6693
|
+
{ t: "mono", v: String(row.name || "") },
|
|
6694
|
+
{ t: "text", v: String(row.purpose || "") },
|
|
6695
|
+
{
|
|
6696
|
+
t: "badge",
|
|
6697
|
+
v: row.set ? "set" : "missing",
|
|
6698
|
+
cls: row.set ? "on" : "off",
|
|
6699
|
+
},
|
|
6700
|
+
]),
|
|
6701
|
+
},
|
|
6702
|
+
];
|
|
6703
|
+
}
|
|
6704
|
+
function reRenderAfterGithubRepoHydration() {
|
|
6705
|
+
const current = activeSection;
|
|
6706
|
+
renderAll();
|
|
6707
|
+
showSection(current || "overview");
|
|
6708
|
+
}
|
|
6709
|
+
/**
|
|
6710
|
+
* Replace the Deploy pipeline stages table from the live probe.
|
|
6711
|
+
* Never leaves demo hold/no-hold claims in place.
|
|
6712
|
+
* @param probes - Live-status probe map from /api/status
|
|
6713
|
+
*/
|
|
6714
|
+
function applyDeployPipelineStages(probes) {
|
|
6715
|
+
const section = DATA.sections.deploy;
|
|
6716
|
+
if (!section || !Array.isArray(section.blocks)) return;
|
|
6717
|
+
const block = section.blocks.find(
|
|
6718
|
+
candidate =>
|
|
6719
|
+
candidate &&
|
|
6720
|
+
candidate.type === "table" &&
|
|
6721
|
+
candidate.id === "deploy-pipeline-stages"
|
|
6722
|
+
);
|
|
6723
|
+
if (!block) return;
|
|
6724
|
+
const raw =
|
|
6725
|
+
probes && Object.hasOwn(probes, "deploy-pipeline-stages")
|
|
6726
|
+
? probes["deploy-pipeline-stages"]
|
|
6727
|
+
: undefined;
|
|
6728
|
+
const result =
|
|
6729
|
+
raw === undefined
|
|
6730
|
+
? {
|
|
6731
|
+
state: "unknown",
|
|
6732
|
+
reason: "missing-probe",
|
|
6733
|
+
message: "The deploy-pipeline-stages probe was not reported",
|
|
6734
|
+
}
|
|
6735
|
+
: normalizeLiveStatusResult(raw);
|
|
6736
|
+
if (result.state === "not-applicable") {
|
|
6737
|
+
block.rows = [
|
|
6738
|
+
[
|
|
6739
|
+
{ t: "mono", v: "—" },
|
|
6740
|
+
{
|
|
6741
|
+
t: "text",
|
|
6742
|
+
v: "No deploy.yml or github.environments configured for this project",
|
|
6743
|
+
},
|
|
6744
|
+
{ t: "status", v: "unknown", reason: "not-applicable" },
|
|
6745
|
+
],
|
|
6746
|
+
];
|
|
6747
|
+
} else if (
|
|
6748
|
+
result.state === "value" &&
|
|
6749
|
+
result.value &&
|
|
6750
|
+
typeof result.value === "object" &&
|
|
6751
|
+
Array.isArray(result.value.stages)
|
|
6752
|
+
) {
|
|
6753
|
+
block.rows = result.value.stages.map(stage => [
|
|
6754
|
+
{
|
|
6755
|
+
t: "mono",
|
|
6756
|
+
v: String(stage.name || stage.id || "stage"),
|
|
6757
|
+
},
|
|
6758
|
+
{
|
|
6759
|
+
t: "text",
|
|
6760
|
+
v: String(stage.description || ""),
|
|
6761
|
+
},
|
|
6762
|
+
{
|
|
6763
|
+
t: "status",
|
|
6764
|
+
v: stage.active === "unknown" ? "unknown" : stage.active === true,
|
|
6765
|
+
reason:
|
|
6766
|
+
typeof stage.reason === "string" && stage.reason.length > 0
|
|
6767
|
+
? stage.reason
|
|
6768
|
+
: undefined,
|
|
6769
|
+
},
|
|
6770
|
+
]);
|
|
6771
|
+
if (block.rows.length === 0) {
|
|
6772
|
+
block.rows = [
|
|
6773
|
+
[
|
|
6774
|
+
{ t: "mono", v: "—" },
|
|
6775
|
+
{ t: "text", v: "Deploy pipeline reported zero stages" },
|
|
6776
|
+
{
|
|
6777
|
+
t: "status",
|
|
6778
|
+
v: "unknown",
|
|
6779
|
+
reason: "empty-stages",
|
|
6780
|
+
},
|
|
6781
|
+
],
|
|
6782
|
+
];
|
|
6783
|
+
}
|
|
6784
|
+
} else {
|
|
6785
|
+
const reason =
|
|
6786
|
+
result.state === "unknown"
|
|
6787
|
+
? result.reason + ": " + result.message
|
|
6788
|
+
: "invalid-value: deploy-pipeline-stages probe returned an unusable value";
|
|
6789
|
+
block.rows = [
|
|
6790
|
+
[
|
|
6791
|
+
{ t: "mono", v: "—" },
|
|
6792
|
+
{
|
|
6793
|
+
t: "text",
|
|
6794
|
+
v: "Deploy pipeline stages unavailable",
|
|
6795
|
+
},
|
|
6796
|
+
{ t: "status", v: "unknown", reason: reason },
|
|
6797
|
+
],
|
|
6798
|
+
];
|
|
6799
|
+
}
|
|
6800
|
+
const existing = document.getElementById("deploy-pipeline-stages");
|
|
6801
|
+
if (existing) {
|
|
6802
|
+
existing.replaceWith(buildTable(block));
|
|
6803
|
+
}
|
|
6804
|
+
}
|
|
6805
|
+
/* Map the detected-stacks probe into stackDetection. A missing probe or a
|
|
6806
|
+
non-value/non-array result becomes an explicit unknown state — never a
|
|
6807
|
+
false empty and never the metadata catalog. */
|
|
6808
|
+
function applyDetectedStacks(probes) {
|
|
6809
|
+
const raw =
|
|
6810
|
+
probes && Object.hasOwn(probes, "detected-stacks")
|
|
6811
|
+
? probes["detected-stacks"]
|
|
6812
|
+
: undefined;
|
|
6813
|
+
if (raw === undefined) {
|
|
6814
|
+
stackDetection = {
|
|
6815
|
+
status: "unknown",
|
|
6816
|
+
ids: [],
|
|
6817
|
+
reason: "missing-probe",
|
|
6818
|
+
message: "The detected-stacks probe was not reported",
|
|
6819
|
+
};
|
|
6820
|
+
refreshStacks();
|
|
6821
|
+
return;
|
|
6822
|
+
}
|
|
6823
|
+
const result = normalizeLiveStatusResult(raw);
|
|
6824
|
+
if (result.state === "value" && Array.isArray(result.value)) {
|
|
6825
|
+
const ids = result.value.filter(v => typeof v === "string");
|
|
6826
|
+
if (ids.length !== result.value.length) {
|
|
6827
|
+
// Any non-string element makes the whole array untrustworthy;
|
|
6828
|
+
// an empty state here would fabricate a "no stacks" certainty.
|
|
6829
|
+
stackDetection = {
|
|
6830
|
+
status: "unknown",
|
|
6831
|
+
ids: [],
|
|
6832
|
+
reason: "invalid-value",
|
|
6833
|
+
message: "detected-stacks did not return a string array",
|
|
6834
|
+
};
|
|
6835
|
+
} else {
|
|
6836
|
+
stackDetection =
|
|
6837
|
+
ids.length > 0
|
|
6838
|
+
? { status: "value", ids }
|
|
6839
|
+
: { status: "empty", ids: [] };
|
|
6840
|
+
}
|
|
6841
|
+
} else if (result.state === "value") {
|
|
6842
|
+
stackDetection = {
|
|
6843
|
+
status: "unknown",
|
|
6844
|
+
ids: [],
|
|
6845
|
+
reason: "invalid-value",
|
|
6846
|
+
message: "detected-stacks did not return a string array",
|
|
6847
|
+
};
|
|
6848
|
+
} else if (result.state === "not-applicable") {
|
|
6849
|
+
stackDetection = {
|
|
6850
|
+
status: "unknown",
|
|
6851
|
+
ids: [],
|
|
6852
|
+
reason: "not-applicable",
|
|
6853
|
+
message: "detected-stacks reported not applicable",
|
|
6854
|
+
};
|
|
6855
|
+
} else {
|
|
6856
|
+
stackDetection = {
|
|
6857
|
+
status: "unknown",
|
|
6858
|
+
ids: [],
|
|
6859
|
+
reason: result.reason,
|
|
6860
|
+
message: result.message,
|
|
6861
|
+
};
|
|
6862
|
+
}
|
|
6863
|
+
refreshStacks();
|
|
6864
|
+
}
|
|
6865
|
+
function markStacksUnreachable(error) {
|
|
6866
|
+
stackDetection = {
|
|
6867
|
+
status: "unknown",
|
|
6868
|
+
ids: [],
|
|
6869
|
+
reason: "status-unavailable",
|
|
6870
|
+
message: error instanceof Error ? error.message : String(error),
|
|
6871
|
+
};
|
|
6872
|
+
refreshStacks();
|
|
6873
|
+
}
|
|
6874
|
+
function automationsScheduledJobsBlock() {
|
|
6875
|
+
const blocks =
|
|
6876
|
+
(DATA.sections.automations && DATA.sections.automations.blocks) || [];
|
|
6877
|
+
return blocks.find(block => block.id === "automations-scheduled-jobs");
|
|
6878
|
+
}
|
|
6879
|
+
function automationRowFromProbe(entry) {
|
|
6880
|
+
const cadence =
|
|
6881
|
+
typeof entry.cadence === "string" && entry.cadence.trim().length > 0
|
|
6882
|
+
? entry.cadence
|
|
6883
|
+
: "unknown";
|
|
6884
|
+
const lastRun =
|
|
6885
|
+
typeof entry.lastRunAt === "string" &&
|
|
6886
|
+
entry.lastRunAt.trim().length > 0
|
|
6887
|
+
? entry.lastRunAt
|
|
6888
|
+
: "—";
|
|
6889
|
+
const status =
|
|
6890
|
+
typeof entry.status === "string" && entry.status.trim().length > 0
|
|
6891
|
+
? entry.status
|
|
6892
|
+
: "unknown";
|
|
6893
|
+
const runtime =
|
|
6894
|
+
entry.runtime === "claude" ? "claude /schedule" : "codex";
|
|
6895
|
+
const row = [
|
|
6896
|
+
{ t: "mono", v: entry.id },
|
|
6897
|
+
{ t: "badge", v: runtime, cls: "default" },
|
|
6898
|
+
{ t: "mono", v: cadence },
|
|
6899
|
+
{ t: "mono", v: lastRun },
|
|
6900
|
+
{
|
|
6901
|
+
t: "badge",
|
|
6902
|
+
v: status,
|
|
6903
|
+
cls: status.toUpperCase() === "ACTIVE" ? "pass" : "default",
|
|
6904
|
+
},
|
|
6905
|
+
];
|
|
6906
|
+
row._automationId = entry.id;
|
|
6907
|
+
return row;
|
|
6908
|
+
}
|
|
6909
|
+
function hydrateAutomationsSection(probeResult) {
|
|
6910
|
+
const block = automationsScheduledJobsBlock();
|
|
6911
|
+
if (!block) return;
|
|
6912
|
+
const result = normalizeLiveStatusResult(probeResult);
|
|
6913
|
+
if (result.state === "value") {
|
|
6914
|
+
const automations = Array.isArray(result.value.automations)
|
|
6915
|
+
? result.value.automations
|
|
6916
|
+
: [];
|
|
6917
|
+
block.rows = automations.map(automationRowFromProbe);
|
|
6918
|
+
if (block.rows.length === 0) {
|
|
6919
|
+
block.empty = {
|
|
6920
|
+
state: "empty",
|
|
6921
|
+
message:
|
|
6922
|
+
"No lisa-auto-<project>- automations found in the harness scheduler",
|
|
6923
|
+
};
|
|
6924
|
+
} else {
|
|
6925
|
+
delete block.empty;
|
|
6926
|
+
}
|
|
6927
|
+
} else if (result.state === "not-applicable") {
|
|
6928
|
+
block.rows = [];
|
|
6929
|
+
block.empty = {
|
|
6930
|
+
state: "not-applicable",
|
|
6931
|
+
message: "Automations are not applicable on this machine",
|
|
6932
|
+
};
|
|
6933
|
+
} else {
|
|
6934
|
+
block.rows = [];
|
|
6935
|
+
block.empty = {
|
|
6936
|
+
state: "unknown",
|
|
6937
|
+
message: result.reason + ": " + result.message,
|
|
6938
|
+
};
|
|
6939
|
+
}
|
|
6940
|
+
renderAll();
|
|
6941
|
+
showSection(activeSection);
|
|
6942
|
+
}
|
|
6943
|
+
|
|
6312
6944
|
async function hydrateLiveStatuses() {
|
|
6313
6945
|
try {
|
|
6314
6946
|
const response = await fetch("/api/status", {
|
|
@@ -6318,9 +6950,29 @@
|
|
|
6318
6950
|
const snapshot = await response.json();
|
|
6319
6951
|
const probes = liveStatusProbeMap(snapshot);
|
|
6320
6952
|
renderLiveStatuses(probes);
|
|
6321
|
-
|
|
6953
|
+
applyEnabledPlugins(probes);
|
|
6954
|
+
applyDetectedStacks(probes);
|
|
6955
|
+
// Quality jobs / automations re-render the page; paint the version chip after.
|
|
6322
6956
|
applyCiQualityJobs(probes);
|
|
6957
|
+
hydrateAutomationsSection(
|
|
6958
|
+
probes.automations || {
|
|
6959
|
+
state: "unknown",
|
|
6960
|
+
reason: "probe-missing",
|
|
6961
|
+
message: "Automations probe was omitted from /api/status",
|
|
6962
|
+
}
|
|
6963
|
+
);
|
|
6323
6964
|
applyLisaVersion(probes);
|
|
6965
|
+
const repoResult = normalizeLiveStatusResult(
|
|
6966
|
+
probes["github-repo"] || {
|
|
6967
|
+
state: "unknown",
|
|
6968
|
+
reason: "probe-missing",
|
|
6969
|
+
message: "GitHub repository probe did not run",
|
|
6970
|
+
}
|
|
6971
|
+
);
|
|
6972
|
+
applyGithubRepoPanel(repoResult);
|
|
6973
|
+
reRenderAfterGithubRepoHydration();
|
|
6974
|
+
applyDeployPipelineStages(probes);
|
|
6975
|
+
applyObservabilityProviders(probes);
|
|
6324
6976
|
} catch (error) {
|
|
6325
6977
|
const message =
|
|
6326
6978
|
error instanceof Error ? error.message : String(error);
|
|
@@ -6330,8 +6982,21 @@
|
|
|
6330
6982
|
message,
|
|
6331
6983
|
};
|
|
6332
6984
|
renderLiveStatuses({ transport: unavailable });
|
|
6985
|
+
applyEnabledPlugins({ "enabled-plugins": unavailable });
|
|
6333
6986
|
applyCiQualityJobs({ "ci-quality-jobs": unavailable });
|
|
6987
|
+
hydrateAutomationsSection(unavailable);
|
|
6334
6988
|
applyLisaVersion({ "lisa-version": unavailable });
|
|
6989
|
+
applyGithubRepoPanel(unavailable);
|
|
6990
|
+
reRenderAfterGithubRepoHydration();
|
|
6991
|
+
applyDeployPipelineStages({
|
|
6992
|
+
"deploy-pipeline-stages": unavailable,
|
|
6993
|
+
});
|
|
6994
|
+
applyObservabilityProviders({
|
|
6995
|
+
sentry: unavailable,
|
|
6996
|
+
"cloudwatch-alarms": unavailable,
|
|
6997
|
+
"x-ray": unavailable,
|
|
6998
|
+
});
|
|
6999
|
+
markStacksUnreachable(error);
|
|
6335
7000
|
}
|
|
6336
7001
|
}
|
|
6337
7002
|
|