great-cto 3.27.0 → 3.27.2

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "great-cto",
3
3
  "description": "You already have the agent. This is everything around it. great_cto runs Claude Code as a pipeline of 70 specialist agents \u2014 an independent model checks each stage before the next builds on it, spending caps refuse rather than warn, and three decisions stay yours: what gets built, how, and whether it ships.",
4
- "version": "3.27.0",
4
+ "version": "3.27.2",
5
5
  "author": {
6
6
  "name": "Alexander Velikiy",
7
7
  "url": "https://hashnode.com/@Greatcto"
@@ -3035,6 +3035,44 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3035
3035
  @media (prefers-reduced-motion: reduce) {
3036
3036
  *, *::before, *::after { transition-duration: 1ms !important; animation-duration: 1ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
3037
3037
  }
3038
+ /* ── Fidelity pass (v3.27.1): the density the design canvas draws. These come
3039
+ after the rules they override — same specificity, later wins. ── */
3040
+ .metric-card { padding: 14px 16px; }
3041
+ .metric-card::before { display: none; }
3042
+ .metric-num { font-size: var(--fs-num-m); }
3043
+ .metric-label { margin-top: 6px; }
3044
+ .inbox-greet { font-family: var(--sans); font-size: var(--fs-small); color: var(--text2); line-height: 1.5; margin: 0 0 12px; font-weight: 400; letter-spacing: 0; }
3045
+ .inbox-row { padding: 8px 12px; gap: 12px; }
3046
+ .inbox-row .ttl { font-size: var(--fs-small); }
3047
+ /* A stalled task is one line: id · reason · title · agent, and the re-run button
3048
+ sits at the row's right instead of on a second grid row under it. */
3049
+ .inbox-row { grid-template-columns: 110px 1fr auto auto auto; align-items: center; }
3050
+ .inbox-row .actions:empty { display: none; }
3051
+ .ledger-title-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 12px; }
3052
+ .side-task-actions .gate-btn { flex: 0 0 auto; min-width: 96px; }
3053
+ .inbox-row .ttl { flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
3054
+ .inbox-row .ttl > :first-child { display: inline; }
3055
+ .inbox-row .ttl .why { display: inline-block; font-size: var(--fs-eyebrow); padding: 0 6px; line-height: 18px; white-space: nowrap; }
3056
+ .inbox-row .ttl .meta { margin-top: 0; }
3057
+ .inbox-row .ttl .ttl-desc { flex-basis: 100%; -webkit-line-clamp: 1; margin-top: 0; }
3058
+ .inbox-row .id { padding-top: 0; }
3059
+ .agent-row .budget-chip { display: block; font-size: var(--fs-eyebrow); line-height: 14px; padding: 0 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 96px; margin-left: auto; }
3060
+ .inbox-section-head h3 { font-size: var(--fs-small); }
3061
+ .fleet-title-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 12px; }
3062
+ .fleet-controls-row { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; }
3063
+ .fleet-controls-row input[type="search"] { width: 260px; max-width: 100%; background: var(--bg-muted); border: 1px solid var(--border); color: var(--text); font-family: var(--mono); font-size: var(--fs-small); padding: 6px 10px; }
3064
+ .fleet-controls-row select { background: var(--bg-muted); border: 1px solid var(--border); color: var(--text); font-family: var(--mono); font-size: var(--fs-small); padding: 5px 8px; }
3065
+ .fleet-list { padding: 0; margin: 0; list-style: none; }
3066
+ .fleet-list > li { margin: 0; }
3067
+ .fleet-head, .agent-row { display: grid; grid-template-columns: 10px minmax(200px, 1fr) minmax(220px, 1.3fr) 130px 48px 56px 100px 16px; gap: 12px; align-items: center; }
3068
+ .fleet-head { padding: 6px 12px; font-size: var(--fs-eyebrow); letter-spacing: .08em; text-transform: uppercase; color: var(--text2); border-bottom: 1px solid var(--border); }
3069
+ .agent-row { min-height: 36px; padding: 4px 12px; border-radius: 0; border: none; border-bottom: 1px solid var(--border); width: 100%; }
3070
+ .agent-row .ar-name { font-size: var(--fs-small); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
3071
+ .agent-row .ar-posture { display: flex; gap: 6px; flex-wrap: nowrap; overflow: hidden; }
3072
+ .agent-row .ar-status { width: 6px; height: 6px; border-radius: 50%; }
3073
+ .agent-row .ar-chev { color: var(--text3); }
3074
+ .budgets-table .bt-desc { display: none; }
3075
+ .mp-card { padding: 14px 16px; }
3038
3076
  /* ── 768–1199: the sidebar becomes a 56px icon rail (DESIGN §9). Labels leave
3039
3077
  the screen but not the element — every nav item carries its name as
3040
3078
  aria-label and title. The Views block and the switcher's text go with the
@@ -3056,8 +3094,15 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3056
3094
 
3057
3095
  .harness-hosts, .so-options { grid-template-columns: 1fr; }
3058
3096
 
3059
- .ar-cells { grid-template-columns: 1fr auto; }
3060
- .ar-posture { grid-column: 1 / -1; }
3097
+ .fleet-head { display: none; }
3098
+ .agent-row { grid-template-columns: 10px 1fr auto auto auto 16px; grid-template-areas: "dot name name name name chev" "dot posture posture posture posture chev" "dot last runs pass spend chev"; row-gap: 4px; padding: 8px 12px; }
3099
+ .agent-row .ar-status { grid-area: dot; }
3100
+ .agent-row .ar-name { grid-area: name; white-space: normal; }
3101
+ .agent-row .ar-posture { grid-area: posture; flex-wrap: wrap; }
3102
+ .agent-row .ar-last { grid-area: last; }
3103
+ .agent-row .ar-chev { grid-area: chev; }
3104
+ .fleet-controls-row { flex-wrap: wrap; }
3105
+ .fleet-controls-row input[type="search"] { width: 100%; }
3061
3106
 
3062
3107
  /* The one asymmetry DESIGN §3.2 draws: an expensive or unclassified gate's
3063
3108
  Reject/Approve stack full-width at phone width; a routine gate's may sit
@@ -3319,6 +3364,10 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3319
3364
  <!-- Inbox -->
3320
3365
  <div class="panel active" id="panel-inbox">
3321
3366
  <div class="inbox-page">
3367
+ <!-- Fidelity pass (v3.27.1): the design's header is the screen's name and
3368
+ its count; the sentence the board used to shout in 52px display
3369
+ type is still said, one line, muted, because it carries the stall. -->
3370
+ <div class="panel-head" style="margin-bottom:6px"><h1 class="page-title" style="margin:0">Decisions <span id="decisions-count" class="muted" style="font-weight:400;font-size:var(--fs-title-s)"></span></h1></div>
3322
3371
  <div class="inbox-greet" id="inbox-greet">Good morning. Here's what needs your decision.</div>
3323
3372
  <!-- Top rung of the evidence ladder: are the bytes that were reviewed
3324
3373
  still the bytes in the tree? Hidden while they match — and now only
@@ -3507,34 +3556,22 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3507
3556
  the redesign: a count of installed agents is not a decision (brief,
3508
3557
  "What must NOT be on the board"). The view title carries the one
3509
3558
  number that is — how many need attention, of how many. -->
3510
- <section aria-label="Fleet summary">
3511
- <h1 class="page-title" id="fleet-view-title">Fleet</h1>
3559
+ <section aria-label="Fleet summary" class="fleet-title-row">
3560
+ <h1 class="page-title" id="fleet-view-title" style="margin:0">Fleet</h1>
3512
3561
  </section>
3513
3562
 
3514
3563
  <!-- §3.1 Fleet controls -->
3515
- <section aria-label="Fleet controls" style="margin-top:20px">
3516
- <div class="mp-card fleet-controls" style="display:flex;flex-wrap:wrap;align-items:center;gap:12px 20px;padding:8px 12px">
3517
- <div class="fleet-search">
3518
- <input type="search" id="fleet-search" placeholder="Filter agents (press / to focus)" aria-label="Filter agents by slug" />
3519
- <span class="search-hint"><kbd>/</kbd> focus · <kbd>Enter</kbd> open · <kbd>Esc</kbd> close</span>
3520
- </div>
3521
- <!-- The Domain / Activity / Health facet chips left with the redesign:
3522
- domain is the grouping, activity and health are the saved views in
3523
- the sidebar (DESIGN §5 — five views, not user-creatable). The
3524
- functions stay for the drawer's deep links. -->
3525
- <div class="fleet-sort">
3526
- <label for="fleet-sort-by">Sort by</label>
3527
- <select id="fleet-sort-by">
3528
- <option value="runs_30d">runs (30d)</option>
3529
- <option value="last_run">last run</option>
3530
- <option value="success_rate">success rate</option>
3531
- <option value="savings_x">savings ×</option>
3532
- <option value="llm_usd_30d_est">LLM spend (30d)</option>
3533
- <option value="slug">slug A→Z</option>
3534
- </select>
3535
- <button type="button" id="fleet-reset-filters">Reset filters</button>
3536
- </div>
3537
- </div>
3564
+ <section aria-label="Fleet controls" class="fleet-controls-row">
3565
+ <input type="search" id="fleet-search" placeholder="Filter agents ( / )" aria-label="Filter agents by slug" />
3566
+ <label for="fleet-sort-by" class="muted cap">Sort</label>
3567
+ <select id="fleet-sort-by">
3568
+ <option value="runs_30d">runs (30d)</option>
3569
+ <option value="last_run">last run</option>
3570
+ <option value="success_rate">success rate</option>
3571
+ <option value="llm_usd_30d_est">LLM spend (30d)</option>
3572
+ <option value="slug">slug A→Z</option>
3573
+ </select>
3574
+ <button type="button" id="fleet-reset-filters" class="btn-ghost-sm">Reset</button>
3538
3575
  </section>
3539
3576
 
3540
3577
  <!-- §3.1 Fleet list -->
@@ -3595,7 +3632,21 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3595
3632
 
3596
3633
  <div class="panel" id="panel-budgets" role="tabpanel">
3597
3634
  <div class="metrics-page">
3598
- <h1 class="page-title">Ledger</h1>
3635
+ <div class="ledger-title-row">
3636
+ <h1 class="page-title" style="margin:0">Ledger</h1>
3637
+ <!-- The period governs every figure below it, so it sits where the
3638
+ figures start — beside the name, not under the stuck list. -->
3639
+ <div class="period-toolbar" style="margin:0">
3640
+ <span class="period-label">PERIOD</span>
3641
+ <div class="period-chips" id="period-chips">
3642
+ <button class="period-chip" data-period="1">1D</button>
3643
+ <button class="period-chip" data-period="7">7D</button>
3644
+ <button class="period-chip active" data-period="30">30D</button>
3645
+ <button class="period-chip" data-period="90">90D</button>
3646
+ <button class="period-chip" data-period="365">1Y</button>
3647
+ </div>
3648
+ </div>
3649
+ </div>
3599
3650
  <!-- Ledger (great_cto-ki1x.7): the one decision this screen supports is
3600
3651
  "is something running that I should stop". Two figures, the stage
3601
3652
  strip and the stuck list answer it; the caps table below is where
@@ -3604,16 +3655,6 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3604
3655
  <!-- From the old Metrics tab (great_cto-ki1x.16): the period chips, the caps
3605
3656
  card and the cost panel keep their ids — the code that fills them did
3606
3657
  not move, only the screen that shows them. -->
3607
- <div class="period-toolbar">
3608
- <span class="period-label">PERIOD</span>
3609
- <div class="period-chips" id="period-chips">
3610
- <button class="period-chip" data-period="1">1D</button>
3611
- <button class="period-chip" data-period="7">7D</button>
3612
- <button class="period-chip active" data-period="30">30D</button>
3613
- <button class="period-chip" data-period="90">90D</button>
3614
- <button class="period-chip" data-period="365">1Y</button>
3615
- </div>
3616
- </div>
3617
3658
  <!-- Budgets live HERE, on Metrics, not two clicks deep in the Agents
3618
3659
  drill-in. A control the operator could not find is a control they do
3619
3660
  not have — and it was invisible on top of that, hidden whenever a
@@ -3621,16 +3662,11 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
3621
3662
  needs the button to declare one. -->
3622
3663
  <div class="mp-section-label" style="margin-top:24px">Agent budgets</div>
3623
3664
  <div id="agent-budgets" class="mp-card" style="padding:12px 16px;font-size:var(--fs-small);margin-bottom:4px;"></div>
3624
- <div class="mp-section-label" id="mp-cost-label" style="margin-top:24px">Cost · last 30 days</div>
3625
- <div class="mp-card cost-panel" id="cost-panel">
3626
- <div class="cost-empty">Loading…</div>
3627
- </div>
3628
- <p class="muted" style="max-width:70ch;margin:14px 0 12px">
3629
- A cap on what one agent may spend. When measured spend passes it, the pipeline
3630
- refuses to dispatch that agent and says so — it does not fail quietly.
3631
- Spend is measured from verdicts that carry a cost; while none does, the cap
3632
- cannot fire and says that too. An estimate never refuses.
3633
- </p>
3665
+ <!-- The old cost panel (four tiles: 'vs human team', a projected month, a
3666
+ daily burn, an empty chart) left in the fidelity pass — a forecast and a
3667
+ human-team comparison are two of the brief's refusals, and the
3668
+ Ledger head already carries spend with its provenance. -->
3669
+
3634
3670
  <!-- Whether the judge is connected at all. Without a key nothing breaks
3635
3671
  — every stage is scored `unverifiable`, honestly — but "not
3636
3672
  connected" and "nothing to check" read the same from here, and the
@@ -3957,6 +3993,7 @@ function gateChip(t) {
3957
3993
  }
3958
3994
  function renderInbox(d) {
3959
3995
  inboxSecondOpinion = d?.second_opinion || null;
3996
+ { const c = document.getElementById('decisions-count'); const n = (d?.pending_gates || []).length; if (c) c.textContent = n ? `${n} waiting` : 'nothing waiting'; }
3960
3997
  for (const g of (d?.pending_gates || [])) if (g?.id && g.reversibility) GATE_REVERSIBILITY[g.id] = g.reversibility;
3961
3998
  if (d && Array.isArray(d.pending_gates)) d = { ...d, pending_gates: sortGatesByCostOfUndo(d.pending_gates) };
3962
3999
  // Kept so renderResume can ask what this section is already showing, rather
@@ -4523,6 +4560,11 @@ function renderInboxList(rootId, items, countId, opts = {}) {
4523
4560
  + `<button type="button" class="gate-btn" title="Copy a git diff limited to the reviewed files"`
4524
4561
  + ` onclick="event.stopPropagation(); copyDiff(${esc(JSON.stringify(JSON.stringify(t.__files)))})">Copy diff</button></div>`
4525
4562
  : '';
4563
+ const unblock = (!canDecide && !t.__synthetic && t.id && (t.raw_status || t.status) === 'blocked')
4564
+ ? `<div class="actions" onclick="event.stopPropagation()">`
4565
+ + `<button type="button" class="gate-btn gate-approve" title="Set the task back to open"`
4566
+ + ` onclick="event.stopPropagation(); taskDecision('${esc(t.id)}', 'open')">Unblock</button></div>`
4567
+ : '';
4526
4568
  const rerun = (!canDecide && !t.__synthetic && t.agent && t.id)
4527
4569
  ? `<div class="actions" onclick="event.stopPropagation()">`
4528
4570
  + `<button type="button" class="gate-btn" title="Copy the line that re-runs this agent"`
@@ -4577,7 +4619,7 @@ function renderInboxList(rootId, items, countId, opts = {}) {
4577
4619
  ${canDecide ? gateChip(t) : `<span class="id">${esc(t.id || '')}</span>`}
4578
4620
  <span class="ttl">${canDecide ? '' : reasonChip}${esc(canDecide ? String(t.title || '').replace(/^gate:[a-z-]+\s*[—–-]?\s*/, '') : t.title)}${desc}<span class="meta">${meta}</span>${tierNote}</span>
4579
4621
  ${canDecide ? reviewerCells(t) : ''}
4580
- ${actions}${rerun}${diffBtn}
4622
+ ${actions}${unblock}${rerun}${diffBtn}
4581
4623
  </div>`;
4582
4624
  }).join('');
4583
4625
  }
@@ -7847,11 +7889,22 @@ function openSide(task) {
7847
7889
  Reject
7848
7890
  </button>
7849
7891
  </div>` : '';
7892
+ // A task that is not a gate still needs a decision the operator can take
7893
+ // here: a blocked one is unblocked, an open one is started, either is closed.
7894
+ // Before this the drawer showed the status and told the operator to go run
7895
+ // `bd update` — a board that could read the state but not change it.
7896
+ const taskActions = (!task.is_gate && rawStatus !== 'closed') ? `
7897
+ <div class="side-gate-actions side-task-actions">
7898
+ ${rawStatus === 'blocked' ? `<button class="gate-btn gate-approve" onclick="taskDecision('${esc(task.id)}', 'open')">Unblock</button>` : ''}
7899
+ ${rawStatus === 'open' ? `<button class="gate-btn gate-approve" onclick="taskDecision('${esc(task.id)}', 'in_progress')">Start</button>` : ''}
7900
+ ${rawStatus === 'in_progress' || rawStatus === 'open' ? `<button class="gate-btn" onclick="taskDecision('${esc(task.id)}', 'blocked')">Block</button>` : ''}
7901
+ <button class="gate-btn gate-reject" onclick="taskDecision('${esc(task.id)}', 'closed')">Close</button>
7902
+ </div>` : '';
7850
7903
  _sideTask = task; // so the doc viewer's "← back" can restore this task
7851
7904
  document.getElementById('side-body').innerHTML = `
7852
7905
  <div class="side-title">${esc(task.title)}</div>
7853
7906
  ${desc}
7854
- ${gateActions}
7907
+ ${gateActions}${taskActions}
7855
7908
  <div class="props">
7856
7909
  ${rows.map(([k, v]) => `<div class="prop-key">${k}</div><div class="prop-val">${v}</div>`).join('')}
7857
7910
  </div>
@@ -7884,6 +7937,23 @@ function openSide(task) {
7884
7937
  </div>`).join('')}`;
7885
7938
  });
7886
7939
  }
7940
+ // One status change, through the endpoint the board has had since the kanban:
7941
+ // POST /api/tasks/:id/status. Closing asks once — it is the only transition the
7942
+ // list stops showing; the rest are visible and reversible from the same drawer.
7943
+ async function taskDecision(id, status) {
7944
+ if (status === 'closed' && !confirm(`Close ${id}? It leaves every list on this board.`)) return;
7945
+ const r = await api(`/api/tasks/${encodeURIComponent(id)}/status${pqs()}`, {
7946
+ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status }),
7947
+ }).catch(() => null);
7948
+ if (!r || r.error) { showToast('✗ ' + esc(r?.error || 'status not changed'), 'error', 4000); return; }
7949
+ const said = { open: 'unblocked', in_progress: 'started', blocked: 'blocked', closed: 'closed' }[status] || status;
7950
+ showToast(`<strong>${esc(id)}</strong> ${said}`, status === 'closed' ? 'error' : 'success', 2500);
7951
+ if (_sideTask && _sideTask.id === id) {
7952
+ if (status === 'closed') closeSide();
7953
+ else openSide({ ..._sideTask, status, raw_status: status });
7954
+ }
7955
+ refreshInbox();
7956
+ }
7887
7957
  // ── Documents referenced by a task: extraction + the inline side-panel viewer ──
7888
7958
  let _sideTask = null;
7889
7959
  function extractDocRefs(task) {
@@ -8201,7 +8271,7 @@ function switchTab(id, el) {
8201
8271
  // promised 50 unread and the screen showed settings. Declared and not
8202
8272
  // consumed, in the UI this time.
8203
8273
  if (lid === 'notifications') loadNotifHistory();
8204
- if (lid === 'budgets') { loadBudgetsPage(); refreshCost(); }
8274
+ if (lid === 'budgets') loadBudgetsPage();
8205
8275
  if (lid === 'agents') {
8206
8276
  // pqsd(), not pqs(): this call omitted `days` entirely, so the server applied
8207
8277
  // its 30-day default and overwrote whatever window the period chips had
@@ -8530,7 +8600,8 @@ function renderFleetList() {
8530
8600
  const order = [...DOMAINS, ...Object.keys(byDomain).filter((d) => !DOMAINS.includes(d))];
8531
8601
  const expanded = order.filter((d) => (byDomain[d] || []).length);
8532
8602
  const collapsed = order.filter((d) => !(byDomain[d] || []).length && totals[d]);
8533
- const rows = expanded.map((d) => `
8603
+ const head = `<li class="fleet-head" aria-hidden="true"><span></span><span>Agent</span><span>Posture</span><span>Last verdict</span><span class="tab">Runs</span><span class="tab">Pass</span><span class="tab">30d $</span><span></span></li>`;
8604
+ const rows = head + expanded.map((d) => `
8534
8605
  <li class="fleet-group-head"><span>${esc(d)} · ${byDomain[d].length} of ${totals[d] || 0}</span></li>
8535
8606
  ${byDomain[d].map(agentRowHtml).join('')}`).join('');
8536
8607
  const tail = collapsed.length
@@ -8586,18 +8657,12 @@ function agentRowHtml(a) {
8586
8657
  onclick="openAgentDrawer('${esc(a.slug)}')"
8587
8658
  onkeydown="if(event.key==='Enter'){event.preventDefault();openAgentDrawer('${esc(a.slug)}')}">
8588
8659
  <span class="ar-status" aria-hidden="true"></span>
8589
- <span class="ar-body">
8590
- <span class="ar-name">${esc(a.slug)} <span class="mono cap muted" style="font-weight:400">${model}</span></span>
8591
- <span class="ar-cells">
8592
- <span class="ar-posture">${postureChips(a)}</span>
8593
- <span class="mono cap ar-last">${last}</span>
8594
- <span class="mono cap tab" title="runs in the last 30 days">${runs}</span>
8595
- <span class="mono cap tab" title="pass rate over those runs">${pass}</span>
8596
- <span class="mono cap tab" title="LLM spend, last 30 days">${spend}</span>
8597
- ${budgetChip(a.budget)}
8598
- ${a.retired ? '<span style="color:var(--agent-retired)">retired</span>' : ''}
8599
- </span>
8600
- </span>
8660
+ <span class="ar-name">${esc(a.slug)} <span class="mono cap muted" style="font-weight:400">${model}</span>${a.retired ? ' <span class="cap" style="color:var(--agent-retired)">retired</span>' : ''}</span>
8661
+ <span class="ar-posture">${postureChips(a)}</span>
8662
+ <span class="mono cap ar-last">${last}</span>
8663
+ <span class="mono cap tab" title="runs in the last 30 days">${runs}</span>
8664
+ <span class="mono cap tab" title="pass rate over those runs">${pass}</span>
8665
+ <span class="mono cap tab" title="LLM spend, last 30 days">${spend}${budgetChip(a.budget)}</span>
8601
8666
  <span class="ar-chev" aria-hidden="true">›</span>
8602
8667
  </button>
8603
8668
  </li>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "3.27.0",
3
+ "version": "3.27.2",
4
4
  "description": "One command install for the great_cto Claude Code plugin. Auto-detects your stack, picks the right archetype, bootstraps PROJECT.md.",
5
5
  "keywords": [
6
6
  "claude-code",