thumbgate 1.2.0 → 1.4.0
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/README.md +4 -4
- package/.claude-plugin/marketplace.json +32 -13
- package/.claude-plugin/plugin.json +15 -2
- package/.well-known/llms.txt +60 -0
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +133 -23
- package/adapters/README.md +1 -1
- package/adapters/chatgpt/openapi.yaml +168 -0
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/codex/config.toml +2 -2
- package/adapters/mcp/server-stdio.js +85 -2
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +215 -19
- package/bin/postinstall.js +8 -2
- package/config/budget.json +18 -0
- package/config/gates/code-edit.json +61 -0
- package/config/gates/db-write.json +61 -0
- package/config/gates/default.json +154 -3
- package/config/gates/deploy.json +61 -0
- package/config/github-about.json +2 -1
- package/config/merge-quality-checks.json +23 -0
- package/config/model-tiers.json +11 -0
- package/openapi/openapi.yaml +168 -0
- package/package.json +47 -13
- package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-codex-bridge/.mcp.json +1 -1
- package/plugins/claude-codex-bridge/scripts/codex-bridge.js +1 -3
- package/plugins/codex-profile/.codex-plugin/plugin.json +1 -1
- package/plugins/codex-profile/.mcp.json +1 -1
- package/plugins/codex-profile/INSTALL.md +27 -4
- package/plugins/codex-profile/README.md +33 -9
- package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor-marketplace/README.md +2 -2
- package/plugins/cursor-marketplace/commands/capture-feedback.md +2 -2
- package/plugins/cursor-marketplace/rules/feedback-capture.mdc +3 -3
- package/plugins/cursor-marketplace/skills/capture-feedback/SKILL.md +3 -2
- package/plugins/opencode-profile/INSTALL.md +1 -1
- package/public/blog.html +73 -0
- package/public/compare/mem0.html +189 -0
- package/public/compare/speclock.html +180 -0
- package/public/compare.html +12 -4
- package/public/guide.html +5 -5
- package/public/guides/claude-code-prevent-repeated-mistakes.html +161 -0
- package/public/guides/codex-cli-guardrails.html +158 -0
- package/public/guides/cursor-prevent-repeated-mistakes.html +161 -0
- package/public/guides/pre-action-gates.html +162 -0
- package/public/guides/stop-repeated-ai-agent-mistakes.html +159 -0
- package/public/index.html +169 -70
- package/public/learn/ai-agent-persistent-memory.html +1 -0
- package/public/lessons.html +334 -17
- package/public/llm-context.md +140 -0
- package/public/pro.html +24 -22
- package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
- package/scripts/access-anomaly-detector.js +1 -1
- package/scripts/adk-consolidator.js +1 -5
- package/scripts/agent-security-hardening.js +4 -6
- package/scripts/agentic-data-pipeline.js +1 -3
- package/scripts/async-job-runner.js +1 -5
- package/scripts/audit-trail.js +7 -5
- package/scripts/background-agent-governance.js +2 -10
- package/scripts/billing.js +2 -16
- package/scripts/budget-enforcer.js +173 -0
- package/scripts/build-codex-plugin.js +152 -0
- package/scripts/capture-railway-diagnostics.sh +97 -0
- package/scripts/check-congruence.js +133 -15
- package/scripts/claude-feedback-sync.js +320 -0
- package/scripts/cli-telemetry.js +4 -1
- package/scripts/commercial-offer.js +5 -7
- package/scripts/content-engine/linkedin-content-generator.js +154 -0
- package/scripts/content-engine/output/linkedin-memento-validation.md +17 -0
- package/scripts/content-engine/output/linkedin-posts-2026-04-09.md +175 -0
- package/scripts/content-engine/reddit-thread-finder.js +154 -0
- package/scripts/context-engine.js +21 -6
- package/scripts/contextfs.js +33 -44
- package/scripts/dashboard.js +104 -0
- package/scripts/decision-journal.js +341 -0
- package/scripts/delegation-runtime.js +1 -5
- package/scripts/distribution-surfaces.js +26 -0
- package/scripts/document-intake.js +927 -0
- package/scripts/ephemeral-agent-store.js +1 -8
- package/scripts/evolution-state.js +1 -5
- package/scripts/experiment-tracker.js +1 -5
- package/scripts/export-databricks-bundle.js +1 -5
- package/scripts/export-hf-dataset.js +1 -5
- package/scripts/export-training.js +1 -5
- package/scripts/feedback-attribution.js +1 -16
- package/scripts/feedback-history-distiller.js +1 -16
- package/scripts/feedback-loop.js +17 -5
- package/scripts/feedback-root-consolidator.js +2 -21
- package/scripts/feedback-session.js +49 -0
- package/scripts/feedback-to-rules.js +188 -28
- package/scripts/filesystem-search.js +1 -9
- package/scripts/fs-utils.js +104 -0
- package/scripts/gates-engine.js +149 -4
- package/scripts/github-about.js +32 -8
- package/scripts/gtm-revenue-loop.js +1 -5
- package/scripts/harness-selector.js +148 -0
- package/scripts/hosted-job-launcher.js +1 -5
- package/scripts/hybrid-feedback-context.js +7 -33
- package/scripts/intervention-policy.js +753 -0
- package/scripts/lesson-db.js +3 -18
- package/scripts/lesson-inference.js +194 -16
- package/scripts/lesson-retrieval.js +60 -24
- package/scripts/llm-client.js +59 -0
- package/scripts/local-model-profile.js +18 -2
- package/scripts/managed-lesson-agent.js +183 -0
- package/scripts/marketing-experiment.js +8 -22
- package/scripts/meta-agent-loop.js +624 -0
- package/scripts/metered-billing.js +1 -1
- package/scripts/model-tier-router.js +10 -1
- package/scripts/money-watcher.js +1 -4
- package/scripts/obsidian-export.js +1 -5
- package/scripts/operational-integrity.js +369 -34
- package/scripts/org-dashboard.js +6 -1
- package/scripts/per-step-scoring.js +2 -4
- package/scripts/pr-manager.js +201 -19
- package/scripts/pro-features.js +3 -2
- package/scripts/prompt-dlp.js +3 -3
- package/scripts/prove-adapters.js +2 -5
- package/scripts/prove-attribution.js +1 -5
- package/scripts/prove-automation.js +3 -5
- package/scripts/prove-cloudflare-sandbox.js +1 -3
- package/scripts/prove-data-pipeline.js +1 -3
- package/scripts/prove-intelligence.js +1 -3
- package/scripts/prove-lancedb.js +1 -5
- package/scripts/prove-local-intelligence.js +1 -3
- package/scripts/prove-packaged-runtime.js +326 -0
- package/scripts/prove-predictive-insights.js +1 -3
- package/scripts/prove-runtime.js +13 -0
- package/scripts/prove-training-export.js +1 -3
- package/scripts/prove-workflow-contract.js +1 -5
- package/scripts/rate-limiter.js +6 -4
- package/scripts/reddit-dm-outreach.js +14 -4
- package/scripts/schedule-manager.js +3 -5
- package/scripts/security-scanner.js +448 -0
- package/scripts/self-distill-agent.js +579 -0
- package/scripts/semantic-dedup.js +115 -0
- package/scripts/skill-exporter.js +1 -3
- package/scripts/skill-generator.js +1 -5
- package/scripts/social-analytics/engagement-audit.js +1 -18
- package/scripts/social-analytics/pollers/linkedin.js +26 -16
- package/scripts/social-analytics/publishers/linkedin.js +1 -1
- package/scripts/social-analytics/publishers/zernio.js +51 -0
- package/scripts/social-pipeline.js +1 -3
- package/scripts/social-post-hourly.js +47 -4
- package/scripts/statusline-links.js +6 -5
- package/scripts/statusline-local-stats.js +2 -0
- package/scripts/statusline.sh +38 -7
- package/scripts/sync-branch-protection.js +340 -0
- package/scripts/tessl-export.js +1 -3
- package/scripts/thumbgate-search.js +32 -1
- package/scripts/tool-kpi-tracker.js +1 -1
- package/scripts/tool-registry.js +108 -4
- package/scripts/vector-store.js +1 -5
- package/scripts/weekly-auto-post.js +1 -1
- package/scripts/workflow-sentinel.js +205 -4
- package/skills/thumbgate/SKILL.md +2 -2
- package/src/api/server.js +273 -4
- package/scripts/social-analytics/db/social-analytics.db-shm +0 -0
- /package/scripts/social-analytics/db/{social-analytics.db-wal → analytics.sqlite} +0 -0
|
@@ -84,6 +84,11 @@ const {
|
|
|
84
84
|
const {
|
|
85
85
|
searchThumbgate,
|
|
86
86
|
} = require('../../scripts/thumbgate-search');
|
|
87
|
+
const {
|
|
88
|
+
importDocument,
|
|
89
|
+
listImportedDocuments,
|
|
90
|
+
readImportedDocument,
|
|
91
|
+
} = require('../../scripts/document-intake');
|
|
87
92
|
const { checkLimit, UPGRADE_MESSAGE } = require('../../scripts/rate-limiter');
|
|
88
93
|
const { generateOrgDashboard } = require('../../scripts/org-dashboard');
|
|
89
94
|
const {
|
|
@@ -105,7 +110,7 @@ function enforceLimit(action) {
|
|
|
105
110
|
const limit = checkLimit(action);
|
|
106
111
|
if (!limit.allowed) {
|
|
107
112
|
const err = new Error(
|
|
108
|
-
`Free tier
|
|
113
|
+
`Free tier limit reached. Upgrade to Pro for unlimited: https://thumbgate-production.up.railway.app/pro\n${UPGRADE_MESSAGE}\nUpgrade now: ${PRO_CHECKOUT_URL}`
|
|
109
114
|
);
|
|
110
115
|
err.errorCategory = 'rate_limit';
|
|
111
116
|
err.isRetryable = false;
|
|
@@ -119,7 +124,7 @@ const {
|
|
|
119
124
|
finalizeSession: finalizeFeedbackSession,
|
|
120
125
|
} = require('../../scripts/feedback-session');
|
|
121
126
|
|
|
122
|
-
const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.
|
|
127
|
+
const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.4.0' };
|
|
123
128
|
const COMMERCE_CATEGORIES = [
|
|
124
129
|
'product_recommendation',
|
|
125
130
|
'brand_compliance',
|
|
@@ -145,6 +150,28 @@ function resolveSafePath(targetPath, { mustExist = false } = {}) {
|
|
|
145
150
|
return resolved;
|
|
146
151
|
}
|
|
147
152
|
|
|
153
|
+
function resolveImportDocumentPath(targetPath) {
|
|
154
|
+
const workspaceRoot = path.resolve(process.cwd());
|
|
155
|
+
const resolved = path.resolve(workspaceRoot, String(targetPath || ''));
|
|
156
|
+
const allowedRoots = [workspaceRoot, SAFE_DATA_DIR]
|
|
157
|
+
.filter(Boolean)
|
|
158
|
+
.map((root) => path.resolve(root));
|
|
159
|
+
const allowed = allowedRoots.some((root) => {
|
|
160
|
+
const relative = path.relative(root, resolved);
|
|
161
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (!allowed) {
|
|
165
|
+
throw new Error(`Path must stay within ${workspaceRoot} or ${SAFE_DATA_DIR}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!fs.existsSync(resolved)) {
|
|
169
|
+
throw new Error(`Path does not exist: ${resolved}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return resolved;
|
|
173
|
+
}
|
|
174
|
+
|
|
148
175
|
function toTextResult(payload) {
|
|
149
176
|
const text = typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2);
|
|
150
177
|
return {
|
|
@@ -421,6 +448,29 @@ async function callToolInner(name, args) {
|
|
|
421
448
|
source: args.source,
|
|
422
449
|
signal: args.signal,
|
|
423
450
|
}));
|
|
451
|
+
case 'import_document':
|
|
452
|
+
return toTextResult(importDocument({
|
|
453
|
+
filePath: args.filePath ? resolveImportDocumentPath(args.filePath) : null,
|
|
454
|
+
content: typeof args.content === 'string' ? args.content : null,
|
|
455
|
+
title: args.title,
|
|
456
|
+
sourceFormat: args.sourceFormat,
|
|
457
|
+
sourceUrl: args.sourceUrl,
|
|
458
|
+
tags: Array.isArray(args.tags) ? args.tags : [],
|
|
459
|
+
proposeGates: args.proposeGates !== false,
|
|
460
|
+
}));
|
|
461
|
+
case 'list_imported_documents':
|
|
462
|
+
return toTextResult(listImportedDocuments({
|
|
463
|
+
query: args.query || '',
|
|
464
|
+
tag: args.tag || null,
|
|
465
|
+
limit: Number(args.limit || 20),
|
|
466
|
+
}));
|
|
467
|
+
case 'get_imported_document': {
|
|
468
|
+
const document = readImportedDocument(args.documentId);
|
|
469
|
+
if (!document) {
|
|
470
|
+
throw new Error(`Imported document not found: ${args.documentId}`);
|
|
471
|
+
}
|
|
472
|
+
return toTextResult(document);
|
|
473
|
+
}
|
|
424
474
|
case 'feedback_stats':
|
|
425
475
|
return toTextResult(analyzeFeedback());
|
|
426
476
|
case 'diagnose_failure':
|
|
@@ -489,6 +539,19 @@ async function callToolInner(name, args) {
|
|
|
489
539
|
}));
|
|
490
540
|
case 'enforcement_matrix':
|
|
491
541
|
return toTextResult(listEnforcementMatrix());
|
|
542
|
+
case 'security_scan': {
|
|
543
|
+
const { scanCode, scanDependencyChange, scanGitDiff } = require('../../scripts/security-scanner');
|
|
544
|
+
if (args.diffMode) {
|
|
545
|
+
return toTextResult(scanGitDiff(args.content));
|
|
546
|
+
}
|
|
547
|
+
const codeResult = scanCode(args.content, args.filePath || '');
|
|
548
|
+
if (args.filePath && args.filePath.endsWith('package.json')) {
|
|
549
|
+
const supplyResult = scanDependencyChange('', args.content);
|
|
550
|
+
codeResult.findings = (codeResult.findings || []).concat(supplyResult.findings || []);
|
|
551
|
+
codeResult.detected = codeResult.detected || supplyResult.detected;
|
|
552
|
+
}
|
|
553
|
+
return toTextResult(codeResult);
|
|
554
|
+
}
|
|
492
555
|
case 'prevention_rules': {
|
|
493
556
|
const outputPath = args.outputPath ? resolveSafePath(args.outputPath) : undefined;
|
|
494
557
|
return toTextResult(writePreventionRules(outputPath, Number(args.minOccurrences || 2)));
|
|
@@ -680,6 +743,26 @@ async function callToolInner(name, args) {
|
|
|
680
743
|
return toTextResult(appendFeedbackContext(args.sessionId, args.message, args.role));
|
|
681
744
|
case 'finalize_feedback_session':
|
|
682
745
|
return toTextResult(finalizeFeedbackSession(args.sessionId));
|
|
746
|
+
case 'run_managed_lesson_agent': {
|
|
747
|
+
const { runManagedAgent } = require('../../scripts/managed-lesson-agent');
|
|
748
|
+
return toTextResult(await runManagedAgent({ dryRun: args.dryRun, limit: args.limit, model: args.model }));
|
|
749
|
+
}
|
|
750
|
+
case 'managed_agent_status': {
|
|
751
|
+
const { getManagedAgentStatus } = require('../../scripts/managed-lesson-agent');
|
|
752
|
+
return toTextResult(getManagedAgentStatus() || { message: 'No managed agent runs recorded yet.' });
|
|
753
|
+
}
|
|
754
|
+
case 'run_self_distill': {
|
|
755
|
+
const { runSelfDistill } = require('../../scripts/self-distill-agent');
|
|
756
|
+
return toTextResult(await runSelfDistill({ dryRun: args.dryRun, limit: args.limit, model: args.model }));
|
|
757
|
+
}
|
|
758
|
+
case 'self_distill_status': {
|
|
759
|
+
const { getSelfDistillStatus } = require('../../scripts/self-distill-agent');
|
|
760
|
+
return toTextResult(getSelfDistillStatus() || { message: 'No self-distill runs found.' });
|
|
761
|
+
}
|
|
762
|
+
case 'context_stuff_lessons': {
|
|
763
|
+
const { getAllLessonsForContext } = require('../../scripts/lesson-inference');
|
|
764
|
+
return toTextResult(getAllLessonsForContext({ maxTokenBudget: args.maxTokenBudget, signal: args.signal, format: args.format }));
|
|
765
|
+
}
|
|
683
766
|
default:
|
|
684
767
|
throw new Error(`Unsupported tool: ${name}`);
|
|
685
768
|
}
|
package/bin/cli.js
CHANGED
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
* npx thumbgate init --agent claude-code # scaffold + wire hooks for specific agent
|
|
9
9
|
* npx thumbgate gate-check # PreToolUse hook: pipe tool JSON via stdin, get verdict
|
|
10
10
|
* npx thumbgate capture # capture feedback
|
|
11
|
+
* npx thumbgate import-doc # import a local policy/runbook document and propose gates
|
|
11
12
|
* npx thumbgate export-dpo # export DPO training pairs
|
|
12
13
|
* npx thumbgate export-databricks # export Databricks-ready analytics bundle
|
|
13
14
|
* npx thumbgate stats # feedback analytics + Revenue-at-Risk
|
|
14
15
|
* npx thumbgate cfo # local operational billing summary
|
|
15
|
-
* npx thumbgate pro #
|
|
16
|
+
* npx thumbgate pro # solo dashboard + exports side lane
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
'use strict';
|
|
@@ -49,7 +50,9 @@ function upgradeNudge() {
|
|
|
49
50
|
if (isProTier()) return;
|
|
50
51
|
} catch (_) { return; }
|
|
51
52
|
process.stderr.write(
|
|
52
|
-
|
|
53
|
+
'\n Team rollout: start with the Workflow Hardening Sprint\n' +
|
|
54
|
+
' https://thumbgate-production.up.railway.app/#workflow-sprint-intake\n' +
|
|
55
|
+
`\n Solo side lane: Pro — ${PRO_PRICE_LABEL}\n` +
|
|
53
56
|
` ${PRO_CHECKOUT_URL}\n\n`
|
|
54
57
|
);
|
|
55
58
|
}
|
|
@@ -115,6 +118,7 @@ function telemetryPing(installId) {
|
|
|
115
118
|
const req = mod.request(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }, timeout: 3000 }, () => {});
|
|
116
119
|
req.on('error', () => {});
|
|
117
120
|
req.on('timeout', () => { req.destroy(); });
|
|
121
|
+
req.on('socket', (s) => s.unref()); // fire-and-forget: never block process exit
|
|
118
122
|
req.end(payload);
|
|
119
123
|
} catch (_) { /* telemetry is best-effort */ }
|
|
120
124
|
}
|
|
@@ -134,8 +138,8 @@ function proNudge(context) {
|
|
|
134
138
|
function limitNudge(action) {
|
|
135
139
|
if (process.env.THUMBGATE_NO_NUDGE === '1') return;
|
|
136
140
|
process.stderr.write(
|
|
137
|
-
`\n ⚠️ Free tier
|
|
138
|
-
` Upgrade to Pro for unlimited usage — ${PRO_PRICE_LABEL}:\n` +
|
|
141
|
+
`\n ⚠️ Free tier limit reached. Upgrade to Pro for unlimited: https://thumbgate-production.up.railway.app/pro\n` +
|
|
142
|
+
` ${action} daily limit reached. Upgrade to Pro for unlimited usage — ${PRO_PRICE_LABEL}:\n` +
|
|
139
143
|
` ${PRO_CHECKOUT_URL}\n\n`
|
|
140
144
|
);
|
|
141
145
|
}
|
|
@@ -455,8 +459,78 @@ function setupForge() {
|
|
|
455
459
|
return true;
|
|
456
460
|
}
|
|
457
461
|
|
|
458
|
-
function
|
|
459
|
-
|
|
462
|
+
function detectAgent(projectDir) {
|
|
463
|
+
if (fs.existsSync(path.join(projectDir, '.claude'))) return 'claude-code';
|
|
464
|
+
if (fs.existsSync(path.join(projectDir, '.cursorrules'))) return 'cursor';
|
|
465
|
+
if (fs.existsSync(path.join(projectDir, '.cursor'))) return 'cursor';
|
|
466
|
+
if (fs.existsSync(path.join(projectDir, '.codex'))) return 'codex';
|
|
467
|
+
if (fs.existsSync(path.join(projectDir, '.gemini'))) return 'gemini';
|
|
468
|
+
if (fs.existsSync(path.join(projectDir, '.amp'))) return 'amp';
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function quickStart() {
|
|
473
|
+
const qsArgs = parseArgs(process.argv.slice(3));
|
|
474
|
+
const projectDir = process.cwd();
|
|
475
|
+
const detectedAgent = detectAgent(projectDir);
|
|
476
|
+
const agent = qsArgs.agent || detectedAgent || 'claude-code';
|
|
477
|
+
const thumbgateDir = path.join(projectDir, '.thumbgate');
|
|
478
|
+
const configPath = path.join(thumbgateDir, 'config.json');
|
|
479
|
+
const agentSource = qsArgs.agent ? 'specified' : (detectedAgent ? 'auto-detected' : 'default');
|
|
480
|
+
|
|
481
|
+
console.log(`\nthumbgate quick-start v${pkgVersion()}`);
|
|
482
|
+
console.log(`Agent: ${agent} (${agentSource})`);
|
|
483
|
+
console.log('');
|
|
484
|
+
|
|
485
|
+
// 1. Run init with the resolved agent so hook wiring uses the same target.
|
|
486
|
+
init({ ...qsArgs, agent });
|
|
487
|
+
|
|
488
|
+
// 2. Copy default gates
|
|
489
|
+
const defaultGates = path.join(PKG_ROOT, 'config', 'gates', 'default.json');
|
|
490
|
+
const targetGates = path.join(thumbgateDir, 'gates.json');
|
|
491
|
+
if (fs.existsSync(defaultGates)) {
|
|
492
|
+
fs.mkdirSync(thumbgateDir, { recursive: true });
|
|
493
|
+
fs.copyFileSync(defaultGates, targetGates);
|
|
494
|
+
console.log(' Copied default gates to .thumbgate/gates.json');
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// 3. Write config
|
|
498
|
+
let baseConfig = {};
|
|
499
|
+
if (fs.existsSync(configPath)) {
|
|
500
|
+
try {
|
|
501
|
+
baseConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
502
|
+
} catch (_) {
|
|
503
|
+
baseConfig = {};
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const config = {
|
|
507
|
+
...baseConfig,
|
|
508
|
+
selfDistillation: true,
|
|
509
|
+
contextStuffing: true,
|
|
510
|
+
maxTokenBudget: 10000,
|
|
511
|
+
autoGatePromotion: true,
|
|
512
|
+
agent,
|
|
513
|
+
version: pkgVersion(),
|
|
514
|
+
installId: baseConfig.installId || require('crypto').randomBytes(8).toString('hex'),
|
|
515
|
+
quickStart: true,
|
|
516
|
+
createdAt: baseConfig.createdAt || new Date().toISOString(),
|
|
517
|
+
};
|
|
518
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
519
|
+
console.log(' Created .thumbgate/config.json');
|
|
520
|
+
|
|
521
|
+
console.log('\n Enforcement setup complete:');
|
|
522
|
+
console.log(' Self-distillation: ON (agent auto-learns from outcomes)');
|
|
523
|
+
console.log(' Context-stuffing: ON (all lessons injected at session start)');
|
|
524
|
+
console.log(' Auto-gate promotion: ON (recurring failures become hard blocks)');
|
|
525
|
+
console.log(' Default gates: ' + (fs.existsSync(targetGates) ? 'loaded' : 'not found'));
|
|
526
|
+
console.log('\n Next steps:');
|
|
527
|
+
console.log(' npx thumbgate capture --feedback=down --context="what failed"');
|
|
528
|
+
console.log(' npx thumbgate stats');
|
|
529
|
+
console.log('');
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function init(cliArgs = parseArgs(process.argv.slice(3))) {
|
|
533
|
+
const args = { ...cliArgs };
|
|
460
534
|
|
|
461
535
|
// --wire-hooks only mode: skip scaffolding, just wire hooks
|
|
462
536
|
if (args['wire-hooks']) {
|
|
@@ -580,13 +654,12 @@ function init() {
|
|
|
580
654
|
console.log('Run: npx thumbgate help');
|
|
581
655
|
trackEvent('cli_init', { command: 'init' });
|
|
582
656
|
proNudge();
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
);
|
|
657
|
+
console.log('');
|
|
658
|
+
console.log(' ┌──────────────────────────────────────────────────────────┐');
|
|
659
|
+
console.log(' │ Teams: shared enforcement, CI gates, audit trails │');
|
|
660
|
+
console.log(' │ One correction protects every agent on your team. │');
|
|
661
|
+
console.log(' │ https://thumbgate-production.up.railway.app/pro │');
|
|
662
|
+
console.log(' └──────────────────────────────────────────────────────────┘');
|
|
590
663
|
|
|
591
664
|
try {
|
|
592
665
|
const { appendFunnelEvent } = require(path.join(PKG_ROOT, 'scripts', 'billing'));
|
|
@@ -663,7 +736,7 @@ function capture() {
|
|
|
663
736
|
const pct = Math.round((capLimit.used / capLimit.limit) * 100);
|
|
664
737
|
console.log(` Usage : ${capLimit.used}/${capLimit.limit} captures today (${pct}%)`);
|
|
665
738
|
if (capLimit.remaining <= 1) {
|
|
666
|
-
console.log(` ⚠️
|
|
739
|
+
console.log(` ⚠️ Free tier limit reached. Upgrade to Pro for unlimited: https://thumbgate-production.up.railway.app/pro`);
|
|
667
740
|
}
|
|
668
741
|
}
|
|
669
742
|
console.log('');
|
|
@@ -696,8 +769,9 @@ function stats() {
|
|
|
696
769
|
console.log(` Repeated Failures detected: ${data.totalNegative}`);
|
|
697
770
|
console.log(` Estimated Operational Loss: $${revenueAtRisk}`);
|
|
698
771
|
console.log(' Action Required: Run "npx thumbgate rules" to generate guardrails.');
|
|
699
|
-
console.log(' Strategic Recommendation:
|
|
700
|
-
console.log('
|
|
772
|
+
console.log(' Strategic Recommendation: if this is a shared workflow problem, start the Workflow Hardening Sprint.');
|
|
773
|
+
console.log(' Team intake: https://thumbgate-production.up.railway.app/#workflow-sprint-intake');
|
|
774
|
+
console.log(' Solo side lane: npx thumbgate pro');
|
|
701
775
|
} else {
|
|
702
776
|
console.log('\n✅ System is currently high-reliability. No immediate revenue loss detected.');
|
|
703
777
|
}
|
|
@@ -804,11 +878,11 @@ function pro() {
|
|
|
804
878
|
const truthUrl = 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/COMMERCIAL_TRUTH.md';
|
|
805
879
|
console.log('\nThumbGate Pro — Local Dashboard');
|
|
806
880
|
console.log('─'.repeat(50));
|
|
807
|
-
console.log('Self-serve
|
|
881
|
+
console.log('Self-serve side lane today: Pro ($19/mo or $149/yr).');
|
|
808
882
|
console.log('Every licensed Pro user gets a personal local dashboard on localhost.');
|
|
809
883
|
console.log('\nWhat is available:');
|
|
810
884
|
console.log(' - Local Pro dashboard: your own browser dashboard for search, gates, and DPO export');
|
|
811
|
-
console.log(' -
|
|
885
|
+
console.log(' - Team rollout path: shared hosted lessons, org visibility, and workflow proof');
|
|
812
886
|
console.log(' - Commercial truth doc: source of truth for traction, pricing, and proof claims');
|
|
813
887
|
console.log('\nLinks:');
|
|
814
888
|
console.log(` Buy Pro : ${PRO_CHECKOUT_URL}`);
|
|
@@ -892,7 +966,7 @@ function pro() {
|
|
|
892
966
|
|
|
893
967
|
if (args.info) {
|
|
894
968
|
printProInfo();
|
|
895
|
-
|
|
969
|
+
process.exit(0);
|
|
896
970
|
}
|
|
897
971
|
|
|
898
972
|
const resolvedKey = resolveProKey();
|
|
@@ -901,6 +975,7 @@ function pro() {
|
|
|
901
975
|
}
|
|
902
976
|
|
|
903
977
|
printProInfo();
|
|
978
|
+
process.exit(0);
|
|
904
979
|
}
|
|
905
980
|
|
|
906
981
|
function summary() {
|
|
@@ -1037,6 +1112,67 @@ function exportDatabricks() {
|
|
|
1037
1112
|
}
|
|
1038
1113
|
}
|
|
1039
1114
|
|
|
1115
|
+
function importDoc() {
|
|
1116
|
+
syncActiveProjectContext();
|
|
1117
|
+
const args = parseArgs(process.argv.slice(3));
|
|
1118
|
+
const positionalFilePath = process.argv.slice(3).find((arg) => !arg.startsWith('--'));
|
|
1119
|
+
const filePath = positionalFilePath || args.file || args.path || null;
|
|
1120
|
+
const inlineContent = typeof args.content === 'string' && args.content.trim()
|
|
1121
|
+
? args.content
|
|
1122
|
+
: (!filePath ? readStdinText().trim() : '');
|
|
1123
|
+
const tags = String(args.tags || args.tag || '')
|
|
1124
|
+
.split(',')
|
|
1125
|
+
.map((tag) => tag.trim())
|
|
1126
|
+
.filter(Boolean);
|
|
1127
|
+
|
|
1128
|
+
if (!filePath && !inlineContent) {
|
|
1129
|
+
console.error('Error: import-doc requires a file path, --content, or stdin text');
|
|
1130
|
+
process.exit(1);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
try {
|
|
1134
|
+
const { importDocument: importDocumentLocal } = require(path.join(PKG_ROOT, 'scripts', 'document-intake'));
|
|
1135
|
+
const document = importDocumentLocal({
|
|
1136
|
+
filePath,
|
|
1137
|
+
content: inlineContent || null,
|
|
1138
|
+
title: args.title || null,
|
|
1139
|
+
sourceFormat: args.format || args['source-format'] || null,
|
|
1140
|
+
sourceUrl: args.url || args['source-url'] || null,
|
|
1141
|
+
tags,
|
|
1142
|
+
proposeGates: args['no-proposals'] ? false : args.proposeGates !== 'false',
|
|
1143
|
+
});
|
|
1144
|
+
trackEvent('cli_import_doc', {
|
|
1145
|
+
command: 'import-doc',
|
|
1146
|
+
documentId: document.documentId,
|
|
1147
|
+
sourceFormat: document.sourceFormat,
|
|
1148
|
+
proposalCount: Array.isArray(document.proposals) ? document.proposals.length : 0,
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
const payload = {
|
|
1152
|
+
ok: true,
|
|
1153
|
+
document,
|
|
1154
|
+
};
|
|
1155
|
+
if (args.json) {
|
|
1156
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
console.log(`Imported document: ${document.title}`);
|
|
1161
|
+
console.log(` ID: ${document.documentId}`);
|
|
1162
|
+
console.log(` Format: ${document.sourceFormat}`);
|
|
1163
|
+
console.log(` Proposals: ${Array.isArray(document.proposals) ? document.proposals.length : 0}`);
|
|
1164
|
+
if (Array.isArray(document.proposals) && document.proposals.length > 0) {
|
|
1165
|
+
console.log('\nProposed gates:');
|
|
1166
|
+
for (const proposal of document.proposals.slice(0, 6)) {
|
|
1167
|
+
console.log(` - [${proposal.type}] ${proposal.title} (${proposal.action}/${proposal.severity})`);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
} catch (err) {
|
|
1171
|
+
console.error(err && err.message ? err.message : err);
|
|
1172
|
+
process.exit(1);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1040
1176
|
function obsidianExport() {
|
|
1041
1177
|
const args = parseArgs(process.argv.slice(3));
|
|
1042
1178
|
const { exportAll } = require(path.join(PKG_ROOT, 'scripts', 'obsidian-export'));
|
|
@@ -1232,6 +1368,10 @@ function cacheUpdate() {
|
|
|
1232
1368
|
|
|
1233
1369
|
function statuslineRender() {
|
|
1234
1370
|
syncActiveProjectContext();
|
|
1371
|
+
try {
|
|
1372
|
+
const { syncClaudeHistoryFeedback } = require(path.join(PKG_ROOT, 'scripts', 'claude-feedback-sync'));
|
|
1373
|
+
syncClaudeHistoryFeedback();
|
|
1374
|
+
} catch (_) { /* best-effort fallback sync */ }
|
|
1235
1375
|
const payload = readStdinText();
|
|
1236
1376
|
const output = execFileSync('bash', [path.join(PKG_ROOT, 'scripts', 'statusline.sh')], {
|
|
1237
1377
|
encoding: 'utf8',
|
|
@@ -1283,9 +1423,27 @@ function hookAutoCapture() {
|
|
|
1283
1423
|
|
|
1284
1424
|
function sessionStart() {
|
|
1285
1425
|
syncActiveProjectContext();
|
|
1426
|
+
try {
|
|
1427
|
+
const { syncClaudeHistoryFeedback } = require(path.join(PKG_ROOT, 'scripts', 'claude-feedback-sync'));
|
|
1428
|
+
syncClaudeHistoryFeedback();
|
|
1429
|
+
} catch (_) { /* best-effort fallback sync */ }
|
|
1286
1430
|
const { analyzeFeedback } = require(path.join(PKG_ROOT, 'scripts', 'feedback-loop'));
|
|
1287
1431
|
const { refreshStatuslineCache } = require(path.join(PKG_ROOT, 'scripts', 'hook-thumbgate-cache-updater'));
|
|
1288
1432
|
refreshStatuslineCache(analyzeFeedback());
|
|
1433
|
+
|
|
1434
|
+
// Surface gate-program.md active rules so the agent starts aware of what is blocked.
|
|
1435
|
+
try {
|
|
1436
|
+
const { readGateProgram, extractBlockPatterns } = require(path.join(PKG_ROOT, 'scripts', 'meta-agent-loop'));
|
|
1437
|
+
const gateProgram = readGateProgram();
|
|
1438
|
+
if (gateProgram) {
|
|
1439
|
+
const blockPatterns = extractBlockPatterns(gateProgram);
|
|
1440
|
+
if (blockPatterns.length > 0) {
|
|
1441
|
+
process.stderr.write('\n[ThumbGate] Active hard-block rules from gate-program.md:\n');
|
|
1442
|
+
blockPatterns.forEach((p, i) => process.stderr.write(` ${i + 1}. ${p}\n`));
|
|
1443
|
+
process.stderr.write('\n');
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
} catch (_) { /* gate-program awareness is best-effort */ }
|
|
1289
1447
|
}
|
|
1290
1448
|
|
|
1291
1449
|
function installMcp() {
|
|
@@ -1356,6 +1514,7 @@ function help() {
|
|
|
1356
1514
|
console.log(' risk [flags] Train or query the boosted local risk scorer');
|
|
1357
1515
|
console.log(' doctor Audit runtime isolation, bootstrap context, and permission tier');
|
|
1358
1516
|
console.log(' dispatch Print a Dispatch-safe remote ops brief for phone-driven review sessions');
|
|
1517
|
+
console.log(' import-doc Import a local policy/runbook and propose reviewable gate candidates');
|
|
1359
1518
|
console.log(' export-dpo Export DPO training pairs (prompt/chosen/rejected JSONL)');
|
|
1360
1519
|
console.log(' export-databricks Export feedback logs + proof artifacts as a Databricks-ready analytics bundle');
|
|
1361
1520
|
console.log(' obsidian-export Export all feedback data as interlinked Obsidian markdown notes');
|
|
@@ -1364,6 +1523,9 @@ function help() {
|
|
|
1364
1523
|
console.log(' rules Generate prevention rules from repeated failures');
|
|
1365
1524
|
console.log(' optimize [PRO] Prune CLAUDE.md and migrate manual rules to Pre-Action Gates');
|
|
1366
1525
|
console.log(' force-gate <PATTERN> Immediately create a blocking gate from a pattern');
|
|
1526
|
+
console.log(' meta-agent Run meta-agent loop: generate + evaluate + promote prevention rules');
|
|
1527
|
+
console.log(' --dry-run Preview rules without writing');
|
|
1528
|
+
console.log(' --status Show last run summary');
|
|
1367
1529
|
console.log(' self-heal Run self-healing check and auto-fix');
|
|
1368
1530
|
console.log(' activate <KEY> Activate a Pro license key (from Stripe checkout)');
|
|
1369
1531
|
console.log(' pro Show Pro plan ($19/mo) + hosted pilot info');
|
|
@@ -1385,6 +1547,7 @@ function help() {
|
|
|
1385
1547
|
console.log(' npx thumbgate init');
|
|
1386
1548
|
console.log(' npx thumbgate stats');
|
|
1387
1549
|
console.log(' npx thumbgate cfo');
|
|
1550
|
+
console.log(' npx thumbgate import-doc docs/release-policy.md --json');
|
|
1388
1551
|
console.log(' npx thumbgate repair-github-marketplace --write');
|
|
1389
1552
|
console.log(' npx thumbgate lessons --query="verification" --limit=5');
|
|
1390
1553
|
console.log(' npx thumbgate model-fit');
|
|
@@ -1405,6 +1568,9 @@ switch (COMMAND) {
|
|
|
1405
1568
|
init();
|
|
1406
1569
|
upgradeNudge();
|
|
1407
1570
|
break;
|
|
1571
|
+
case 'quick-start':
|
|
1572
|
+
quickStart();
|
|
1573
|
+
break;
|
|
1408
1574
|
case 'install':
|
|
1409
1575
|
install();
|
|
1410
1576
|
break;
|
|
@@ -1550,6 +1716,10 @@ switch (COMMAND) {
|
|
|
1550
1716
|
case 'obsidian-export':
|
|
1551
1717
|
obsidianExport();
|
|
1552
1718
|
break;
|
|
1719
|
+
case 'import-doc':
|
|
1720
|
+
case 'import-document':
|
|
1721
|
+
importDoc();
|
|
1722
|
+
break;
|
|
1553
1723
|
case 'rules':
|
|
1554
1724
|
rules();
|
|
1555
1725
|
break;
|
|
@@ -1568,6 +1738,32 @@ switch (COMMAND) {
|
|
|
1568
1738
|
console.log(`Total auto-promoted gates: ${result.totalGates}`);
|
|
1569
1739
|
break;
|
|
1570
1740
|
}
|
|
1741
|
+
case 'meta-agent': {
|
|
1742
|
+
const metaArgs = parseArgs(process.argv.slice(3));
|
|
1743
|
+
if (metaArgs.status) {
|
|
1744
|
+
const { getMetaAgentStatus } = require(path.join(PKG_ROOT, 'scripts', 'meta-agent-loop'));
|
|
1745
|
+
const status = getMetaAgentStatus();
|
|
1746
|
+
if (!status) {
|
|
1747
|
+
console.log('No meta-agent runs recorded yet. Run: npx thumbgate meta-agent');
|
|
1748
|
+
} else {
|
|
1749
|
+
console.log(JSON.stringify(status, null, 2));
|
|
1750
|
+
}
|
|
1751
|
+
} else {
|
|
1752
|
+
const { runMetaAgentLoop } = require(path.join(PKG_ROOT, 'scripts', 'meta-agent-loop'));
|
|
1753
|
+
runMetaAgentLoop({ dryRun: Boolean(metaArgs['dry-run']), verbose: true })
|
|
1754
|
+
.then((manifest) => {
|
|
1755
|
+
console.log(`\nMeta-agent run complete.`);
|
|
1756
|
+
console.log(` Promoted : ${manifest.promotedCount} rule(s)`);
|
|
1757
|
+
console.log(` Reverted : ${manifest.revertedCount} candidate(s)`);
|
|
1758
|
+
if (manifest.dryRun) console.log(' [DRY RUN] No rules written.');
|
|
1759
|
+
})
|
|
1760
|
+
.catch((err) => {
|
|
1761
|
+
console.error('Meta-agent failed:', err.message);
|
|
1762
|
+
process.exit(1);
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
break;
|
|
1766
|
+
}
|
|
1571
1767
|
case 'self-heal':
|
|
1572
1768
|
selfHeal();
|
|
1573
1769
|
break;
|
package/bin/postinstall.js
CHANGED
|
@@ -15,7 +15,9 @@ if (isCI || isQuiet) process.exit(0);
|
|
|
15
15
|
const {
|
|
16
16
|
PRO_MONTHLY_PAYMENT_LINK,
|
|
17
17
|
PRO_PRICE_LABEL,
|
|
18
|
+
TEAM_PRICE_LABEL,
|
|
18
19
|
} = require('../scripts/commercial-offer');
|
|
20
|
+
const WORKFLOW_SPRINT_URL = 'https://thumbgate-production.up.railway.app/#workflow-sprint-intake';
|
|
19
21
|
|
|
20
22
|
process.stderr.write(`
|
|
21
23
|
┌─────────────────────────────────────────────────────┐
|
|
@@ -26,9 +28,13 @@ process.stderr.write(`
|
|
|
26
28
|
│ npx thumbgate init │
|
|
27
29
|
│ npx thumbgate stats │
|
|
28
30
|
│ │
|
|
29
|
-
│
|
|
30
|
-
│
|
|
31
|
+
│ Team rollout starts with the Workflow Hardening │
|
|
32
|
+
│ Sprint: ${WORKFLOW_SPRINT_URL} │
|
|
33
|
+
│ │
|
|
34
|
+
│ Solo side lane: Pro (personal local dashboard, │
|
|
35
|
+
│ DPO export) — ${PRO_PRICE_LABEL}: │
|
|
31
36
|
│ ${PRO_MONTHLY_PAYMENT_LINK} │
|
|
37
|
+
│ Team: ${TEAM_PRICE_LABEL} after intake. │
|
|
32
38
|
│ │
|
|
33
39
|
│ Or run: npx thumbgate pro │
|
|
34
40
|
│ │
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"max_actions": 2000,
|
|
3
|
+
"max_time_minutes": 600,
|
|
4
|
+
"profiles": {
|
|
5
|
+
"strict": {
|
|
6
|
+
"max_actions": 500,
|
|
7
|
+
"max_time_minutes": 150
|
|
8
|
+
},
|
|
9
|
+
"guided": {
|
|
10
|
+
"max_actions": 2000,
|
|
11
|
+
"max_time_minutes": 600
|
|
12
|
+
},
|
|
13
|
+
"autonomous": {
|
|
14
|
+
"max_actions": 5000,
|
|
15
|
+
"max_time_minutes": 1200
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"harness": "code-edit",
|
|
4
|
+
"description": "Specialized gates for code editing operations. Loaded when tool context involves Edit, Write, or MultiEdit tools.",
|
|
5
|
+
"gates": [
|
|
6
|
+
{
|
|
7
|
+
"id": "edit-env-direct",
|
|
8
|
+
"layer": "Execution",
|
|
9
|
+
"toolNames": ["Edit", "Write", "MultiEdit"],
|
|
10
|
+
"pattern": "\\.env$|\\.env\\.local$|\\.env\\.production$",
|
|
11
|
+
"action": "warn",
|
|
12
|
+
"severity": "high",
|
|
13
|
+
"message": "Editing a .env file directly. Ensure you are editing .env.example instead, and that no real secrets are committed."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "edit-lockfile-manual",
|
|
17
|
+
"layer": "Execution",
|
|
18
|
+
"toolNames": ["Edit", "Write"],
|
|
19
|
+
"pattern": "package-lock\\.json$|yarn\\.lock$|pnpm-lock\\.yaml$",
|
|
20
|
+
"action": "warn",
|
|
21
|
+
"severity": "medium",
|
|
22
|
+
"message": "Manually editing a lockfile is not recommended. Run npm install / yarn / pnpm install to regenerate it."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "edit-generated-file",
|
|
26
|
+
"layer": "Execution",
|
|
27
|
+
"toolNames": ["Edit", "Write"],
|
|
28
|
+
"pattern": "dist/|build/|\\.min\\.js$|\\.min\\.css$",
|
|
29
|
+
"action": "warn",
|
|
30
|
+
"severity": "medium",
|
|
31
|
+
"message": "Editing a generated/built file. Edit the source instead and rebuild."
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "edit-test-skip",
|
|
35
|
+
"layer": "Execution",
|
|
36
|
+
"toolNames": ["Edit", "Write", "MultiEdit"],
|
|
37
|
+
"pattern": "\\.skip\\(|test\\.skip|describe\\.skip|it\\.skip|xit\\(|xdescribe\\(",
|
|
38
|
+
"action": "warn",
|
|
39
|
+
"severity": "high",
|
|
40
|
+
"message": "Skipping a test. Only skip tests intentionally and document why — never skip to pass CI."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "edit-console-log-commit",
|
|
44
|
+
"layer": "Execution",
|
|
45
|
+
"toolNames": ["Edit", "Write"],
|
|
46
|
+
"pattern": "console\\.log\\(.*password|console\\.log\\(.*secret|console\\.log\\(.*token|console\\.log\\(.*api.?key",
|
|
47
|
+
"action": "block",
|
|
48
|
+
"severity": "critical",
|
|
49
|
+
"message": "Logging a secret value to console is blocked. Remove the log or redact the value."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "edit-version-file-without-sync",
|
|
53
|
+
"layer": "Execution",
|
|
54
|
+
"toolNames": ["Edit", "Write"],
|
|
55
|
+
"pattern": "\"version\"\\s*:\\s*\"",
|
|
56
|
+
"action": "warn",
|
|
57
|
+
"severity": "medium",
|
|
58
|
+
"message": "Editing a version field. Run node scripts/sync-version.js after changing package.json version to propagate to all targets."
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|