thumbgate 1.3.0 → 1.4.1
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 +25 -0
- 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 +242 -126
- package/adapters/README.md +1 -1
- package/adapters/chatgpt/INSTALL.md +59 -4
- 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 +84 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +204 -13
- 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/openapi/openapi.yaml +168 -0
- package/package.json +47 -11
- 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/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 +10 -2
- package/public/guide.html +2 -2
- 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 +172 -65
- package/public/lessons.html +33 -24
- package/public/llm-context.md +140 -0
- package/public/pro.html +24 -22
- 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 +1 -5
- package/scripts/auto-promote-gates.js +5 -3
- package/scripts/background-agent-governance.js +2 -10
- package/scripts/billing-setup.js +109 -0
- package/scripts/billing.js +2 -16
- package/scripts/budget-enforcer.js +173 -0
- package/scripts/build-claude-mcpb.js +71 -5
- package/scripts/build-codex-plugin.js +152 -0
- package/scripts/check-congruence.js +132 -14
- 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 +1 -21
- package/scripts/dashboard.js +20 -0
- package/scripts/decision-journal.js +341 -0
- package/scripts/delegation-runtime.js +1 -5
- package/scripts/distribution-surfaces.js +54 -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 +1 -5
- package/scripts/feedback-root-consolidator.js +2 -21
- package/scripts/feedback-session.js +49 -0
- package/scripts/feedback-to-rules.js +215 -36
- package/scripts/filesystem-search.js +1 -9
- package/scripts/fs-utils.js +104 -0
- package/scripts/gates-engine.js +200 -11
- 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-config.js +2 -0
- package/scripts/hosted-job-launcher.js +1 -5
- package/scripts/hybrid-feedback-context.js +33 -49
- package/scripts/intervention-policy.js +58 -1
- 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/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/money-watcher.js +1 -4
- package/scripts/obsidian-export.js +1 -5
- package/scripts/operational-integrity.js +15 -3
- package/scripts/operational-summary.js +41 -5
- 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 +1 -5
- package/scripts/prove-attribution.js +1 -5
- package/scripts/prove-automation.js +1 -3
- 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 +75 -9
- package/scripts/prove-predictive-insights.js +1 -3
- package/scripts/prove-training-export.js +1 -3
- package/scripts/prove-workflow-contract.js +1 -5
- package/scripts/ralph-loop.js +376 -0
- package/scripts/ralph-mode-ci.js +331 -0
- package/scripts/rate-limiter.js +3 -1
- package/scripts/reddit-dm-outreach.js +14 -4
- package/scripts/rotate-stripe-webhook-secret.js +314 -0
- 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.sh +29 -153
- 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 +106 -2
- package/scripts/vector-store.js +1 -5
- package/scripts/weekly-auto-post.js +1 -1
- package/scripts/workflow-sentinel.js +91 -0
- package/skills/thumbgate/SKILL.md +1 -1
- package/src/api/server.js +296 -7
- package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
- package/scripts/social-analytics/db/social-analytics.db-shm +0 -0
- /package/scripts/social-analytics/db/{social-analytics.db-wal → analytics.sqlite} +0 -0
|
@@ -814,6 +814,98 @@ paths:
|
|
|
814
814
|
description: Invalid dashboard render view or query
|
|
815
815
|
'401':
|
|
816
816
|
description: Unauthorized
|
|
817
|
+
/v1/decisions/evaluate:
|
|
818
|
+
post:
|
|
819
|
+
operationId: evaluateDecision
|
|
820
|
+
requestBody:
|
|
821
|
+
required: true
|
|
822
|
+
content:
|
|
823
|
+
application/json:
|
|
824
|
+
schema:
|
|
825
|
+
type: object
|
|
826
|
+
required: [toolName]
|
|
827
|
+
properties:
|
|
828
|
+
toolName:
|
|
829
|
+
type: string
|
|
830
|
+
command:
|
|
831
|
+
type: string
|
|
832
|
+
filePath:
|
|
833
|
+
type: string
|
|
834
|
+
changedFiles:
|
|
835
|
+
type: array
|
|
836
|
+
items:
|
|
837
|
+
type: string
|
|
838
|
+
repoPath:
|
|
839
|
+
type: string
|
|
840
|
+
baseBranch:
|
|
841
|
+
type: string
|
|
842
|
+
requirePrForReleaseSensitive:
|
|
843
|
+
type: boolean
|
|
844
|
+
requireVersionNotBehindBase:
|
|
845
|
+
type: boolean
|
|
846
|
+
responses:
|
|
847
|
+
'200':
|
|
848
|
+
description: Persisted workflow-sentinel recommendation with decision-control metadata and actionId
|
|
849
|
+
content:
|
|
850
|
+
application/json:
|
|
851
|
+
schema:
|
|
852
|
+
type: object
|
|
853
|
+
additionalProperties: true
|
|
854
|
+
'400':
|
|
855
|
+
description: Invalid decision evaluation request
|
|
856
|
+
'401':
|
|
857
|
+
description: Unauthorized
|
|
858
|
+
/v1/decisions/outcome:
|
|
859
|
+
post:
|
|
860
|
+
operationId: recordDecisionOutcome
|
|
861
|
+
requestBody:
|
|
862
|
+
required: true
|
|
863
|
+
content:
|
|
864
|
+
application/json:
|
|
865
|
+
schema:
|
|
866
|
+
type: object
|
|
867
|
+
required: [actionId, outcome]
|
|
868
|
+
properties:
|
|
869
|
+
actionId:
|
|
870
|
+
type: string
|
|
871
|
+
outcome:
|
|
872
|
+
type: string
|
|
873
|
+
actualDecision:
|
|
874
|
+
type: string
|
|
875
|
+
actor:
|
|
876
|
+
type: string
|
|
877
|
+
notes:
|
|
878
|
+
type: string
|
|
879
|
+
latencyMs:
|
|
880
|
+
type: number
|
|
881
|
+
metadata:
|
|
882
|
+
type: object
|
|
883
|
+
additionalProperties: true
|
|
884
|
+
responses:
|
|
885
|
+
'200':
|
|
886
|
+
description: Recorded a decision override, rollback, completion, or block outcome
|
|
887
|
+
content:
|
|
888
|
+
application/json:
|
|
889
|
+
schema:
|
|
890
|
+
type: object
|
|
891
|
+
additionalProperties: true
|
|
892
|
+
'400':
|
|
893
|
+
description: Invalid decision outcome request
|
|
894
|
+
'401':
|
|
895
|
+
description: Unauthorized
|
|
896
|
+
/v1/decisions/metrics:
|
|
897
|
+
get:
|
|
898
|
+
operationId: getDecisionMetrics
|
|
899
|
+
responses:
|
|
900
|
+
'200':
|
|
901
|
+
description: Decision-loop metrics derived from recorded evaluations and outcomes
|
|
902
|
+
content:
|
|
903
|
+
application/json:
|
|
904
|
+
schema:
|
|
905
|
+
type: object
|
|
906
|
+
additionalProperties: true
|
|
907
|
+
'401':
|
|
908
|
+
description: Unauthorized
|
|
817
909
|
/v1/settings/status:
|
|
818
910
|
get:
|
|
819
911
|
operationId: getSettingsStatus
|
|
@@ -1115,6 +1207,82 @@ paths:
|
|
|
1115
1207
|
description: DPO export accepted as a hosted background job
|
|
1116
1208
|
'401':
|
|
1117
1209
|
description: Unauthorized
|
|
1210
|
+
/v1/documents:
|
|
1211
|
+
get:
|
|
1212
|
+
operationId: listImportedDocuments
|
|
1213
|
+
parameters:
|
|
1214
|
+
- in: query
|
|
1215
|
+
name: query
|
|
1216
|
+
schema:
|
|
1217
|
+
type: string
|
|
1218
|
+
- in: query
|
|
1219
|
+
name: q
|
|
1220
|
+
schema:
|
|
1221
|
+
type: string
|
|
1222
|
+
- in: query
|
|
1223
|
+
name: tag
|
|
1224
|
+
schema:
|
|
1225
|
+
type: string
|
|
1226
|
+
- in: query
|
|
1227
|
+
name: limit
|
|
1228
|
+
schema:
|
|
1229
|
+
type: integer
|
|
1230
|
+
default: 20
|
|
1231
|
+
responses:
|
|
1232
|
+
'200':
|
|
1233
|
+
description: Imported policy and runbook documents
|
|
1234
|
+
'401':
|
|
1235
|
+
description: Unauthorized
|
|
1236
|
+
/v1/documents/import:
|
|
1237
|
+
post:
|
|
1238
|
+
operationId: importDocument
|
|
1239
|
+
requestBody:
|
|
1240
|
+
required: true
|
|
1241
|
+
content:
|
|
1242
|
+
application/json:
|
|
1243
|
+
schema:
|
|
1244
|
+
type: object
|
|
1245
|
+
properties:
|
|
1246
|
+
filePath:
|
|
1247
|
+
type: string
|
|
1248
|
+
content:
|
|
1249
|
+
type: string
|
|
1250
|
+
title:
|
|
1251
|
+
type: string
|
|
1252
|
+
sourceFormat:
|
|
1253
|
+
type: string
|
|
1254
|
+
enum: [markdown, text, yaml, json, html]
|
|
1255
|
+
sourceUrl:
|
|
1256
|
+
type: string
|
|
1257
|
+
tags:
|
|
1258
|
+
type: array
|
|
1259
|
+
items:
|
|
1260
|
+
type: string
|
|
1261
|
+
proposeGates:
|
|
1262
|
+
type: boolean
|
|
1263
|
+
responses:
|
|
1264
|
+
'201':
|
|
1265
|
+
description: Document imported
|
|
1266
|
+
'400':
|
|
1267
|
+
description: Invalid document import request
|
|
1268
|
+
'401':
|
|
1269
|
+
description: Unauthorized
|
|
1270
|
+
/v1/documents/{documentId}:
|
|
1271
|
+
get:
|
|
1272
|
+
operationId: getImportedDocument
|
|
1273
|
+
parameters:
|
|
1274
|
+
- in: path
|
|
1275
|
+
name: documentId
|
|
1276
|
+
required: true
|
|
1277
|
+
schema:
|
|
1278
|
+
type: string
|
|
1279
|
+
responses:
|
|
1280
|
+
'200':
|
|
1281
|
+
description: Imported document with proposed gates
|
|
1282
|
+
'401':
|
|
1283
|
+
description: Unauthorized
|
|
1284
|
+
'404':
|
|
1285
|
+
description: Imported document not found
|
|
1118
1286
|
/v1/jobs:
|
|
1119
1287
|
get:
|
|
1120
1288
|
operationId: listHostedJobs
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"thumbgate": {
|
|
4
4
|
"command": "npx",
|
|
5
|
-
"args": ["--yes", "--package", "thumbgate@1.
|
|
5
|
+
"args": ["--yes", "--package", "thumbgate@1.4.0", "thumbgate", "serve"]
|
|
6
6
|
}
|
|
7
7
|
},
|
|
8
8
|
"hooks": {
|
|
9
9
|
"preToolUse": {
|
|
10
10
|
"command": "npx",
|
|
11
|
-
"args": ["--yes", "--package", "thumbgate@1.
|
|
11
|
+
"args": ["--yes", "--package", "thumbgate@1.4.0", "thumbgate", "gate-check"]
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Codex MCP profile (copy into ~/.codex/config.toml or merge section)
|
|
2
2
|
[mcp_servers.thumbgate]
|
|
3
3
|
command = "npx"
|
|
4
|
-
args = ["--yes", "--package", "thumbgate@1.
|
|
4
|
+
args = ["--yes", "--package", "thumbgate@1.4.0", "thumbgate", "serve"]
|
|
5
5
|
|
|
6
6
|
# Hard PreToolUse hook for Codex
|
|
7
7
|
[hooks.pre_tool_use]
|
|
8
8
|
command = "npx"
|
|
9
|
-
args = ["--yes", "--package", "thumbgate@1.
|
|
9
|
+
args = ["--yes", "--package", "thumbgate@1.4.0", "thumbgate", "gate-check"]
|
|
@@ -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 {
|
|
@@ -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
|
}
|
|
@@ -456,8 +459,78 @@ function setupForge() {
|
|
|
456
459
|
return true;
|
|
457
460
|
}
|
|
458
461
|
|
|
459
|
-
function
|
|
460
|
-
|
|
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 };
|
|
461
534
|
|
|
462
535
|
// --wire-hooks only mode: skip scaffolding, just wire hooks
|
|
463
536
|
if (args['wire-hooks']) {
|
|
@@ -582,11 +655,11 @@ function init() {
|
|
|
582
655
|
trackEvent('cli_init', { command: 'init' });
|
|
583
656
|
proNudge();
|
|
584
657
|
console.log('');
|
|
585
|
-
console.log('
|
|
586
|
-
console.log(' │
|
|
587
|
-
console.log(' │
|
|
588
|
-
console.log(' │
|
|
589
|
-
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'));
|
|
@@ -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}`);
|
|
@@ -1038,6 +1112,67 @@ function exportDatabricks() {
|
|
|
1038
1112
|
}
|
|
1039
1113
|
}
|
|
1040
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
|
+
|
|
1041
1176
|
function obsidianExport() {
|
|
1042
1177
|
const args = parseArgs(process.argv.slice(3));
|
|
1043
1178
|
const { exportAll } = require(path.join(PKG_ROOT, 'scripts', 'obsidian-export'));
|
|
@@ -1295,6 +1430,20 @@ function sessionStart() {
|
|
|
1295
1430
|
const { analyzeFeedback } = require(path.join(PKG_ROOT, 'scripts', 'feedback-loop'));
|
|
1296
1431
|
const { refreshStatuslineCache } = require(path.join(PKG_ROOT, 'scripts', 'hook-thumbgate-cache-updater'));
|
|
1297
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 */ }
|
|
1298
1447
|
}
|
|
1299
1448
|
|
|
1300
1449
|
function installMcp() {
|
|
@@ -1357,6 +1506,7 @@ function help() {
|
|
|
1357
1506
|
console.log(' capture [flags] Capture feedback (--feedback=up|down --context="..." --tags="...")');
|
|
1358
1507
|
console.log(' stats Show feedback analytics + Revenue-at-Risk');
|
|
1359
1508
|
console.log(' cfo Show hosted billing summary when configured, else local fallback JSON');
|
|
1509
|
+
console.log(' billing:setup Generate operator key + print Railway setup instructions');
|
|
1360
1510
|
console.log(' repair-github-marketplace Dry-run or apply legacy GitHub Marketplace amount repairs (--write)');
|
|
1361
1511
|
console.log(' north-star Show proof-backed workflow-run progress toward the North Star');
|
|
1362
1512
|
console.log(' summary Human-readable feedback summary');
|
|
@@ -1365,6 +1515,7 @@ function help() {
|
|
|
1365
1515
|
console.log(' risk [flags] Train or query the boosted local risk scorer');
|
|
1366
1516
|
console.log(' doctor Audit runtime isolation, bootstrap context, and permission tier');
|
|
1367
1517
|
console.log(' dispatch Print a Dispatch-safe remote ops brief for phone-driven review sessions');
|
|
1518
|
+
console.log(' import-doc Import a local policy/runbook and propose reviewable gate candidates');
|
|
1368
1519
|
console.log(' export-dpo Export DPO training pairs (prompt/chosen/rejected JSONL)');
|
|
1369
1520
|
console.log(' export-databricks Export feedback logs + proof artifacts as a Databricks-ready analytics bundle');
|
|
1370
1521
|
console.log(' obsidian-export Export all feedback data as interlinked Obsidian markdown notes');
|
|
@@ -1373,6 +1524,9 @@ function help() {
|
|
|
1373
1524
|
console.log(' rules Generate prevention rules from repeated failures');
|
|
1374
1525
|
console.log(' optimize [PRO] Prune CLAUDE.md and migrate manual rules to Pre-Action Gates');
|
|
1375
1526
|
console.log(' force-gate <PATTERN> Immediately create a blocking gate from a pattern');
|
|
1527
|
+
console.log(' meta-agent Run meta-agent loop: generate + evaluate + promote prevention rules');
|
|
1528
|
+
console.log(' --dry-run Preview rules without writing');
|
|
1529
|
+
console.log(' --status Show last run summary');
|
|
1376
1530
|
console.log(' self-heal Run self-healing check and auto-fix');
|
|
1377
1531
|
console.log(' activate <KEY> Activate a Pro license key (from Stripe checkout)');
|
|
1378
1532
|
console.log(' pro Show Pro plan ($19/mo) + hosted pilot info');
|
|
@@ -1394,6 +1548,7 @@ function help() {
|
|
|
1394
1548
|
console.log(' npx thumbgate init');
|
|
1395
1549
|
console.log(' npx thumbgate stats');
|
|
1396
1550
|
console.log(' npx thumbgate cfo');
|
|
1551
|
+
console.log(' npx thumbgate import-doc docs/release-policy.md --json');
|
|
1397
1552
|
console.log(' npx thumbgate repair-github-marketplace --write');
|
|
1398
1553
|
console.log(' npx thumbgate lessons --query="verification" --limit=5');
|
|
1399
1554
|
console.log(' npx thumbgate model-fit');
|
|
@@ -1414,6 +1569,9 @@ switch (COMMAND) {
|
|
|
1414
1569
|
init();
|
|
1415
1570
|
upgradeNudge();
|
|
1416
1571
|
break;
|
|
1572
|
+
case 'quick-start':
|
|
1573
|
+
quickStart();
|
|
1574
|
+
break;
|
|
1417
1575
|
case 'install':
|
|
1418
1576
|
install();
|
|
1419
1577
|
break;
|
|
@@ -1455,6 +1613,9 @@ switch (COMMAND) {
|
|
|
1455
1613
|
case 'revenue':
|
|
1456
1614
|
cfo();
|
|
1457
1615
|
break;
|
|
1616
|
+
case 'billing:setup':
|
|
1617
|
+
require(path.join(PKG_ROOT, 'scripts', 'billing-setup'));
|
|
1618
|
+
break;
|
|
1458
1619
|
case 'repair-github-marketplace':
|
|
1459
1620
|
repairGithubMarketplace();
|
|
1460
1621
|
break;
|
|
@@ -1559,6 +1720,10 @@ switch (COMMAND) {
|
|
|
1559
1720
|
case 'obsidian-export':
|
|
1560
1721
|
obsidianExport();
|
|
1561
1722
|
break;
|
|
1723
|
+
case 'import-doc':
|
|
1724
|
+
case 'import-document':
|
|
1725
|
+
importDoc();
|
|
1726
|
+
break;
|
|
1562
1727
|
case 'rules':
|
|
1563
1728
|
rules();
|
|
1564
1729
|
break;
|
|
@@ -1577,6 +1742,32 @@ switch (COMMAND) {
|
|
|
1577
1742
|
console.log(`Total auto-promoted gates: ${result.totalGates}`);
|
|
1578
1743
|
break;
|
|
1579
1744
|
}
|
|
1745
|
+
case 'meta-agent': {
|
|
1746
|
+
const metaArgs = parseArgs(process.argv.slice(3));
|
|
1747
|
+
if (metaArgs.status) {
|
|
1748
|
+
const { getMetaAgentStatus } = require(path.join(PKG_ROOT, 'scripts', 'meta-agent-loop'));
|
|
1749
|
+
const status = getMetaAgentStatus();
|
|
1750
|
+
if (!status) {
|
|
1751
|
+
console.log('No meta-agent runs recorded yet. Run: npx thumbgate meta-agent');
|
|
1752
|
+
} else {
|
|
1753
|
+
console.log(JSON.stringify(status, null, 2));
|
|
1754
|
+
}
|
|
1755
|
+
} else {
|
|
1756
|
+
const { runMetaAgentLoop } = require(path.join(PKG_ROOT, 'scripts', 'meta-agent-loop'));
|
|
1757
|
+
runMetaAgentLoop({ dryRun: Boolean(metaArgs['dry-run']), verbose: true })
|
|
1758
|
+
.then((manifest) => {
|
|
1759
|
+
console.log(`\nMeta-agent run complete.`);
|
|
1760
|
+
console.log(` Promoted : ${manifest.promotedCount} rule(s)`);
|
|
1761
|
+
console.log(` Reverted : ${manifest.revertedCount} candidate(s)`);
|
|
1762
|
+
if (manifest.dryRun) console.log(' [DRY RUN] No rules written.');
|
|
1763
|
+
})
|
|
1764
|
+
.catch((err) => {
|
|
1765
|
+
console.error('Meta-agent failed:', err.message);
|
|
1766
|
+
process.exit(1);
|
|
1767
|
+
});
|
|
1768
|
+
}
|
|
1769
|
+
break;
|
|
1770
|
+
}
|
|
1580
1771
|
case 'self-heal':
|
|
1581
1772
|
selfHeal();
|
|
1582
1773
|
break;
|