tink-harness 1.9.10 → 1.9.12

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": "tink",
3
3
  "description": "A small harness layer for Claude Code and Codex.",
4
- "version": "1.9.10",
4
+ "version": "1.9.12",
5
5
  "author": {
6
6
  "name": "dotori"
7
7
  }
package/CHANGELOG.md CHANGED
@@ -6,6 +6,26 @@ All notable changes to Tink are tracked here.
6
6
 
7
7
  No unreleased changes yet.
8
8
 
9
+ ## [1.9.12] - 2026-06-11
10
+
11
+ ### Changed
12
+
13
+ - Restyled the dashboard with a warm editorial palette (charcoal/ivory/amber) in place of the generic black-and-blue look, with softer semantic tones and larger radii.
14
+ - Improved harness-tab readability: bigger card titles, labels, detail text, and history entries with more breathing room.
15
+
16
+ ### Added
17
+
18
+ - Next-action suggestions now provide copy-paste commands for both Claude Code (`/tink:...`) and Codex (`$tink:...`).
19
+ - README (EN/KO) refreshed: latest-package summary, release badge, and an up-to-date description of the tabbed dashboard, 3D harness map, and next-action panel.
20
+
21
+ ## [1.9.11] - 2026-06-11
22
+
23
+ ### Changed
24
+
25
+ - Home health-group selection no longer carries over into the harness map or cards - it only opens the inline list and next-action suggestion on Home.
26
+ - Removed the Move/Rotate toggle: left-drag always pans, right-drag always rotates, and the in-map hint reflects this fixed mapping.
27
+ - The selected-node panel now starts empty ("no harness selected") instead of showing an arbitrary first harness, and uses the same tone-chip format as click selections; next-action phrasing was neutralized to fit single harnesses as well as groups.
28
+
9
29
  ## [1.9.10] - 2026-06-11
10
30
 
11
31
  ### Changed
package/README.ko.md CHANGED
@@ -8,7 +8,7 @@ Claude Code와 Codex를 위한 작은 하네스 레이어입니다.
8
8
 
9
9
  Tink는 지금 작업에 맞는 하네스를 고르고, 실행 상태를 보이게 만들고, 실제 사용 중 생긴 실패와 피드백으로 하네스 세트를 개선합니다.
10
10
 
11
- **최신 패키지:** v1.9.1 — 하네스 건강 요약, graph, timeline, 후보 점수, 생애주기 상태, 정적 로컬 리포트를 추가합니다. 전체 변경 이력은 [CHANGELOG](CHANGELOG.md)를 확인하세요.
11
+ **최신 패키지:** v1.9.12로컬 건강 리포트가 탭형 대시보드로 바뀌었습니다. 3D 하네스 지도, 쉬운 말 건강 요약, Claude Code와 Codex 양쪽 복사-붙여넣기 명령이 포함된 다음 행동 제안을 제공합니다. 전체 변경 이력은 [CHANGELOG](CHANGELOG.md)를 확인하세요.
12
12
 
13
13
  [English](README.md) · **한국어** · [변경 이력](CHANGELOG.md)
14
14
 
@@ -131,7 +131,7 @@ node .tink/tools/generate-harness-lifecycle-summary.mjs
131
131
  node .tink/tools/render-harness-health-report.mjs
132
132
  ```
133
133
 
134
- 리포트는 정적 대시보드 형태입니다. graph overview, 최근 run timeline, 하네스별 카드를 보여주지만 서버를 시작하거나, 파일을 감시하거나, hidden cache를 만들거나, 새 public `tink index` 명령을 추가하지 않습니다.
134
+ 리포트는 정적인 탭형 로컬 페이지입니다. 요약, 사용량 순 하네스 카드와 평가·생성 히스토리, 메모리 참조, run 활동 피드, 그리고 인터랙티브 3D 하네스 지도(드래그 이동, 우클릭 드래그 회전, 휠 확대 — three.js를 CDN에서 불러오며 오프라인이면 안내가 표시됩니다)를 제공합니다. 하네스나 건강 그룹을 선택하면 쉬운 말 요약과 함께 Claude Code(`/tink:...`)·Codex(`$tink:...`) 양쪽 복사용 명령이 포함된 다음 행동을 제안합니다. 여전히 서버를 시작하거나, 파일을 감시하거나, hidden cache를 만들거나, 새 public `tink index` 명령을 추가하지 않습니다 — 제안만 하며, 재사용 상태 변경은 기존 승인 절차를 그대로 따릅니다.
135
135
 
136
136
  선택된 하네스에 따라 `.tink/current/goals.json`에는 현재 실행의 목표 체크포인트가, `.tink/current/delegation.md`에는 인수인계 패킷이 추가될 수 있습니다. Tink는 이런 브리프를 보이는 상태로 준비하지만, 별도 승인된 워크플로가 아니면 worker, tmux pane, worktree를 시작하지 않습니다.
137
137
 
package/README.md CHANGED
@@ -17,14 +17,14 @@
17
17
  </p>
18
18
 
19
19
  <p>
20
- <a href="https://github.com/dotoricode/tink-harness/releases/tag/v1.9.1"><img src="https://img.shields.io/github/v/release/dotoricode/tink-harness?label=release&color=2ea44f" alt="GitHub release"></a>
20
+ <a href="https://github.com/dotoricode/tink-harness/releases/tag/v1.9.12"><img src="https://img.shields.io/github/v/release/dotoricode/tink-harness?label=release&color=2ea44f" alt="GitHub release"></a>
21
21
  <a href="https://www.npmjs.com/package/tink-harness"><img src="https://img.shields.io/npm/v/tink-harness?label=npm&color=cb3837" alt="npm version"></a>
22
22
  <a href="https://github.com/dotoricode/tink-harness/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/dotoricode/tink-harness/ci.yml?branch=main&label=ci" alt="CI"></a>
23
23
  <a href="https://github.com/dotoricode/tink-harness/blob/main/LICENSE"><img src="https://img.shields.io/github/license/dotoricode/tink-harness" alt="License"></a>
24
24
  <a href="https://github.com/dotoricode/tink-harness/stargazers"><img src="https://img.shields.io/github/stars/dotoricode/tink-harness?style=social" alt="GitHub stars"></a>
25
25
  </p>
26
26
 
27
- <p><strong>Latest package:</strong> v1.9.1 - Adds harness health summaries with graph, timeline, scoring, lifecycle states, and a static local report. See <a href="CHANGELOG.md">CHANGELOG</a> for release history.</p>
27
+ <p><strong>Latest package:</strong> v1.9.12 - The local health report is now a tabbed dashboard with a 3D harness map, plain-language health summaries, and next-action suggestions with copy-paste commands for both Claude Code and Codex. See <a href="CHANGELOG.md">CHANGELOG</a> for release history.</p>
28
28
 
29
29
  **English** · [한국어](README.ko.md) · [Changelog](CHANGELOG.md)
30
30
 
@@ -210,7 +210,7 @@ node .tink/tools/generate-harness-lifecycle-summary.mjs
210
210
  node .tink/tools/render-harness-health-report.mjs
211
211
  ```
212
212
 
213
- The report is a static dashboard-style page with a graph overview, recent run timeline, and one card per harness. It does not start a server, watch files, create a hidden cache, or add a public `tink index` command.
213
+ The report is a static, tabbed local page: a home overview, usage-sorted harness cards with an evaluation/maintenance history, memory references, a run activity feed, and an interactive 3D harness map (drag to move, right-drag to rotate, wheel to zoom; rendered with three.js from a CDN, with an offline notice when unavailable). Selecting a harness or health group shows a plain-language summary plus a suggested next action with copy-paste commands for both Claude Code (`/tink:...`) and Codex (`$tink:...`). It still does not start a server, watch files, create a hidden cache, or add a public `tink index` command - suggestions only; reusable-state changes keep their approval gates.
214
214
 
215
215
  When selected, current-run artifacts may also include `.tink/current/goals.json` for goal checkpoints or `.tink/current/delegation.md` for handoff packets. Tink prepares those briefs as visible state; it does not start workers, tmux panes, or worktrees unless a separate approved workflow does so.
216
216
 
package/VERSIONING.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Versioning
2
2
 
3
- Current version: `1.9.10`
3
+ Current version: `1.9.12`
4
4
 
5
5
  Tink follows semver from `1.0.0` onward.
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tink-harness",
3
- "version": "1.9.10",
3
+ "version": "1.9.12",
4
4
  "description": "Self-growing harnesses for Claude Code and Codex.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -161,11 +161,11 @@ const COPY = {
161
161
  frog_candidate: ['Cleanup review', 'bad']
162
162
  },
163
163
  actions: {
164
- keep: { what: 'This group is healthy and proven by records.', next: 'No action needed - just keep using it.', command: '', expect: '' },
165
- observe: { what: 'Not enough records to judge yet.', next: 'Keep working normally with /tink:cast so records accumulate.', command: '/tink:cast', expect: 'What happens: it picks the right harness for your task, plans the run, and the work gets recorded - improving future judgments.' },
166
- weave: { what: 'Repeated friction was recorded here.', next: 'Run /tink:weave to prepare a small improvement proposal.', command: '/tink:weave', expect: 'What happens: it drafts an improvement (e.g. updated instructions) from the recorded friction and shows it to you. Nothing changes until you approve.' },
167
- merge_candidate: { what: 'These are often used together with another harness.', next: 'Run /tink:weave to check the overlap and decide whether to combine them.', command: '/tink:weave', expect: 'What happens: it inspects the overlap evidence and proposes whether to combine or keep separate. Nothing changes until you approve.' },
168
- frog_candidate: { what: 'Repeated trouble plus high cost - cleanup candidates.', next: 'Run /tink:frog to review archive/delete with approval.', command: '/tink:frog', expect: 'What happens: it lists cleanup candidates with their evidence. Archiving or deleting only happens after your explicit approval.' }
164
+ keep: { what: 'Healthy and proven by records.', next: 'No action needed - just keep using it.', command: '', expect: '' },
165
+ observe: { what: 'Not enough records to judge yet.', next: 'Keep working normally with /tink:cast so records accumulate.', command: 'tink:cast', expect: 'What happens: it picks the right harness for your task, plans the run, and the work gets recorded - improving future judgments.' },
166
+ weave: { what: 'Repeated friction shows up in the records.', next: 'Run /tink:weave to prepare a small improvement proposal.', command: 'tink:weave', expect: 'What happens: it drafts an improvement (e.g. updated instructions) from the recorded friction and shows it to you. Nothing changes until you approve.' },
167
+ merge_candidate: { what: 'Often used together with another harness.', next: 'Run /tink:weave to check the overlap and decide whether to combine them.', command: 'tink:weave', expect: 'What happens: it inspects the overlap evidence and proposes whether to combine or keep separate. Nothing changes until you approve.' },
168
+ frog_candidate: { what: 'Repeated trouble plus high cost - cleanup candidates.', next: 'Run /tink:frog to review archive/delete with approval.', command: 'tink:frog', expect: 'What happens: it lists cleanup candidates with their evidence. Archiving or deleting only happens after your explicit approval.' }
169
169
  },
170
170
  recPlain: {
171
171
  keep: 'Working well - keep using it as is.',
@@ -299,11 +299,11 @@ COPY.ko = {
299
299
  frog_candidate: ['정리 검토', 'bad']
300
300
  },
301
301
  actions: {
302
- keep: { what: '기록으로 검증된 건강한 그룹이에요.', next: '별도 행동이 필요 없어요 — 계속 사용하면 됩니다.', command: '', expect: '' },
303
- observe: { what: '아직 판단할 기록이 부족해요.', next: '/tink:cast로 평소처럼 작업하면 기록이 쌓여 판단이 정확해져요.', command: '/tink:cast', expect: '실행하면: 작업에 맞는 하네스를 골라 계획을 세우고, 끝나면 이번 작업이 기록으로 남아 다음 판단이 정확해져요.' },
304
- weave: { what: '반복되는 마찰이 기록된 그룹이에요.', next: '/tink:weave를 실행해 작은 개선 제안을 준비하세요.', command: '/tink:weave', expect: '실행하면: 기록된 마찰을 바탕으로 개선안(지침 수정 등)을 만들어 보여줘요. 승인하기 전에는 아무것도 바뀌지 않아요.' },
305
- merge_candidate: { what: '다른 하네스와 자주 함께 쓰이는 그룹이에요.', next: '/tink:weave로 겹침을 확인하고 합칠지 검토하세요.', command: '/tink:weave', expect: '실행하면: 겹침 근거를 살펴보고 합칠지 따로 둘지 제안해 줘요. 승인 전에는 아무것도 바뀌지 않아요.' },
306
- frog_candidate: { what: '반복 문제와 높은 비용이 기록됐어요 — 정리 후보예요.', next: '/tink:frog로 보관/삭제 검토를 승인 절차와 함께 진행하세요.', command: '/tink:frog', expect: '실행하면: 정리 후보와 근거를 정리해 보여줘요. 보관·삭제는 명시적으로 승인해야만 실제로 진행돼요.' }
302
+ keep: { what: '기록으로 검증되어 있어요.', next: '별도 행동이 필요 없어요 — 계속 사용하면 됩니다.', command: '', expect: '' },
303
+ observe: { what: '아직 판단할 기록이 부족해요.', next: '/tink:cast로 평소처럼 작업하면 기록이 쌓여 판단이 정확해져요.', command: 'tink:cast', expect: '실행하면: 작업에 맞는 하네스를 골라 계획을 세우고, 끝나면 이번 작업이 기록으로 남아 다음 판단이 정확해져요.' },
304
+ weave: { what: '반복되는 마찰이 기록되어 있어요.', next: '/tink:weave를 실행해 작은 개선 제안을 준비하세요.', command: 'tink:weave', expect: '실행하면: 기록된 마찰을 바탕으로 개선안(지침 수정 등)을 만들어 보여줘요. 승인하기 전에는 아무것도 바뀌지 않아요.' },
305
+ merge_candidate: { what: '다른 하네스와 자주 함께 쓰여요.', next: '/tink:weave로 겹침을 확인하고 합칠지 검토하세요.', command: 'tink:weave', expect: '실행하면: 겹침 근거를 살펴보고 합칠지 따로 둘지 제안해 줘요. 승인 전에는 아무것도 바뀌지 않아요.' },
306
+ frog_candidate: { what: '반복 문제와 높은 비용이 기록됐어요 — 정리 후보예요.', next: '/tink:frog로 보관/삭제 검토를 승인 절차와 함께 진행하세요.', command: 'tink:frog', expect: '실행하면: 정리 후보와 근거를 정리해 보여줘요. 보관·삭제는 명시적으로 승인해야만 실제로 진행돼요.' }
307
307
  },
308
308
  recPlain: {
309
309
  keep: '잘 쓰이고 있어요 — 그대로 유지하면 됩니다.',
@@ -874,10 +874,6 @@ function renderGraphCanvas(summary, copy) {
874
874
  <button class="active" type="button" data-mode="full" aria-pressed="true">${escapeHtml(copy.full)}</button>
875
875
  <button type="button" data-mode="core" aria-pressed="false">${escapeHtml(copy.core)}</button>
876
876
  </div>
877
- <div class="map-controls" aria-label="drag mode">
878
- <button class="active" type="button" data-drag="pan" aria-pressed="true">${escapeHtml(copy.dragPan || 'Move')}</button>
879
- <button type="button" data-drag="rotate" aria-pressed="false">${escapeHtml(copy.dragRotate || 'Rotate')}</button>
880
- </div>
881
877
  <div class="map-controls" aria-label="zoom">
882
878
  <button type="button" data-zoom="in" aria-label="${escapeAttr(copy.zoomIn || 'Zoom in')}" title="${escapeAttr(copy.zoomIn || 'Zoom in')}">+</button>
883
879
  <button type="button" data-zoom="out" aria-label="${escapeAttr(copy.zoomOut || 'Zoom out')}" title="${escapeAttr(copy.zoomOut || 'Zoom out')}">−</button>
@@ -1069,24 +1065,7 @@ function renderImportantHarnesses(harnesses, copy) {
1069
1065
  }
1070
1066
 
1071
1067
  function renderSelectedPanel(harnesses, copy) {
1072
- const first = harnesses.find((item) => item.signals?.uses > 0) || harnesses[0];
1073
- if (!first) {
1074
- return `<section class="insight-card selected" id="selected-panel"><p class="eyebrow">${escapeHtml(copy.selected)}</p><h2>${escapeHtml(copy.noHarnessSelected)}</h2><p>${escapeHtml(copy.clickNode)}</p></section>`;
1075
- }
1076
- const plain = (copy.recPlain && copy.recPlain[first.recommendation]) || normalizeReason(first.reason, copy) || copy.clickNode;
1077
- const stateText = (copy.statePlain && copy.statePlain[first.lifecycle_state]) || renderCopyValue(first.lifecycle_state, copy);
1078
- return `
1079
- <section class="insight-card selected" id="selected-panel">
1080
- <p class="eyebrow">${escapeHtml(copy.selected)}</p>
1081
- <h2>${escapeHtml(first.id)}</h2>
1082
- <p>${escapeHtml(plain)}</p>
1083
- <div class="stat-chips">
1084
- <span class="stat-chip">${escapeHtml(copy.uses)} ${escapeHtml(first.signals?.uses || 0)}${escapeHtml(copy.timesSuffix || '')}</span>
1085
- <span class="stat-chip">${escapeHtml(copy.score)} ${escapeHtml(first.candidate_score?.total || 0)}</span>
1086
- <span class="stat-chip">${escapeHtml(stateText)}</span>
1087
- </div>
1088
- </section>
1089
- `;
1068
+ return `<section class="insight-card selected" id="selected-panel"><p class="eyebrow">${escapeHtml(copy.selected)}</p><h2>${escapeHtml(copy.noHarnessSelected)}</h2><p>${escapeHtml(copy.clickNode)}</p></section>`;
1090
1069
  }
1091
1070
 
1092
1071
  function renderHarness(item, copy) {
@@ -1579,14 +1558,17 @@ function renderScript(harnesses, copy) {
1579
1558
  actionPanel.innerHTML = head + '<p class="action-body">' + esc(copy.actionDefault || '') + '</p>';
1580
1559
  return;
1581
1560
  }
1582
- const command = action.command ? (harnessId ? action.command + ' ' + harnessId : action.command) : '';
1561
+ const suffix = harnessId ? ' ' + harnessId : '';
1562
+ const cmdRow = (label, value) =>
1563
+ '<div class="cmd-row"><span class="cmd-label">' + esc(label) + '</span><code>' + esc(value) + '</code>' +
1564
+ '<button class="copy-btn" type="button" data-copy-cmd="' + esc(value) + '">' + esc(copy.copyCmd || 'Copy') + '</button></div>';
1583
1565
  actionPanel.innerHTML = head +
1584
1566
  (harnessId ? '<p class="action-target">' + esc(copy.actionTarget || 'Target') + ': <strong>' + esc(harnessId) + '</strong></p>' : '') +
1585
1567
  '<p class="action-body">' + esc(action.what || '') + '</p>' +
1586
1568
  '<p class="action-next">' + esc(action.next || '') + '</p>' +
1587
- (command
1588
- ? '<div class="cmd-row"><code>' + esc(command) + '</code>' +
1589
- '<button class="copy-btn" type="button" data-copy-cmd="' + esc(command) + '">' + esc(copy.copyCmd || 'Copy') + '</button></div>' +
1569
+ (action.command
1570
+ ? cmdRow('Claude Code', '/' + action.command + suffix) +
1571
+ cmdRow('Codex', '$' + action.command + suffix) +
1590
1572
  (action.expect ? '<p class="action-expect">' + esc(action.expect) + '</p>' : '') +
1591
1573
  '<p class="action-caveat">' + esc(copy.actionCaveat || '') + '</p>'
1592
1574
  : '');
@@ -1629,10 +1611,6 @@ function renderScript(harnesses, copy) {
1629
1611
  item.setAttribute('aria-pressed', 'false');
1630
1612
  });
1631
1613
  if (alreadyActive) {
1632
- cards.forEach((card) => card.classList.remove('is-filtered-out'));
1633
- window.__tinkGraphState.filter = null;
1634
- if (window.__tinkGraph) window.__tinkGraph.setFilter(null);
1635
- setStatus(copy.showingAll);
1636
1614
  setFilterStatus(copy.showingAll);
1637
1615
  updateActionPanel(null, null);
1638
1616
  updateGroupDetail(null);
@@ -1640,11 +1618,7 @@ function renderScript(harnesses, copy) {
1640
1618
  }
1641
1619
  button.classList.add('active-filter');
1642
1620
  button.setAttribute('aria-pressed', 'true');
1643
- cards.forEach((card) => card.classList.toggle('is-filtered-out', card.dataset.recommendation !== value));
1644
- window.__tinkGraphState.filter = value;
1645
- if (window.__tinkGraph) window.__tinkGraph.setFilter(value);
1646
1621
  const label = recLabelByFilter[value] || value;
1647
- setStatus(copy.filteredTo + ': ' + label);
1648
1622
  setFilterStatus(copy.filteredTo + ': ' + label);
1649
1623
  updateActionPanel(value, null);
1650
1624
  updateGroupDetail(value);
@@ -1656,24 +1630,8 @@ function renderScript(harnesses, copy) {
1656
1630
  else window.__tinkGraph.zoom(button.dataset.zoom === 'in' ? 0.78 : 1.28);
1657
1631
  });
1658
1632
  });
1659
- document.querySelectorAll('[data-drag]').forEach((button) => {
1660
- button.addEventListener('click', () => {
1661
- const mode = button.dataset.drag;
1662
- document.querySelectorAll('[data-drag]').forEach((item) => {
1663
- const active = item.dataset.drag === mode;
1664
- item.classList.toggle('active', active);
1665
- item.setAttribute('aria-pressed', active ? 'true' : 'false');
1666
- });
1667
- window.__tinkGraphState.dragMode = mode;
1668
- if (window.__tinkGraph) window.__tinkGraph.setDragMode(mode);
1669
- updateGraphHint();
1670
- });
1671
- });
1672
1633
  const graphHint = document.getElementById('graph-hint');
1673
- function updateGraphHint() {
1674
- if (graphHint) graphHint.textContent = window.__tinkGraphState.dragMode === 'rotate' ? (copy.hintRotate || '') : (copy.hintPan || '');
1675
- }
1676
- updateGraphHint();
1634
+ if (graphHint) graphHint.textContent = copy.hintPan || '';
1677
1635
  const rail = document.querySelector('.right-rail');
1678
1636
  const railResizer = document.getElementById('rail-resizer');
1679
1637
  if (rail && railResizer) {
@@ -1812,19 +1770,15 @@ function renderGraph3DModule(copy) {
1812
1770
  controls.zoomSpeed = 1.1;
1813
1771
  controls.screenSpacePanning = true;
1814
1772
  controls.panSpeed = 1.1;
1815
- function applyDragMode(mode) {
1816
- const rotate = mode === 'rotate';
1817
- controls.mouseButtons = {
1818
- LEFT: rotate ? THREE.MOUSE.ROTATE : THREE.MOUSE.PAN,
1819
- MIDDLE: THREE.MOUSE.DOLLY,
1820
- RIGHT: rotate ? THREE.MOUSE.PAN : THREE.MOUSE.ROTATE
1821
- };
1822
- controls.touches = {
1823
- ONE: rotate ? THREE.TOUCH.ROTATE : THREE.TOUCH.PAN,
1824
- TWO: THREE.TOUCH.DOLLY_ROTATE
1825
- };
1826
- }
1827
- applyDragMode((window.__tinkGraphState && window.__tinkGraphState.dragMode) || 'pan');
1773
+ controls.mouseButtons = {
1774
+ LEFT: THREE.MOUSE.PAN,
1775
+ MIDDLE: THREE.MOUSE.DOLLY,
1776
+ RIGHT: THREE.MOUSE.ROTATE
1777
+ };
1778
+ controls.touches = {
1779
+ ONE: THREE.TOUCH.PAN,
1780
+ TWO: THREE.TOUCH.DOLLY_ROTATE
1781
+ };
1828
1782
  controls.autoRotate = false;
1829
1783
  controls.minDistance = 8;
1830
1784
  controls.maxDistance = 220;
@@ -2184,9 +2138,6 @@ function renderGraph3DModule(copy) {
2184
2138
  camera.position.copy(INITIAL_CAM);
2185
2139
  controls.target.copy(INITIAL_TARGET);
2186
2140
  controls.update();
2187
- },
2188
- setDragMode(mode) {
2189
- applyDragMode(mode);
2190
2141
  }
2191
2142
  };
2192
2143
  applyState();
@@ -2332,30 +2283,30 @@ function renderStyles() {
2332
2283
  return `<style>
2333
2284
  :root {
2334
2285
  color-scheme: dark;
2335
- --bg-page: #0A0A0A;
2336
- --bg-card: #111111;
2337
- --bg-hover: #161616;
2338
- --bg-selected: #1C1C1C;
2339
- --border-default: #1F1F1F;
2340
- --border-hover: #2A2A2A;
2341
- --border-strong: #383838;
2342
- --text-primary: #E8E8E8;
2343
- --text-secondary: #666666;
2344
- --text-muted: #3D3D3D;
2345
- --accent: #2563EB;
2346
- --accent-dim: #1E2D4A;
2347
- --accent-text: #93C5FD;
2348
- --success: #22C55E;
2349
- --success-dim: #14291E;
2350
- --warning: #F59E0B;
2351
- --warning-dim: #2A1F0A;
2352
- --danger: #EF4444;
2353
- --danger-dim: #2A0F0F;
2354
- --font-ui: 'IBM Plex Sans', -apple-system, sans-serif;
2286
+ --bg-page: #171511;
2287
+ --bg-card: #1E1B16;
2288
+ --bg-hover: #25211A;
2289
+ --bg-selected: #2C2720;
2290
+ --border-default: #2D2820;
2291
+ --border-hover: #3B342A;
2292
+ --border-strong: #4C4336;
2293
+ --text-primary: #EBE4D6;
2294
+ --text-secondary: #A2988A;
2295
+ --text-muted: #645B4E;
2296
+ --accent: #C9913F;
2297
+ --accent-dim: #3A2F1B;
2298
+ --accent-text: #E2B873;
2299
+ --success: #8FAE76;
2300
+ --success-dim: #28301F;
2301
+ --warning: #D2A24A;
2302
+ --warning-dim: #342917;
2303
+ --danger: #C97862;
2304
+ --danger-dim: #34221C;
2305
+ --font-ui: 'Avenir Next', 'Pretendard', 'IBM Plex Sans', -apple-system, 'Segoe UI', sans-serif;
2355
2306
  --font-mono: 'IBM Plex Mono', 'Fira Code', monospace;
2356
- --radius-sm: 4px;
2357
- --radius-md: 6px;
2358
- --radius-lg: 8px;
2307
+ --radius-sm: 5px;
2308
+ --radius-md: 8px;
2309
+ --radius-lg: 12px;
2359
2310
  --border-width: 1px;
2360
2311
  --space-1: 4px;
2361
2312
  --space-2: 8px;
@@ -2363,12 +2314,12 @@ function renderStyles() {
2363
2314
  --space-4: 16px;
2364
2315
  --space-6: 24px;
2365
2316
  --space-8: 32px;
2366
- --chart-line: #2563EB;
2317
+ --chart-line: #C9913F;
2367
2318
  --chart-line-w: 1.5px;
2368
- --chart-area: rgba(37, 99, 235, 0.06);
2369
- --chart-grid: #1F1F1F;
2370
- --chart-dot-fill: #111111;
2371
- --chart-dot-stroke: #2563EB;
2319
+ --chart-area: rgba(201, 145, 63, 0.07);
2320
+ --chart-grid: #2D2820;
2321
+ --chart-dot-fill: #1E1B16;
2322
+ --chart-dot-stroke: #C9913F;
2372
2323
  }
2373
2324
 
2374
2325
  * { box-sizing: border-box; }
@@ -3099,7 +3050,7 @@ function renderStyles() {
3099
3050
  padding: 6px 12px;
3100
3051
  border-radius: var(--radius-md);
3101
3052
  border: 1px solid var(--border-default);
3102
- background: rgba(10, 10, 14, 0.78);
3053
+ background: rgba(23, 21, 17, 0.82);
3103
3054
  color: var(--text-secondary);
3104
3055
  font-size: 12px;
3105
3056
  pointer-events: none;
@@ -3240,6 +3191,13 @@ function renderStyles() {
3240
3191
  flex-wrap: wrap;
3241
3192
  }
3242
3193
 
3194
+ .cmd-label {
3195
+ min-width: 78px;
3196
+ color: var(--text-muted);
3197
+ font-size: 11.5px;
3198
+ white-space: nowrap;
3199
+ }
3200
+
3243
3201
  .cmd-row code {
3244
3202
  background: var(--bg-hover);
3245
3203
  border: 1px solid var(--border-default);
@@ -3537,13 +3495,14 @@ function renderStyles() {
3537
3495
 
3538
3496
  .harness-summary:hover { background: var(--bg-hover); }
3539
3497
 
3540
- .harness-summary .eyebrow { margin: 0 0 2px; font-size: 10px; }
3498
+ .harness-summary .eyebrow { margin: 0 0 3px; font-size: 11.5px; letter-spacing: 0.03em; }
3541
3499
 
3542
3500
  .harness-summary h3 {
3543
3501
  margin: 0;
3544
- font-size: 14px;
3545
- line-height: 1.25;
3502
+ font-size: 15.5px;
3503
+ line-height: 1.3;
3546
3504
  font-weight: 600;
3505
+ letter-spacing: -0.01em;
3547
3506
  overflow-wrap: anywhere;
3548
3507
  }
3549
3508
 
@@ -3555,12 +3514,12 @@ function renderStyles() {
3555
3514
 
3556
3515
  .harness-mini span {
3557
3516
  color: var(--text-secondary);
3558
- font-size: 11px;
3517
+ font-size: 12.5px;
3559
3518
  white-space: nowrap;
3560
3519
  }
3561
3520
 
3562
3521
  .harness-mini strong {
3563
- font-size: 18px;
3522
+ font-size: 20px;
3564
3523
  line-height: 1;
3565
3524
  font-family: var(--font-mono);
3566
3525
  font-weight: 600;
@@ -3604,16 +3563,15 @@ function renderStyles() {
3604
3563
  .harness-reason {
3605
3564
  margin: 0;
3606
3565
  color: var(--text-secondary);
3607
- font-size: 12px;
3608
- line-height: 1.5;
3566
+ font-size: 13.5px;
3567
+ line-height: 1.6;
3609
3568
  }
3610
3569
 
3611
3570
  .detail-label {
3612
- margin: var(--space-1) 0 0;
3571
+ margin: var(--space-2) 0 0;
3613
3572
  color: var(--text-secondary);
3614
- font-size: 10px;
3615
- text-transform: uppercase;
3616
- letter-spacing: 0.06em;
3573
+ font-size: 11.5px;
3574
+ letter-spacing: 0.04em;
3617
3575
  }
3618
3576
 
3619
3577
  .co-used-chips { display: flex; flex-wrap: wrap; gap: 6px; }
@@ -3640,7 +3598,7 @@ function renderStyles() {
3640
3598
  display: flex;
3641
3599
  justify-content: space-between;
3642
3600
  gap: var(--space-2);
3643
- font-size: 12px;
3601
+ font-size: 13px;
3644
3602
  color: var(--text-secondary);
3645
3603
  }
3646
3604
 
@@ -3649,17 +3607,17 @@ function renderStyles() {
3649
3607
  .harness-next {
3650
3608
  margin: 0;
3651
3609
  color: var(--text-primary);
3652
- font-size: 12px;
3653
- line-height: 1.5;
3610
+ font-size: 13.5px;
3611
+ line-height: 1.6;
3654
3612
  }
3655
3613
 
3656
3614
  .evidence-list {
3657
3615
  margin: 0;
3658
3616
  padding-left: 16px;
3659
3617
  color: var(--text-secondary);
3660
- font-size: 11px;
3618
+ font-size: 12.5px;
3661
3619
  display: grid;
3662
- gap: 3px;
3620
+ gap: 4px;
3663
3621
  }
3664
3622
 
3665
3623
  .harness-card .link-button { margin-top: var(--space-1); justify-self: start; }
@@ -3680,19 +3638,19 @@ function renderStyles() {
3680
3638
 
3681
3639
  .history-feed li {
3682
3640
  display: grid;
3683
- grid-template-columns: 110px 1fr;
3641
+ grid-template-columns: 120px 1fr;
3684
3642
  gap: var(--space-3);
3685
3643
  align-items: start;
3686
3644
  border: 1px solid var(--border-default);
3687
3645
  border-radius: var(--radius-md);
3688
3646
  background: var(--bg-card);
3689
- padding: var(--space-2) var(--space-3);
3647
+ padding: var(--space-3) var(--space-4);
3690
3648
  }
3691
3649
 
3692
3650
  .history-type {
3693
3651
  display: inline-block;
3694
3652
  font-family: var(--font-mono);
3695
- font-size: 10px;
3653
+ font-size: 11px;
3696
3654
  text-transform: uppercase;
3697
3655
  letter-spacing: 0.05em;
3698
3656
  color: var(--text-secondary);
@@ -3709,12 +3667,12 @@ function renderStyles() {
3709
3667
  .history-type.harness-create,
3710
3668
  .history-type.harness-edit { color: var(--success); border-color: var(--success-dim); }
3711
3669
 
3712
- .history-feed strong { font-size: 12px; font-weight: 500; }
3670
+ .history-feed strong { font-size: 13.5px; font-weight: 500; }
3713
3671
 
3714
3672
  .history-harnesses {
3715
- margin: 2px 0 0;
3673
+ margin: 3px 0 0;
3716
3674
  color: var(--text-secondary);
3717
- font-size: 11px;
3675
+ font-size: 12.5px;
3718
3676
  font-family: var(--font-mono);
3719
3677
  }
3720
3678