@yemi33/minions 0.1.2423 → 0.1.2425

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.
Files changed (184) hide show
  1. package/bin/install-internal-minions.js +1209 -0
  2. package/bin/minions.js +75 -9
  3. package/dashboard/docs/typography.md +27 -12
  4. package/dashboard/js/command-center.js +13 -11
  5. package/dashboard/js/live-stream.js +1 -1
  6. package/dashboard/js/memory-search.js +388 -43
  7. package/dashboard/js/modal-qa.js +5 -5
  8. package/dashboard/js/qa.js +39 -26
  9. package/dashboard/js/refresh.js +37 -18
  10. package/dashboard/js/render-agents.js +14 -18
  11. package/dashboard/js/render-dispatch.js +14 -74
  12. package/dashboard/js/render-inbox.js +3 -3
  13. package/dashboard/js/render-meetings.js +8 -8
  14. package/dashboard/js/render-other.js +45 -20
  15. package/dashboard/js/render-pipelines.js +6 -6
  16. package/dashboard/js/render-plans.js +14 -14
  17. package/dashboard/js/render-prd.js +46 -46
  18. package/dashboard/js/render-prs.js +9 -65
  19. package/dashboard/js/render-schedules.js +5 -5
  20. package/dashboard/js/render-skills.js +6 -6
  21. package/dashboard/js/render-utils.js +4 -4
  22. package/dashboard/js/render-watches.js +4 -16
  23. package/dashboard/js/render-work-items.js +95 -107
  24. package/dashboard/js/settings.js +175 -74
  25. package/dashboard/layout.html +3 -2
  26. package/dashboard/pages/inbox.html +1 -1
  27. package/dashboard/pages/work.html +1 -1
  28. package/dashboard/shared/model-display.js +9 -0
  29. package/dashboard/shared/watches-source.js +43 -0
  30. package/dashboard/shared/welcome-popup.js +259 -0
  31. package/dashboard/slim/body.html +6 -6
  32. package/dashboard/slim/js/chat.js +4 -2
  33. package/dashboard/slim/js/history.js +10 -3
  34. package/dashboard/slim/js/modals-tiles.js +85 -16
  35. package/dashboard/slim/js/status.js +12 -16
  36. package/dashboard/slim/styles.css +24 -4
  37. package/dashboard/styles.css +62 -27
  38. package/dashboard-build.js +8 -2
  39. package/dashboard.js +3854 -2514
  40. package/docs/README.md +6 -4
  41. package/docs/api-errors.md +144 -0
  42. package/docs/auto-discovery.md +83 -61
  43. package/docs/capture-demos.js +173 -30
  44. package/docs/command-center.md +5 -1
  45. package/docs/completion-reports.md +127 -11
  46. package/docs/constants.md +15 -3
  47. package/docs/constellation-style-telemetry.md +2 -2
  48. package/docs/copilot-cli-schema.md +32 -22
  49. package/docs/cross-repo-plans.md +30 -30
  50. package/docs/demo/memory-system.html +1 -1
  51. package/docs/deprecated.json +132 -18
  52. package/docs/diagnostics-memory.md +8 -4
  53. package/docs/human-vs-automated.md +2 -2
  54. package/docs/internal-install.md +190 -0
  55. package/docs/kb-pr3223-cascade-archiving.md +16 -0
  56. package/docs/kb-pr696-merge-conflict-docs.md +23 -0
  57. package/docs/kb-sweep.md +2 -2
  58. package/docs/keep-processes.md +8 -1
  59. package/docs/live-checkout-mode.md +12 -7
  60. package/docs/managed-spawn.md +21 -3
  61. package/docs/named-agents.md +3 -2
  62. package/docs/plan-lifecycle.md +32 -32
  63. package/docs/pr-auto-fix-dispatch.md +3 -3
  64. package/docs/pr-review-fix-loop.md +6 -6
  65. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
  66. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
  67. package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
  68. package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
  69. package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
  70. package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
  71. package/docs/qa-runbook-lifecycle.md +67 -51
  72. package/docs/qa-runbooks.md +2 -2
  73. package/docs/runtime-adapters.md +101 -50
  74. package/docs/security.md +28 -0
  75. package/docs/self-improvement.md +47 -13
  76. package/docs/skills.md +29 -0
  77. package/docs/slim-ux/concepts.md +2 -2
  78. package/docs/specs/agent-configurability.md +43 -41
  79. package/docs/specs/agent-rename.md +13 -13
  80. package/docs/team-memory.md +283 -14
  81. package/docs/tutorials/03-plan-driven-feature.md +3 -3
  82. package/docs/tutorials/04-runtimes-and-harness.md +18 -21
  83. package/docs/tutorials/08-operations-and-recovery.md +3 -2
  84. package/docs/tutorials/README.md +4 -3
  85. package/docs/visual-evidence-ci.md +103 -0
  86. package/docs/watches.md +35 -30
  87. package/docs/worktree-lifecycle.md +151 -13
  88. package/engine/acp-transport.js +1 -0
  89. package/engine/ado.js +80 -33
  90. package/engine/agent-api-validation.js +571 -0
  91. package/engine/agent-worker-pool.js +7 -6
  92. package/engine/api-contracts/agent-content.js +761 -0
  93. package/engine/api-contracts/cc-ops.js +219 -0
  94. package/engine/api-contracts/config-runtime.js +419 -0
  95. package/engine/api-contracts/core.js +138 -0
  96. package/engine/api-contracts/index.js +506 -0
  97. package/engine/api-contracts/orchestration.js +602 -0
  98. package/engine/api-contracts/pull-requests.js +323 -0
  99. package/engine/api-contracts/qa-process.js +764 -0
  100. package/engine/api-contracts/work-plan-prd.js +494 -0
  101. package/engine/api-validation.js +543 -0
  102. package/engine/cc-api-validation.js +586 -0
  103. package/engine/cc-worker-pool.js +9 -1
  104. package/engine/cleanup.js +393 -271
  105. package/engine/cli.js +65 -7
  106. package/engine/comment-classifier.js +70 -5
  107. package/engine/consolidation.js +123 -5
  108. package/engine/content-api-validation.js +606 -0
  109. package/engine/create-pr-worktree.js +22 -1
  110. package/engine/db/migrations/027-review-learning-lifecycle.js +162 -0
  111. package/engine/dispatch.js +33 -0
  112. package/engine/distribution.js +189 -0
  113. package/engine/execution-model.js +59 -0
  114. package/engine/features.js +12 -0
  115. package/engine/github.js +67 -25
  116. package/engine/harness-context.js +313 -0
  117. package/engine/inbox-store.js +1 -1
  118. package/engine/kb-sweep.js +161 -38
  119. package/engine/keep-process-sweep.js +43 -3
  120. package/engine/lifecycle.js +1066 -165
  121. package/engine/llm.js +4 -0
  122. package/engine/managed-spawn.js +463 -48
  123. package/engine/meeting.js +197 -34
  124. package/engine/memory-retrieval.js +280 -0
  125. package/engine/memory-store.js +821 -38
  126. package/engine/model-discovery.js +17 -3
  127. package/engine/pipeline.js +802 -14
  128. package/engine/plan-prd-validation.js +745 -0
  129. package/engine/playbook.js +206 -17
  130. package/engine/pooled-agent-process.js +6 -1
  131. package/engine/pr-action.js +1 -1
  132. package/engine/pr-issue-validation.js +653 -0
  133. package/engine/pr-resolve.js +75 -13
  134. package/engine/prd-store.js +39 -6
  135. package/engine/preflight.js +7 -2
  136. package/engine/process-utils.js +79 -31
  137. package/engine/projects.js +198 -12
  138. package/engine/promotion.js +371 -0
  139. package/engine/qa-from-prd.js +3 -0
  140. package/engine/qa-process-validation.js +572 -0
  141. package/engine/qa-runbooks.js +41 -37
  142. package/engine/qa-runners/maestro.js +11 -7
  143. package/engine/qa-runners/playwright.js +37 -14
  144. package/engine/qa-runners.js +9 -10
  145. package/engine/qa-runs.js +124 -11
  146. package/engine/qa-sessions.js +139 -40
  147. package/engine/quarantine-refs.js +81 -11
  148. package/engine/queries.js +163 -9
  149. package/engine/review-learning-backfill.js +482 -0
  150. package/engine/review-learning.js +1236 -0
  151. package/engine/runtimes/claude.js +60 -2
  152. package/engine/runtimes/codex.js +57 -16
  153. package/engine/runtimes/contract.js +21 -0
  154. package/engine/runtimes/copilot.js +51 -3
  155. package/engine/runtimes/index.js +1 -0
  156. package/engine/scheduler.js +201 -32
  157. package/engine/settings-validation.js +909 -0
  158. package/engine/shared.js +705 -208
  159. package/engine/spawn-agent.js +48 -21
  160. package/engine/steering-constraints.js +31 -0
  161. package/engine/steering-store.js +10 -2
  162. package/engine/steering.js +14 -2
  163. package/engine/timeout.js +404 -111
  164. package/engine/untrusted-fence.js +24 -10
  165. package/engine/watch-actions.js +7 -2
  166. package/engine/watches.js +353 -51
  167. package/engine/work-item-validation.js +561 -0
  168. package/engine/work-items-store.js +61 -1
  169. package/engine/worktree-gc.js +656 -37
  170. package/engine/worktree-preflight.js +154 -0
  171. package/engine.js +2068 -342
  172. package/package.json +1 -1
  173. package/playbooks/_pr-description-audit.md +88 -41
  174. package/playbooks/build-fix-complex.md +3 -3
  175. package/playbooks/docs.md +2 -2
  176. package/playbooks/fix.md +18 -18
  177. package/playbooks/implement.md +18 -18
  178. package/playbooks/qa-session-draft.md +10 -12
  179. package/playbooks/qa-session-execute.md +16 -12
  180. package/playbooks/qa-validate.md +8 -7
  181. package/playbooks/shared-rules.md +45 -0
  182. package/playbooks/test.md +4 -0
  183. package/prompts/cc-system.md +1 -1
  184. package/skills/check-self-authored-review-comment/SKILL.md +2 -2
package/bin/minions.js CHANGED
@@ -11,6 +11,7 @@
11
11
  * minions update [--no-wait] Update to latest version (--no-wait backgrounds the post-update restart)
12
12
  * minions version Show installed and package versions
13
13
  * minions doctor [--harness] Check prerequisites and runtime health (--harness: print runtime harness inventory)
14
+ * minions state <subcmd> Check, backup, or export SQLite state
14
15
  * minions restart [--open] Stop + start engine + dashboard (--open forces a new browser tab)
15
16
  * minions start [--open] Start engine + dashboard if not already running (no-op when up)
16
17
  * minions stop Stop the engine
@@ -856,6 +857,15 @@ function getPkgVersionFresh() {
856
857
  try { return JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8')).version; } catch { return null; }
857
858
  }
858
859
 
860
+ // Which npm distribution is this CLI? Derived from the installed package
861
+ // manifest (public `@yemi33/minions` vs an internal rename produced by
862
+ // tools/prepare-package.js) so `minions version` compares against the right
863
+ // registry channel and `minions update` reinstalls ITSELF instead of pulling
864
+ // in a second package that fights over the shared `minions` bin (npm EEXIST).
865
+ function getPkgName() {
866
+ return shared.resolvePackageName(PKG_ROOT);
867
+ }
868
+
859
869
  function getInstalledVersion() {
860
870
  try {
861
871
  const vFile = path.join(MINIONS_HOME, '.minions-version');
@@ -1201,7 +1211,8 @@ function runPostUpdateRestartDetached() {
1201
1211
  function showVersion() {
1202
1212
  const pkg = getPkgVersion();
1203
1213
  const installed = getInstalledVersion();
1204
- console.log(`\n Package version: ${pkg}`);
1214
+ console.log(`\n Package: ${getPkgName()}`);
1215
+ console.log(` Package version: ${pkg}`);
1205
1216
  console.log(` Runtime root: ${MINIONS_HOME}`);
1206
1217
  if (installed) {
1207
1218
  console.log(` Installed version: ${installed}`);
@@ -1216,7 +1227,7 @@ function showVersion() {
1216
1227
 
1217
1228
  // Check npm registry for latest version (best-effort, non-blocking)
1218
1229
  try {
1219
- const latest = execSync('npm view @yemi33/minions version', { encoding: 'utf8', timeout: 5000, windowsHide: true }).trim();
1230
+ const latest = execSync(shared.buildNpmViewVersionCommand(getPkgName()), { encoding: 'utf8', timeout: 5000, windowsHide: true }).trim();
1220
1231
  if (latest && latest !== pkg) {
1221
1232
  console.log(`\n Latest on npm: ${latest}`);
1222
1233
  console.log(' To update: minions update');
@@ -1269,6 +1280,14 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
1269
1280
  minions update [--no-wait] Update to latest version (--no-wait backgrounds the restart)
1270
1281
  minions version Show installed vs package version
1271
1282
  minions doctor [--harness] Check prerequisites and runtime health (--harness: print runtime harness inventory)
1283
+ minions state check Run SQLite integrity and invariant checks
1284
+ minions state backup <file> Checkpoint and create a consistent SQLite backup
1285
+ minions state export <file> Write a human-readable diagnostic JSON export
1286
+ minions state import-legacy-json <dir> --confirm
1287
+ Offline recovery import from a legacy home
1288
+ minions memory backfill-review-learnings [--dry-run] [--limit <N>] [--project <p>]
1289
+ Strict offline backfill of review-learning
1290
+ lessons from legacy artifacts (no LLM inference)
1272
1291
  minions add <project-dir> Link a project (interactive)
1273
1292
  minions remove <project-dir> Unlink a project
1274
1293
  minions list List linked projects
@@ -1342,6 +1361,13 @@ ${fs.existsSync(path.join(PKG_ROOT, '.git')) ? `
1342
1361
  console.log(` Version synced to ${getPkgVersion()} (dev/symlink install — pull from git to update code)`);
1343
1362
  } else {
1344
1363
  const preVersion = getPkgVersionFresh();
1364
+ // Resolve the ACTIVE distribution first: an internal build must update
1365
+ // itself, not the public package. Installing `@yemi33/minions` on top of an
1366
+ // `@opg-microsoft/minions` install adds a second package claiming the same
1367
+ // `minions` bin, which npm rejects with EEXIST (W-ms1j8m54000g4a4a).
1368
+ // Reinstalling the same package name is an in-place upgrade, so no bin
1369
+ // collision is possible.
1370
+ const pkgName = getPkgName();
1345
1371
  // Resolve the target version explicitly BEFORE installing, and pin the
1346
1372
  // install to that exact version instead of a floating `@latest` tag. This
1347
1373
  // avoids a race where `npm view` (used here and by `minions version`) and
@@ -1353,18 +1379,18 @@ ${fs.existsSync(path.join(PKG_ROOT, '.git')) ? `
1353
1379
  // though `npm view pkg version` reports a newer one (W-mr9k4a5x00090184).
1354
1380
  let targetVersion;
1355
1381
  try {
1356
- targetVersion = execSync('npm view @yemi33/minions version', { encoding: 'utf8', timeout: 15000, windowsHide: true }).trim();
1382
+ targetVersion = execSync(shared.buildNpmViewVersionCommand(pkgName), { encoding: 'utf8', timeout: 15000, windowsHide: true }).trim();
1357
1383
  } catch (e) {
1358
- console.error(' Failed to resolve the latest version from the npm registry:', e.message);
1384
+ console.error(` Failed to resolve the latest version of ${pkgName} from the npm registry:`, e.message);
1359
1385
  process.exit(1);
1360
1386
  }
1361
1387
  if (!targetVersion) {
1362
- console.error(' Failed to resolve the latest version from the npm registry (empty response).');
1388
+ console.error(` Failed to resolve the latest version of ${pkgName} from the npm registry (empty response).`);
1363
1389
  process.exit(1);
1364
1390
  }
1365
1391
 
1366
1392
  try {
1367
- execSync(`npm install -g @yemi33/minions@${targetVersion}`, { stdio: 'inherit', timeout: 120000 });
1393
+ execSync(shared.buildNpmGlobalInstallCommand(pkgName, targetVersion), { stdio: 'inherit', timeout: 120000 });
1368
1394
  } catch (e) {
1369
1395
  console.error(' npm update failed:', e.message);
1370
1396
  process.exit(1);
@@ -1615,6 +1641,7 @@ ${fs.existsSync(path.join(PKG_ROOT, '.git')) ? `
1615
1641
 
1616
1642
  console.log('\n Factory reset complete. Run "minions init" to link projects and start fresh.\n');
1617
1643
  } else if (cmd === 'uninstall') {
1644
+ const uninstallPkgName = getPkgName();
1618
1645
  if (!rest.includes('--confirm')) {
1619
1646
  console.log(`
1620
1647
  Uninstall Minions — removes EVERYTHING.
@@ -1623,7 +1650,7 @@ ${fs.existsSync(path.join(PKG_ROOT, '.git')) ? `
1623
1650
  1. Kill all running engine, dashboard, and agent processes
1624
1651
  2. Delete the entire ${MINIONS_HOME} directory (all state, config, agents, knowledge)
1625
1652
  3. Remove extracted skills from ~/.claude/skills/ (minions-authored only)
1626
- 4. Uninstall the npm package (@yemi33/minions)
1653
+ 4. Uninstall the npm package (${uninstallPkgName})
1627
1654
 
1628
1655
  This is irreversible. Your project repos are NOT affected.
1629
1656
 
@@ -1670,11 +1697,11 @@ ${fs.existsSync(path.join(PKG_ROOT, '.git')) ? `
1670
1697
 
1671
1698
  // 4. Uninstall npm package
1672
1699
  console.log(' Uninstalling npm package...');
1673
- try { execSync('npm uninstall -g @yemi33/minions', { stdio: 'inherit', timeout: 60000 }); } catch {}
1700
+ try { execSync(shared.buildNpmGlobalUninstallCommand(uninstallPkgName), { stdio: 'inherit', timeout: 60000 }); } catch {}
1674
1701
 
1675
1702
  console.log('\n Minions uninstalled. Your project repos were not touched.');
1676
1703
  console.log('\n To re-install later, run:');
1677
- console.log(' npm install -g @yemi33/minions && minions init\n');
1704
+ console.log(` npm install -g ${uninstallPkgName} && minions init\n`);
1678
1705
  } else if (cmd === 'state') {
1679
1706
  ensureInstalled();
1680
1707
  const operations = require(path.join(MINIONS_HOME, 'engine', 'state-operations'));
@@ -1707,6 +1734,45 @@ ${fs.existsSync(path.join(PKG_ROOT, '.git')) ? `
1707
1734
  console.error(`\n State ${sub || 'operation'} failed: ${e.message}\n`);
1708
1735
  process.exit(1);
1709
1736
  }
1737
+ } else if (cmd === 'memory') {
1738
+ ensureInstalled();
1739
+ const sub = rest[0];
1740
+ if (sub !== 'backfill-review-learnings') {
1741
+ console.error('\n Usage: minions memory backfill-review-learnings [--dry-run] [--limit <N>] [--project <name>]\n');
1742
+ process.exit(2);
1743
+ }
1744
+ const dryRun = rest.includes('--dry-run');
1745
+ const limitIdx = rest.indexOf('--limit');
1746
+ const limit = limitIdx >= 0 ? Number(rest[limitIdx + 1]) : 100;
1747
+ if (limitIdx >= 0 && (!Number.isFinite(limit) || limit <= 0)) {
1748
+ console.error('\n --limit requires a positive integer\n');
1749
+ process.exit(2);
1750
+ }
1751
+ const projIdx = rest.indexOf('--project');
1752
+ const project = projIdx >= 0 ? rest[projIdx + 1] : null;
1753
+ // A real run mutates memory_records; refuse while the engine is live so we do
1754
+ // not race live capture. Dry-run is read-only and always allowed.
1755
+ if (!dryRun) {
1756
+ const enginePid = readEnginePid(MINIONS_HOME);
1757
+ if (enginePid) {
1758
+ try {
1759
+ process.kill(enginePid, 0);
1760
+ console.error(`\n Engine PID ${enginePid} is running; stop Minions before a real backfill (or use --dry-run)\n`);
1761
+ process.exit(1);
1762
+ } catch (e) {
1763
+ if (!e.code) throw e;
1764
+ }
1765
+ }
1766
+ }
1767
+ try {
1768
+ const { backfillReviewLearnings } = require(path.join(MINIONS_HOME, 'engine', 'review-learning-backfill'));
1769
+ const result = backfillReviewLearnings({ dryRun, limit, project });
1770
+ console.log(JSON.stringify(result, null, 2));
1771
+ process.exit(0);
1772
+ } catch (e) {
1773
+ console.error(`\n memory backfill-review-learnings failed: ${e.message}\n`);
1774
+ process.exit(1);
1775
+ }
1710
1776
  } else if (cmd === 'doctor') {
1711
1777
  ensureInstalled();
1712
1778
  const { doctor, runHarnessDoctor } = require(path.join(MINIONS_HOME, 'engine', 'preflight'));
@@ -40,7 +40,7 @@ two stay in lockstep.
40
40
 
41
41
  | Token | Value | Common use |
42
42
  | ------------------ | ----- | --------------------------------------------------------- |
43
- | `--text-xs` | 10px | Smallest legitimate text; sidebar badges, count pills |
43
+ | `--text-xs` | 10px | Reserved chrome only — not for body/label/button text (see floor note) |
44
44
  | `--text-sm` | 12px | Status badges, chip / tag labels |
45
45
  | `--text-base` | 12px | Captions, secondary metadata, dense table cells |
46
46
  | `--text-md` | 13px | Default for slim chat / detail tables / button labels |
@@ -57,24 +57,39 @@ two stay in lockstep.
57
57
  > token that breaks the "all px" rule and is explicitly allowlisted in
58
58
  > the tripwire.
59
59
 
60
- ### 12px readability floor (W-mr3xgejt0008d3a5)
60
+ ### 12px readability floor (W-mr3xgejt0008d3a5, W-mryzdch9000f069c)
61
61
 
62
62
  `--text-sm` was raised from an 11px base to **12px** so it clears the 12px
63
63
  readability floor in both stylesheets: classic renders it at `12 × 1.05 =
64
64
  12.6px`, slim at `12 × 1 = 12px`. `--text-xs` (10px base) stays below the
65
- floor and is reserved for the very smallest chrome. To lift the
66
- highest-signal small-text callsites off `--text-xs`, these selectors were
67
- reassigned to `--text-sm`:
68
-
69
- - `.sidebar-count` primary nav count pill
70
- - `.dispatch-stuck` uppercase red "stuck" status badge
71
- - `.pl-node-meta` muted plan-node secondary text
72
- - `.error-details-btn` uppercase error-details button label
65
+ floor.
66
+
67
+ An initial pass (W-mr3xgejt0008d3a5) reassigned four high-signal callsites
68
+ (`.sidebar-count`, `.dispatch-stuck`, `.pl-node-meta`, `.error-details-btn`)
69
+ from `--text-xs` to `--text-sm`. **W-mryzdch9000f069c then bumped the
70
+ remaining ~190 `--text-xs` *usages* across `dashboard/**`** (CSS rules,
71
+ inline `style="font-size:var(--text-xs)"`, and JS template strings) to
72
+ `--text-sm`, so almost all dashboard text now renders at or above the floor.
73
+ The `--text-xs` token declaration itself is unchanged (still
74
+ `calc(10px * var(--minions-text-scale))` in both stylesheets) — this was a
75
+ usage migration, not a value change.
76
+
77
+ `--text-xs` is now retained at only these genuinely-dense chrome callsites,
78
+ where 12px would clip or misalign a fixed-size component:
79
+
80
+ - `.token-chart-labels span` — token-usage chart axis labels squeezed into
81
+ fixed `min-width:8px; max-width:24px` columns with `overflow:hidden`; 12px
82
+ would clip the labels.
83
+ - `.qa-phase-arrow` — the decorative `→` separator glyph between QA phase
84
+ chips (a micro-glyph, not readable text).
85
+
86
+ The `.text-xs` utility class (`.text-xs { font-size: var(--text-xs); }`)
87
+ stays declared as the token's escape hatch but currently has no callsites.
73
88
 
74
89
  The `--text-sm` selectors already on the token (`.table th`, `.pr-table th`,
75
90
  `.token-agent-table th`, `.status-badge`, `.sidebar-link`, slim
76
- `.tile-item-subtle`) now clear the floor automatically via the token bump —
77
- no per-callsite change needed.
91
+ `.tile-item-subtle`) clear the floor automatically via the token bump — no
92
+ per-callsite change needed.
78
93
 
79
94
  ## Role aliases (preferred for new code)
80
95
 
@@ -636,10 +636,12 @@ function _ccSetButtonProcessing(on) {
636
636
  }
637
637
 
638
638
  function _ccRenderSuggestedPrompts() {
639
- var el = document.getElementById('cc-messages');
639
+ var host = document.getElementById('cc-suggestions-host');
640
640
  var tab = _ccActiveTab();
641
- if (!el || !tab || tab.messages.length !== 0 || tab._sending || el.children.length !== 0) return;
642
- renderCcSuggestedPrompts(el, function(prompt) {
641
+ if (!host) return;
642
+ clearCcSuggestedPrompts(host);
643
+ if (!tab || tab.messages.length !== 0 || tab._sending) return;
644
+ renderCcSuggestedPrompts(host, function(prompt) {
643
645
  var input = document.getElementById('cc-input');
644
646
  if (!input) return;
645
647
  input.value = prompt;
@@ -738,7 +740,7 @@ function ccSwitchTab(id) {
738
740
  for (var pi = phases.length - 1; pi >= 0; pi--) { if (ms >= phases[pi][0]) { label = phases[pi][1]; break; } }
739
741
  var secs = Math.floor(ms / 1000);
740
742
  html += '<div style="margin-top:' + (text ? '6px' : '0') + ';display:flex;align-items:center;gap:6px"><span style="color:var(--muted);font-size:var(--text-base)">' + label + '</span>' + dotPulse + '<span style="margin-left:auto;font-size:var(--text-sm);color:var(--muted)">' + secs + 's</span>' +
741
- '<button onclick="ccAbort()" style="font-size:var(--text-xs);padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer;margin-left:4px">Stop</button></div>';
743
+ '<button onclick="ccAbort()" style="font-size:var(--text-sm);padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer;margin-left:4px">Stop</button></div>';
742
744
  return html;
743
745
  }
744
746
  ccAddMessage('assistant', _restoreStreamHtml(), true);
@@ -933,7 +935,7 @@ function ccRestoreMessages() {
933
935
  thinking.style.cssText = 'padding:8px 12px;border-radius:8px;font-size:var(--text-base);color:var(--muted);align-self:flex-start;display:flex;align-items:center;gap:8px';
934
936
  // eslint-disable-next-line no-unsanitized/property -- reason: composed from elapsed timer and compile-time thinking UI strings (no user data flows in)
935
937
  thinking.innerHTML = '<span class="dot-pulse" style="display:inline-flex;gap:3px"><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite"></span><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite;animation-delay:0.2s"></span><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite;animation-delay:0.4s"></span></span> <span id="cc-thinking-text">Still working...</span> <span id="cc-thinking-time" style="font-size:var(--text-sm);color:var(--border)">' + Math.floor(elapsed / 1000) + 's</span>' +
936
- ' <button onclick="ccNewTab()" style="font-size:var(--text-xs);padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer">Reset</button>';
938
+ ' <button onclick="ccNewTab()" style="font-size:var(--text-sm);padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer">Reset</button>';
937
939
  el.appendChild(thinking);
938
940
  el.scrollTop = el.scrollHeight;
939
941
  // Update timer
@@ -1012,7 +1014,7 @@ function ccAddMessage(role, html, skipSave, targetTabId, meta) {
1012
1014
  var isVisible = !targetTabId || targetTabId === _ccActiveTabId;
1013
1015
  if (isVisible) {
1014
1016
  var el = document.getElementById('cc-messages');
1015
- clearCcSuggestedPrompts(el);
1017
+ clearCcSuggestedPrompts(document.getElementById('cc-suggestions-host'));
1016
1018
  var div = document.createElement('div');
1017
1019
  div.className = isAssistant ? 'cc-msg-assistant' : isAction ? 'cc-msg-action' : '';
1018
1020
  if (messageId) {
@@ -1099,7 +1101,7 @@ function _renderQueueIndicator() {
1099
1101
  el.className = 'cc-queue-item';
1100
1102
  el.style.cssText = 'padding:8px 12px;border-radius:8px;font-size:var(--text-md);line-height:1.6;max-width:95%;align-self:flex-end;background:var(--blue);color:#fff;opacity:0.5;order:9999';
1101
1103
  // eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; queued message text wrapped in escHtml() (fields: m.message)
1102
- el.innerHTML = escHtml(typeof m === 'string' ? m : m.message) + '<div style="font-size:var(--text-xs);opacity:0.7;font-style:italic;margin-top:2px">queued</div>';
1104
+ el.innerHTML = escHtml(typeof m === 'string' ? m : m.message) + '<div style="font-size:var(--text-sm);opacity:0.7;font-style:italic;margin-top:2px">queued</div>';
1103
1105
  msgs.appendChild(el);
1104
1106
  });
1105
1107
  if (msgs.scrollHeight - msgs.scrollTop - msgs.clientHeight < 150) msgs.scrollTop = msgs.scrollHeight;
@@ -1211,7 +1213,7 @@ async function _ccDoSend(message, skipUserMsg, forceTabId, intentMetadata) {
1211
1213
  }
1212
1214
  var secs = Math.floor(elapsed / 1000);
1213
1215
  return '<div style="display:flex;align-items:center;gap:6px"><span style="color:var(--muted);font-size:var(--text-base)">' + label + '</span>' + dotPulse + '<span style="margin-left:auto;font-size:var(--text-sm);color:var(--muted)">' + secs + 's</span>' +
1214
- '<button onclick="ccAbort()" style="font-size:var(--text-xs);padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer;margin-left:4px">Stop</button></div>';
1216
+ '<button onclick="ccAbort()" style="font-size:var(--text-sm);padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer;margin-left:4px">Stop</button></div>';
1215
1217
  }
1216
1218
  function updateStreamDiv() {
1217
1219
  // Skip DOM updates if user switched to a different tab (restore interval handles that tab)
@@ -1239,7 +1241,7 @@ async function _ccDoSend(message, skipUserMsg, forceTabId, intentMetadata) {
1239
1241
  }
1240
1242
  function _ccElapsedFooter(label) {
1241
1243
  var seconds = Math.round((Date.now() - ccStartTime) / 1000);
1242
- return '<div style="font-size:var(--text-xs);color:var(--muted);margin-top:6px;display:flex;justify-content:flex-end;padding-right:30px">' + label.replace('{seconds}', seconds) + '</div>';
1244
+ return '<div style="font-size:var(--text-sm);color:var(--muted);margin-top:6px;display:flex;justify-content:flex-end;padding-right:30px">' + label.replace('{seconds}', seconds) + '</div>';
1243
1245
  }
1244
1246
  function _ccRetryControls(retryRequest, extraHtml, showReload) {
1245
1247
  var retryTabId = retryRequest && retryRequest.tabId ? retryRequest.tabId : activeTabId;
@@ -1396,7 +1398,7 @@ async function _ccDoSend(message, skipUserMsg, forceTabId, intentMetadata) {
1396
1398
  var ccErrCode = typeof evt.code === 'string' ? evt.code : '';
1397
1399
  var ccRetry = _ccStoreRetryRequest(activeTab, activeTabId, message);
1398
1400
  var codeChip = ccErrCode
1399
- ? '<span style="display:inline-block;margin-left:6px;padding:1px 6px;font-size:var(--text-xs);color:var(--muted);background:var(--surface2);border:1px solid var(--border);border-radius:3px;font-family:monospace">' + escHtml(ccErrCode) + '</span>'
1401
+ ? '<span style="display:inline-block;margin-left:6px;padding:1px 6px;font-size:var(--text-sm);color:var(--muted);background:var(--surface2);border:1px solid var(--border);border-radius:3px;font-family:monospace">' + escHtml(ccErrCode) + '</span>'
1400
1402
  : '';
1401
1403
  var availList = Array.isArray(evt.availableModels) && evt.availableModels.length
1402
1404
  ? '<div style="font-size:var(--text-sm);color:var(--muted);margin-top:6px">Available models: ' + escHtml(evt.availableModels.slice(0, 8).join(', ')) + (evt.availableModels.length > 8 ? '…' : '') + '</div>'
@@ -1547,7 +1549,7 @@ async function _ccDoSend(message, skipUserMsg, forceTabId, intentMetadata) {
1547
1549
  var errorRendered;
1548
1550
  if (ccEnv) {
1549
1551
  var ccCodeChip = ccEnv.code
1550
- ? '<span style="display:inline-block;margin-left:6px;padding:1px 6px;font-size:var(--text-xs);color:var(--muted);background:var(--surface2);border:1px solid var(--border);border-radius:3px;font-family:monospace">' + escHtml(ccEnv.code) + '</span>'
1552
+ ? '<span style="display:inline-block;margin-left:6px;padding:1px 6px;font-size:var(--text-sm);color:var(--muted);background:var(--surface2);border:1px solid var(--border);border-radius:3px;font-family:monospace">' + escHtml(ccEnv.code) + '</span>'
1551
1553
  : '';
1552
1554
  var ccAvail = Array.isArray(ccEnv.availableModels) && ccEnv.availableModels.length
1553
1555
  ? '<div style="font-size:var(--text-sm);color:var(--muted);margin-top:6px">Available models: ' + escHtml(ccEnv.availableModels.slice(0, 8).join(', ')) + (ccEnv.availableModels.length > 8 ? '…' : '') + '</div>'
@@ -352,7 +352,7 @@ async function sendSteering() {
352
352
  if (region) {
353
353
  // eslint-disable-next-line no-unsanitized/method -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: steering message)
354
354
  region.insertAdjacentHTML('beforeend', '<div style="align-self:flex-end;background:var(--blue);color:#fff;padding:6px 12px;border-radius:12px 12px 2px 12px;max-width:80%;margin:4px 0;font-size:var(--text-md)">' + escHtml(message) +
355
- '<div id="steer-pending" style="font-size:var(--text-xs);opacity:0.7;margin-top:2px">\u2197 Sending...</div></div>');
355
+ '<div id="steer-pending" style="font-size:var(--text-sm);opacity:0.7;margin-top:2px">\u2197 Sending...</div></div>');
356
356
  if (container) container.scrollTop = container.scrollHeight;
357
357
  }
358
358
  showToast('cmd-toast', 'Steering message sent to ' + currentAgentId, true);