thumbgate 1.21.0 โ†’ 1.21.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,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate-marketplace",
3
- "version": "1.21.0",
3
+ "version": "1.21.2",
4
4
  "owner": {
5
5
  "name": "Igor Ganapolsky",
6
6
  "email": "ig5973700@gmail.com"
@@ -14,7 +14,7 @@
14
14
  "source": "npm",
15
15
  "package": "thumbgate"
16
16
  },
17
- "version": "1.21.0",
17
+ "version": "1.21.2",
18
18
  "author": {
19
19
  "name": "Igor Ganapolsky",
20
20
  "email": "ig5973700@gmail.com",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "thumbgate",
3
3
  "description": "One ๐Ÿ‘Ž becomes a hard rule the agent cannot bypass. Captures thumbs-down feedback, distills it into PreToolUse Pre-Action Checks, enforced across every future Claude Code session.",
4
- "version": "1.21.0",
4
+ "version": "1.21.2",
5
5
  "author": {
6
6
  "name": "Igor Ganapolsky",
7
7
  "email": "ig5973700@gmail.com",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate",
3
- "version": "1.21.0",
3
+ "version": "1.21.2",
4
4
  "description": "ThumbGate โ€” ๐Ÿ‘๐Ÿ‘Ž feedback that teaches your AI agent. Thumbs down a mistake, it never happens again.",
5
5
  "homepage": "https://thumbgate-production.up.railway.app",
6
6
  "transport": "stdio",
@@ -2,13 +2,13 @@
2
2
  "mcpServers": {
3
3
  "thumbgate": {
4
4
  "command": "npx",
5
- "args": ["--yes", "--package", "thumbgate@1.21.0", "thumbgate", "serve"]
5
+ "args": ["--yes", "--package", "thumbgate@1.21.2", "thumbgate", "serve"]
6
6
  }
7
7
  },
8
8
  "hooks": {
9
9
  "preToolUse": {
10
10
  "command": "npx",
11
- "args": ["--yes", "--package", "thumbgate@1.21.0", "thumbgate", "gate-check"]
11
+ "args": ["--yes", "--package", "thumbgate@1.21.2", "thumbgate", "gate-check"]
12
12
  }
13
13
  }
14
14
  }
@@ -216,7 +216,7 @@ const {
216
216
  finalizeSession: finalizeFeedbackSession,
217
217
  } = require('../../scripts/feedback-session');
218
218
 
219
- const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.21.0' };
219
+ const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.21.2' };
220
220
  const COMMERCE_CATEGORIES = [
221
221
  'product_recommendation',
222
222
  'brand_compliance',
@@ -7,7 +7,7 @@
7
7
  "npx",
8
8
  "--yes",
9
9
  "--package",
10
- "thumbgate@1.21.0",
10
+ "thumbgate@1.21.2",
11
11
  "thumbgate",
12
12
  "serve"
13
13
  ],
package/bin/cli.js CHANGED
@@ -23,6 +23,7 @@
23
23
  * npx thumbgate background-governance # background-agent run report + risk check
24
24
  * npx thumbgate cfo # local operational billing summary
25
25
  * npx thumbgate pro # solo dashboard + exports side lane
26
+ * npx thumbgate audit <file> # audit an agent transcript for repeat-mistake token waste
26
27
  */
27
28
 
28
29
  'use strict';
@@ -40,13 +41,6 @@ const {
40
41
  resolveMcpEntry,
41
42
  } = require(path.join(__dirname, '..', 'scripts', 'mcp-config'));
42
43
  const { trackEvent } = require(path.join(__dirname, '..', 'scripts', 'cli-telemetry'));
43
- const {
44
- cacheUpdateHookCommand,
45
- preToolHookCommand,
46
- sessionStartHookCommand,
47
- statuslineCommand,
48
- userPromptHookCommand,
49
- } = require(path.join(__dirname, '..', 'scripts', 'hook-runtime'));
50
44
  const {
51
45
  PRO_MONTHLY_PAYMENT_LINK,
52
46
  PRO_PRICE_LABEL,
@@ -395,64 +389,24 @@ function whichExists(cmd) {
395
389
 
396
390
  function setupClaude() {
397
391
  const mcpChanged = mergeMcpJson(path.join(CWD, '.mcp.json'), 'Claude Code', 'project');
392
+ const { wireHooks } = require(path.join(PKG_ROOT, 'scripts', 'auto-wire-hooks'));
393
+ const hookResult = wireHooks({ agent: 'claude-code' });
398
394
 
399
- // Upsert Stop hook into .claude/settings.json for autonomous self-scoring
400
- const settingsPath = path.join(CWD, '.claude', 'settings.json');
401
- const stopHookCommand = 'bash scripts/hook-stop-self-score.sh';
402
-
403
- let settings = { hooks: {} };
404
- if (fs.existsSync(settingsPath)) {
405
- try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch (_) { /* fresh */ }
406
- }
407
- settings.hooks = settings.hooks || {};
408
-
409
- const stopAlreadyPresent = (settings.hooks.Stop || [])
410
- .some(entry => (entry.hooks || []).some(h => h.command === stopHookCommand));
411
-
412
- let hooksChanged = false;
413
- if (!stopAlreadyPresent) {
414
- settings.hooks.Stop = settings.hooks.Stop || [];
415
- settings.hooks.Stop.push({ hooks: [{ type: 'command', command: stopHookCommand }] });
416
- hooksChanged = true;
417
- console.log(' Claude Code: installed Stop hook');
418
- }
419
-
420
- // Upsert PostToolUse hook for ThumbGate statusline cache updates
421
- const cacheHookCommand = cacheUpdateHookCommand();
422
- const originalPostToolUseCount = (settings.hooks.PostToolUse || []).length;
423
- settings.hooks.PostToolUse = (settings.hooks.PostToolUse || []).filter(
424
- (entry) => !(entry.hooks || []).some((h) => h.command && h.command !== cacheHookCommand && /(hook-thumbgate-cache-updater|cache-update\b)/.test(h.command))
425
- );
426
- if (settings.hooks.PostToolUse.length !== originalPostToolUseCount) {
427
- hooksChanged = true;
428
- }
429
- const cacheAlreadyPresent = (settings.hooks.PostToolUse || [])
430
- .some(entry => (entry.hooks || []).some(h => h.command === cacheHookCommand || (h.command && h.command.includes('cache-update'))));
431
-
432
- if (!cacheAlreadyPresent) {
433
- settings.hooks.PostToolUse = settings.hooks.PostToolUse || [];
434
- settings.hooks.PostToolUse.push({
435
- matcher: 'mcp__thumbgate__feedback_stats|mcp__thumbgate__dashboard',
436
- hooks: [{ type: 'command', command: cacheHookCommand }]
437
- });
438
- hooksChanged = true;
439
- console.log(' Claude Code: installed ThumbGate cache updater hook');
440
- }
441
-
442
- // Upsert statusLine for ThumbGate feedback display
443
- const statuslineScript = statuslineCommand();
444
- if (!settings.statusLine || settings.statusLine.command !== statuslineScript) {
445
- settings.statusLine = { type: 'command', command: statuslineScript };
446
- hooksChanged = true;
447
- console.log(' Claude Code: installed ThumbGate status line');
395
+ if (hookResult.error) {
396
+ console.log(` Claude Code hooks: ${hookResult.error}`);
397
+ return mcpChanged;
448
398
  }
449
399
 
450
- if (hooksChanged) {
451
- fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
452
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
400
+ if (!hookResult.changed) {
401
+ console.log(` Claude Code hooks: already configured at ${hookResult.settingsPath}`);
402
+ } else {
403
+ for (const h of hookResult.added) {
404
+ console.log(` Claude Code: wired ${h.lifecycle} hook`);
405
+ }
406
+ console.log(` Claude Code settings: ${hookResult.settingsPath}`);
453
407
  }
454
408
 
455
- return mcpChanged || hooksChanged;
409
+ return mcpChanged || hookResult.changed;
456
410
  }
457
411
 
458
412
  function setupCodex() {
@@ -724,6 +678,11 @@ function init(cliArgs = parseArgs(process.argv.slice(3))) {
724
678
  let configured = 0;
725
679
 
726
680
  const platforms = [
681
+ { name: 'Claude Code', detect: [
682
+ () => whichExists('claude'),
683
+ () => fs.existsSync(path.join(HOME, '.claude')),
684
+ () => fs.existsSync(path.join(CWD, '.claude')),
685
+ ], setup: setupClaude },
727
686
  { name: 'Codex', detect: [() => whichExists('codex'), () => fs.existsSync(path.join(HOME, '.codex'))], setup: setupCodex },
728
687
  { name: 'Gemini', detect: [() => whichExists('gemini'), () => fs.existsSync(path.join(HOME, '.gemini'))], setup: setupGemini },
729
688
  { name: 'Amp', detect: [() => whichExists('amp'), () => fs.existsSync(path.join(HOME, '.amp'))], setup: setupAmp },
@@ -2458,6 +2417,11 @@ if (COMMAND === 'daemon' || COMMAND === 'serve-daemon') {
2458
2417
  }
2459
2418
 
2460
2419
  switch (COMMAND) {
2420
+ case '--version':
2421
+ case '-v':
2422
+ case 'version':
2423
+ console.log(pkgVersion());
2424
+ break;
2461
2425
  case 'init':
2462
2426
  init();
2463
2427
  upgradeNudge();
@@ -2858,6 +2822,29 @@ switch (COMMAND) {
2858
2822
  }
2859
2823
  break;
2860
2824
  }
2825
+ case 'audit': {
2826
+ const auditFile = process.argv[3];
2827
+ if (!auditFile) {
2828
+ console.error('Usage: npx thumbgate audit <path-to-transcript.txt>');
2829
+ process.exit(1);
2830
+ }
2831
+ const { runAudit } = require(path.join(PKG_ROOT, 'scripts', 'audit'));
2832
+ const { results, totalWaste, error } = runAudit(auditFile);
2833
+ if (error) {
2834
+ console.error(error);
2835
+ process.exit(1);
2836
+ }
2837
+ console.log('\n๐Ÿ” AI Bill Audit Results\n');
2838
+ if (results.length === 0) {
2839
+ console.log('โœ… No repeat-offender patterns found. Your sessions are efficient!');
2840
+ } else {
2841
+ console.table(results);
2842
+ console.log('\n๐Ÿ’ฐ Total estimated monthly waste: $' + totalWaste);
2843
+ console.log('\nBlock these mistakes permanently with ThumbGate Pro:');
2844
+ console.log(PRO_CHECKOUT_URL);
2845
+ }
2846
+ break;
2847
+ }
2861
2848
  case 'dashboard':
2862
2849
  dashboard();
2863
2850
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thumbgate",
3
- "version": "1.21.0",
3
+ "version": "1.21.2",
4
4
  "description": "ThumbGate self-improving agent governance: thumbs-up/down turns every mistake into a prevention rule and blocks repeat patterns. 33 pre-action checks, budget enforcement, and self-protection for Claude Code, Cursor, Codex, Gemini CLI, and Amp.",
5
5
  "homepage": "https://thumbgate-production.up.railway.app",
6
6
  "repository": {
@@ -31,6 +31,7 @@
31
31
  "scripts/analytics-window.js",
32
32
  "scripts/async-job-runner.js",
33
33
  "scripts/audit-trail.js",
34
+ "scripts/audit.js",
34
35
  "scripts/auto-promote-gates.js",
35
36
  "scripts/auto-wire-hooks.js",
36
37
  "scripts/autoresearch-runner.js",
package/public/index.html CHANGED
@@ -19,7 +19,7 @@ __GOOGLE_SITE_VERIFICATION_META__
19
19
  <meta property="og:image" content="https://thumbgate-production.up.railway.app/og.png">
20
20
  <meta name="twitter:card" content="summary_large_image">
21
21
  <meta name="twitter:image" content="https://thumbgate-production.up.railway.app/og.png">
22
- <meta name="thumbgate-version" content="1.21.0">
22
+ <meta name="thumbgate-version" content="1.21.2">
23
23
  <meta name="keywords" content="ThumbGate, thumbgate, AI agent orchestration, AI experience orchestration, agent enforcement layer, save LLM tokens, reduce Claude API cost, reduce OpenAI cost, AI agent token savings, prevent LLM retries, prevent hallucination retries, stop AI token waste, pre-action checks, agent governance, Claude Code, Cursor, Codex, Gemini, Amp, Cline, OpenCode, workflow hardening, context engineering, AI authenticity, brand authenticity AI">
24
24
  <link rel="apple-touch-icon" href="/apple-touch-icon.png">
25
25
 
@@ -1492,7 +1492,7 @@ __GA_BOOTSTRAP__
1492
1492
  <a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
1493
1493
  <a href="/blog">Blog</a>
1494
1494
  </div>
1495
- <span class="footer-copy">ยฉ 2026 ThumbGate ยท MIT License ยท npm v1.21.0</span>
1495
+ <span class="footer-copy">ยฉ 2026 ThumbGate ยท MIT License ยท npm v1.21.2</span>
1496
1496
  </div>
1497
1497
  </footer>
1498
1498
 
@@ -25,7 +25,7 @@
25
25
  "alternateName": "thumbgate",
26
26
  "applicationCategory": "DeveloperApplication",
27
27
  "operatingSystem": "Cross-platform, Node.js >=18.18.0",
28
- "softwareVersion": "1.21.0",
28
+ "softwareVersion": "1.21.2",
29
29
  "url": "https://thumbgate-production.up.railway.app/numbers",
30
30
  "dateModified": "2026-05-07",
31
31
  "creator": {
@@ -202,7 +202,7 @@
202
202
  <main class="container">
203
203
  <h1>The Numbers</h1>
204
204
  <p class="subtitle">Generated first-party operational snapshot from the ThumbGate runtime. This is not customer traction, install volume, revenue, or proof that a configured gate has fired.</p>
205
- <div class="freshness">Updated: 2026-05-07 ยท Version 1.21.0</div>
205
+ <div class="freshness">Updated: 2026-05-07 ยท Version 1.21.2</div>
206
206
  <div class="truth-note"><strong>Read this first:</strong> configured checks are inventory. Recorded blocks and warnings are usage evidence. This snapshot currently reports 0 recorded hard-block event(s) and 0 recorded warning event(s).</div>
207
207
 
208
208
  <h2>Gate enforcement</h2>
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * scripts/audit.js
5
+ *
6
+ * Heuristic-based AI bill auditor. Finds repeated mistakes in agent transcripts.
7
+ */
8
+
9
+ const fs = require('fs');
10
+
11
+ const PATTERNS = [
12
+ {
13
+ id: 'force-push-retry',
14
+ name: 'git push --force after correction',
15
+ regex: /git\s+push.*--force/gi,
16
+ tokenEstimate: 6000,
17
+ costPerRepeat: 0.44,
18
+ why: 'Full diff context reload on error.'
19
+ },
20
+ {
21
+ id: 'import-loop',
22
+ name: 'Hallucinated import retry',
23
+ regex: /(Cannot find module|Module not found|import .* from .*error)/gi,
24
+ tokenEstimate: 4000,
25
+ costPerRepeat: 0.12,
26
+ why: 'Re-indexing and path searching.'
27
+ },
28
+ {
29
+ id: 'apology-loop',
30
+ name: '"I apologize" retry cycle',
31
+ regex: /(I apologize|Let me try a different approach|I will now attempt)/gi,
32
+ tokenEstimate: 5000,
33
+ costPerRepeat: 0.15,
34
+ why: 'Reasoning chain reset.'
35
+ }
36
+ ];
37
+
38
+ function runAudit(filePath) {
39
+ if (!fs.existsSync(filePath)) {
40
+ return { error: 'File not found: ' + filePath };
41
+ }
42
+
43
+ const content = fs.readFileSync(filePath, 'utf8');
44
+ const results = [];
45
+ let totalWaste = 0;
46
+
47
+ PATTERNS.forEach(p => {
48
+ const matches = (content.match(p.regex) || []).length;
49
+ if (matches > 1) { // It's only a "repeat" if it happens more than once
50
+ const repeats = matches - 1;
51
+ const waste = repeats * p.costPerRepeat;
52
+ totalWaste += waste;
53
+ results.push({
54
+ pattern: p.name,
55
+ occurrences: repeats,
56
+ waste: waste.toFixed(2),
57
+ why: p.why
58
+ });
59
+ }
60
+ });
61
+
62
+ return { results, totalWaste: totalWaste.toFixed(2) };
63
+ }
64
+
65
+ module.exports = { runAudit, PATTERNS };
@@ -525,6 +525,14 @@ const CLI_COMMANDS = [
525
525
  { name: 'json', type: 'boolean', description: 'Output as JSON' },
526
526
  ],
527
527
  },
528
+ {
529
+ name: 'audit',
530
+ description: 'Audit an agent transcript for repeat-mistake patterns and estimated token waste',
531
+ group: 'ops',
532
+ flags: [
533
+ { name: 'file', type: 'string', description: 'Path to the agent transcript to audit' },
534
+ ],
535
+ },
528
536
  {
529
537
  name: 'init',
530
538
  description: 'Scaffold .thumbgate/ config and wire agent hooks',
@@ -33,34 +33,34 @@ function publishedHookCommandsAvailable(version) {
33
33
  return available;
34
34
  }
35
35
 
36
- function resolveCliBaseCommand() {
36
+ function resolveCliCommand(subcommand) {
37
37
  const version = packageVersion();
38
38
  if (publishedHookCommandsAvailable(version)) {
39
- return publishedCliShellCommand(version);
39
+ return publishedCliShellCommand(version, [subcommand]);
40
40
  }
41
41
  if (isSourceCheckout(PKG_ROOT)) {
42
- return `node ${shellQuote(path.join(PKG_ROOT, 'bin', 'cli.js'))}`;
42
+ return `node ${shellQuote(path.join(PKG_ROOT, 'bin', 'cli.js'))} ${subcommand}`;
43
43
  }
44
- return publishedCliShellCommand(version);
44
+ return publishedCliShellCommand(version, [subcommand]);
45
45
  }
46
46
 
47
- function resolveCodexCliBaseCommand() {
47
+ function resolveCodexCliCommand(subcommand) {
48
48
  const version = packageVersion();
49
49
  if (publishedHookCommandsAvailable(version)) {
50
- return publishedCliShellCommand('latest', [], { preferInstalled: false });
50
+ return publishedCliShellCommand('latest', [subcommand], { preferInstalled: false });
51
51
  }
52
52
  if (isSourceCheckout(PKG_ROOT)) {
53
- return `node ${shellQuote(path.join(PKG_ROOT, 'bin', 'cli.js'))}`;
53
+ return `node ${shellQuote(path.join(PKG_ROOT, 'bin', 'cli.js'))} ${subcommand}`;
54
54
  }
55
- return publishedCliShellCommand('latest', [], { preferInstalled: false });
55
+ return publishedCliShellCommand('latest', [subcommand], { preferInstalled: false });
56
56
  }
57
57
 
58
58
  function buildPortableHookCommand(subcommand) {
59
- return `${resolveCliBaseCommand()} ${subcommand}`;
59
+ return resolveCliCommand(subcommand);
60
60
  }
61
61
 
62
62
  function buildCodexPortableHookCommand(subcommand) {
63
- return `${resolveCodexCliBaseCommand()} ${subcommand}`;
63
+ return resolveCodexCliCommand(subcommand);
64
64
  }
65
65
 
66
66
  function preToolHookCommand() {
@@ -115,8 +115,8 @@ module.exports = {
115
115
  packageVersion,
116
116
  publishedHookCommandsAvailable,
117
117
  preToolHookCommand,
118
- resolveCodexCliBaseCommand,
119
- resolveCliBaseCommand,
118
+ resolveCodexCliCommand,
119
+ resolveCliCommand,
120
120
  sessionStartHookCommand,
121
121
  statuslineCommand,
122
122
  userPromptHookCommand,
@@ -7,6 +7,7 @@
7
7
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
8
8
  case "$SCRIPT_DIR" in *[!a-zA-Z0-9/_.-]*) echo "ThumbGate: invalid script path"; exit 1;; esac
9
9
  LOCAL_API_ORIGIN="${THUMBGATE_LOCAL_API_ORIGIN:-http://localhost:3456}"
10
+ STATUSLINE_VERBOSE="${THUMBGATE_STATUSLINE_VERBOSE:-0}"
10
11
 
11
12
  # โ”€โ”€ Parse Claude Code session JSON from stdin โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
12
13
  eval "$(cat | jq -r '
@@ -92,17 +93,19 @@ fi
92
93
  LINK_STATE="offline"
93
94
  UP_URL=""; DOWN_URL=""; DASHBOARD_URL=""; LESSONS_URL=""
94
95
  DASHBOARD_LABEL="Dashboard"; LESSONS_LABEL="Lessons"
95
- _LINKS_JSON=$(node "${SCRIPT_DIR}/statusline-links.js" 2>/dev/null)
96
- if [ -n "$_LINKS_JSON" ]; then
97
- eval "$(echo "$_LINKS_JSON" | jq -r '
98
- @sh "LINK_STATE=\(.state // "offline")",
99
- @sh "UP_URL=\(.upUrl // "")",
100
- @sh "DOWN_URL=\(.downUrl // "")",
101
- @sh "DASHBOARD_URL=\(.dashboardUrl // "")",
102
- @sh "LESSONS_URL=\(.lessonsUrl // "")",
103
- @sh "DASHBOARD_LABEL=\(.dashboardLabel // "Dashboard")",
104
- @sh "LESSONS_LABEL=\(.lessonsLabel // "Lessons")"
105
- ' 2>/dev/null)"
96
+ if [[ "$STATUSLINE_VERBOSE" = "1" ]]; then
97
+ _LINKS_JSON=$(node "${SCRIPT_DIR}/statusline-links.js" 2>/dev/null)
98
+ if [ -n "$_LINKS_JSON" ]; then
99
+ eval "$(echo "$_LINKS_JSON" | jq -r '
100
+ @sh "LINK_STATE=\(.state // "offline")",
101
+ @sh "UP_URL=\(.upUrl // "")",
102
+ @sh "DOWN_URL=\(.downUrl // "")",
103
+ @sh "DASHBOARD_URL=\(.dashboardUrl // "")",
104
+ @sh "LESSONS_URL=\(.lessonsUrl // "")",
105
+ @sh "DASHBOARD_LABEL=\(.dashboardLabel // "Dashboard")",
106
+ @sh "LESSONS_LABEL=\(.lessonsLabel // "Lessons")"
107
+ ' 2>/dev/null)"
108
+ fi
106
109
  fi
107
110
 
108
111
  # โ”€โ”€ ThumbGate package metadata โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -117,13 +120,15 @@ fi
117
120
 
118
121
  # โ”€โ”€ Repo context (branch / work item / PR) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
119
122
  BRANCH_NAME=""; WORK_ITEM_LABEL=""; PR_LABEL=""
120
- _CONTEXT_JSON=$(node "${SCRIPT_DIR}/statusline-context.js" 2>/dev/null)
121
- if [[ -n "$_CONTEXT_JSON" ]]; then
122
- eval "$(echo "$_CONTEXT_JSON" | jq -r '
123
- @sh "BRANCH_NAME=\(.branchName // "")",
124
- @sh "WORK_ITEM_LABEL=\(.workItemLabel // "")",
125
- @sh "PR_LABEL=\(.prLabel // "")"
126
- ' 2>/dev/null)"
123
+ if [[ "$STATUSLINE_VERBOSE" = "1" ]]; then
124
+ _CONTEXT_JSON=$(node "${SCRIPT_DIR}/statusline-context.js" 2>/dev/null)
125
+ if [[ -n "$_CONTEXT_JSON" ]]; then
126
+ eval "$(echo "$_CONTEXT_JSON" | jq -r '
127
+ @sh "BRANCH_NAME=\(.branchName // "")",
128
+ @sh "WORK_ITEM_LABEL=\(.workItemLabel // "")",
129
+ @sh "PR_LABEL=\(.prLabel // "")"
130
+ ' 2>/dev/null)"
131
+ fi
127
132
  fi
128
133
 
129
134
  # โ”€โ”€ Control Tower stats โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -139,14 +144,16 @@ fi
139
144
 
140
145
  # โ”€โ”€ Latest lesson (data available for extensions; not rendered in statusbar) โ”€โ”€
141
146
  LESSON_TEXT=""; LESSON_ID=""; LESSON_LABEL=""; LESSON_LINK=""
142
- _LESSON_JSON=$(node "${SCRIPT_DIR}/statusline-lesson.js" 2>/dev/null)
143
- if [[ -n "$_LESSON_JSON" ]]; then
144
- eval "$(echo "$_LESSON_JSON" | jq -r '
145
- @sh "LESSON_TEXT=\(.text // "")",
146
- @sh "LESSON_ID=\(.lessonId // "")",
147
- @sh "LESSON_LABEL=\(.label // "")",
148
- @sh "LESSON_LINK=\(.link // "")"
149
- ' 2>/dev/null)"
147
+ if [[ "$STATUSLINE_VERBOSE" = "1" ]]; then
148
+ _LESSON_JSON=$(node "${SCRIPT_DIR}/statusline-lesson.js" 2>/dev/null)
149
+ if [[ -n "$_LESSON_JSON" ]]; then
150
+ eval "$(echo "$_LESSON_JSON" | jq -r '
151
+ @sh "LESSON_TEXT=\(.text // "")",
152
+ @sh "LESSON_ID=\(.lessonId // "")",
153
+ @sh "LESSON_LABEL=\(.label // "")",
154
+ @sh "LESSON_LINK=\(.link // "")"
155
+ ' 2>/dev/null)"
156
+ fi
150
157
  fi
151
158
 
152
159
  # โ”€โ”€ Colors โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -199,8 +206,10 @@ LINE="${LINE:+${LINE} ยท }ThumbGate v${TG_VERSION} ยท ${TG_TIER}"
199
206
  if [[ "$UP" = "0" && "$DOWN" = "0" ]]; then
200
207
  LINE="${D}${LINE}${RST} ยท no feedback yet"
201
208
  [[ -n "$PR_LABEL" ]] && LINE="${LINE} ยท ${D}${PR_LABEL}${RST}"
202
- LINE="${LINE} ยท ${C}${DASHBOARD_LINK}${RST} ยท ${M}${LESSONS_LINK}${RST}"
203
- [[ -n "$LATEST_LESSON_LINK" ]] && LINE="${LINE} ยท ${D}${LATEST_LESSON_LINK}${RST}"
209
+ if [[ "$STATUSLINE_VERBOSE" = "1" ]]; then
210
+ LINE="${LINE} ยท ${C}${DASHBOARD_LINK}${RST} ยท ${M}${LESSONS_LINK}${RST}"
211
+ [[ -n "$LATEST_LESSON_LINK" ]] && LINE="${LINE} ยท ${D}${LATEST_LESSON_LINK}${RST}"
212
+ fi
204
213
  printf '%b\n' "$LINE"
205
214
  else
206
215
  LINE="${LINE} ยท ${G}${BD}${UP}${RST}${UP_LINK} ${R}${BD}${DOWN}${RST}${DOWN_LINK} ${ARROW}"
@@ -210,8 +219,10 @@ else
210
219
  [[ "${AT_RISK:-0}" -gt 0 ]] && LINE="${LINE} ${R}${AT_RISK}โš ${RST}"
211
220
  [[ "${ANOMALIES:-0}" -gt 0 ]] && LINE="${LINE} ${R}${ANOMALIES}โ˜ ${RST}"
212
221
  [[ -n "$PR_LABEL" ]] && LINE="${LINE} ยท ${D}${PR_LABEL}${RST}"
213
- LINE="${LINE} ยท ${C}${DASHBOARD_LINK}${RST} ยท ${M}${LESSONS_LINK}${RST}"
214
- [[ -n "$LATEST_LESSON_LINK" ]] && LINE="${LINE} ยท ${D}${LATEST_LESSON_LINK}${RST}"
222
+ if [[ "$STATUSLINE_VERBOSE" = "1" ]]; then
223
+ LINE="${LINE} ยท ${C}${DASHBOARD_LINK}${RST} ยท ${M}${LESSONS_LINK}${RST}"
224
+ [[ -n "$LATEST_LESSON_LINK" ]] && LINE="${LINE} ยท ${D}${LATEST_LESSON_LINK}${RST}"
225
+ fi
215
226
 
216
227
  printf '%b\n' "$LINE"
217
228
  fi