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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +47 -60
- package/package.json +2 -1
- package/public/index.html +2 -2
- package/public/numbers.html +2 -2
- package/scripts/audit.js +65 -0
- package/scripts/cli-schema.js +8 -0
- package/scripts/hook-runtime.js +12 -12
- package/scripts/statusline.sh +41 -30
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate-marketplace",
|
|
3
|
-
"version": "1.21.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
219
|
+
const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.21.2' };
|
|
220
220
|
const COMMERCE_CATEGORIES = [
|
|
221
221
|
'product_recommendation',
|
|
222
222
|
'brand_compliance',
|
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
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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 (
|
|
451
|
-
|
|
452
|
-
|
|
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 ||
|
|
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.
|
|
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.
|
|
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.
|
|
1495
|
+
<span class="footer-copy">ยฉ 2026 ThumbGate ยท MIT License ยท npm v1.21.2</span>
|
|
1496
1496
|
</div>
|
|
1497
1497
|
</footer>
|
|
1498
1498
|
|
package/public/numbers.html
CHANGED
|
@@ -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.
|
|
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.
|
|
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>
|
package/scripts/audit.js
ADDED
|
@@ -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 };
|
package/scripts/cli-schema.js
CHANGED
|
@@ -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',
|
package/scripts/hook-runtime.js
CHANGED
|
@@ -33,34 +33,34 @@ function publishedHookCommandsAvailable(version) {
|
|
|
33
33
|
return available;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function
|
|
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
|
|
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
|
|
59
|
+
return resolveCliCommand(subcommand);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function buildCodexPortableHookCommand(subcommand) {
|
|
63
|
-
return
|
|
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
|
-
|
|
119
|
-
|
|
118
|
+
resolveCodexCliCommand,
|
|
119
|
+
resolveCliCommand,
|
|
120
120
|
sessionStartHookCommand,
|
|
121
121
|
statuslineCommand,
|
|
122
122
|
userPromptHookCommand,
|
package/scripts/statusline.sh
CHANGED
|
@@ -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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
203
|
-
|
|
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
|
-
|
|
214
|
-
|
|
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
|