thumbgate 0.9.14 → 1.0.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/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +1 -0
- package/adapters/README.md +1 -1
- package/adapters/chatgpt/openapi.yaml +105 -0
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/codex/config.toml +2 -2
- package/adapters/forge/forge.yaml +28 -0
- package/adapters/mcp/server-stdio.js +32 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +18 -3
- package/config/mcp-allowlists.json +10 -0
- package/openapi/openapi.yaml +105 -0
- package/package.json +4 -4
- package/plugins/amp-skill/INSTALL.md +3 -4
- package/plugins/amp-skill/SKILL.md +0 -1
- package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-codex-bridge/.mcp.json +1 -1
- package/plugins/claude-skill/INSTALL.md +1 -2
- package/plugins/codex-profile/.codex-plugin/plugin.json +1 -1
- package/plugins/codex-profile/.mcp.json +1 -1
- package/plugins/codex-profile/INSTALL.md +1 -1
- package/plugins/codex-profile/README.md +1 -1
- package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +1 -1
- package/plugins/opencode-profile/INSTALL.md +1 -1
- package/public/blog.html +1 -0
- package/public/dashboard.html +1 -1
- package/public/guide.html +1 -1
- package/public/index.html +3 -3
- package/public/learn/agent-harness-pattern.html +1 -1
- package/public/learn/ai-agent-persistent-memory.html +1 -1
- package/public/learn/mcp-pre-action-gates-explained.html +1 -1
- package/public/learn/stop-ai-agent-force-push.html +1 -1
- package/public/learn/vibe-coding-safety-net.html +1 -1
- package/public/learn.html +1 -1
- package/public/lessons.html +1 -1
- package/public/pro.html +1 -1
- package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
- package/scripts/agent-security-hardening.js +4 -4
- package/scripts/async-job-runner.js +84 -24
- package/scripts/auto-wire-hooks.js +59 -1
- package/scripts/context-manager.js +330 -0
- package/scripts/dashboard.js +1 -1
- package/scripts/distribution-surfaces.js +12 -0
- package/scripts/ensure-repo-bootstrap.js +15 -14
- package/scripts/gates-engine.js +96 -10
- package/scripts/hook-auto-capture.sh +1 -1
- package/scripts/hosted-job-launcher.js +260 -0
- package/scripts/managed-dpo-export.js +91 -0
- package/scripts/obsidian-export.js +0 -1
- package/scripts/operational-integrity.js +50 -7
- package/scripts/prove-lancedb.js +62 -4
- package/scripts/publish-decision.js +16 -0
- package/scripts/self-healing-check.js +6 -1
- package/scripts/social-analytics/load-env.js +33 -2
- package/scripts/social-analytics/store.js +200 -2
- package/scripts/sync-version.js +18 -11
- package/scripts/tool-registry.js +37 -0
- package/scripts/train_from_feedback.py +0 -4
- package/scripts/workflow-sentinel.js +793 -0
- package/src/api/server.js +205 -27
- /package/scripts/{rlhf_session_start.sh → thumbgate_session_start.sh} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-profile",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "ThumbGate for Codex: pre-action gates, skill packs, hallucination detection, PII scanning, progressive disclosure (82% token savings), and MCP-backed reliability memory.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Igor Ganapolsky",
|
|
@@ -31,7 +31,7 @@ The following block is appended to `~/.codex/config.toml`:
|
|
|
31
31
|
```toml
|
|
32
32
|
[mcp_servers.thumbgate]
|
|
33
33
|
command = "npx"
|
|
34
|
-
args = ["--yes", "--package", "thumbgate@0.
|
|
34
|
+
args = ["--yes", "--package", "thumbgate@1.0.0", "thumbgate", "serve"]
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
The repo-local Codex app plugin ships the same runtime path through `plugins/codex-profile/.mcp.json`, so the manual config and plugin metadata stay aligned.
|
|
@@ -29,7 +29,7 @@ That profile launches:
|
|
|
29
29
|
```toml
|
|
30
30
|
[mcp_servers.thumbgate]
|
|
31
31
|
command = "npx"
|
|
32
|
-
args = ["--yes", "--package", "thumbgate@0.
|
|
32
|
+
args = ["--yes", "--package", "thumbgate@1.0.0", "thumbgate", "serve"]
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
## Why this exists
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "thumbgate",
|
|
3
3
|
"displayName": "ThumbGate",
|
|
4
4
|
"description": "👍👎 Thumbs down a mistake — your AI agent won't repeat it. Thumbs up good work — it remembers the pattern.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "1.0.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Igor Ganapolsky"
|
|
8
8
|
},
|
|
@@ -25,7 +25,7 @@ The portable profile adds this MCP server entry:
|
|
|
25
25
|
"mcp": {
|
|
26
26
|
"thumbgate": {
|
|
27
27
|
"type": "local",
|
|
28
|
-
"command": ["npx", "--yes", "--package", "thumbgate@0.
|
|
28
|
+
"command": ["npx", "--yes", "--package", "thumbgate@1.0.0", "thumbgate", "serve"],
|
|
29
29
|
"enabled": true
|
|
30
30
|
}
|
|
31
31
|
}
|
package/public/blog.html
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>ThumbGate Blog — Agent Governance Engineering</title>
|
|
7
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
7
8
|
<meta
|
|
8
9
|
name="description"
|
|
9
10
|
content="Technical breakdowns, release notes, and agent governance insights from the ThumbGate team."
|
package/public/dashboard.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="canonical" href="https://thumbgate-production.up.railway.app/dashboard">
|
|
9
9
|
<meta name="robots" content="noindex">
|
|
10
10
|
<!-- Privacy-friendly analytics by Plausible -->
|
|
11
|
-
<script defer data-domain="thumbgate-production.up.railway.app"
|
|
11
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
12
12
|
<style>
|
|
13
13
|
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
14
14
|
:root {
|
package/public/guide.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>How to Stop AI Coding Agents From Repeating Mistakes — ThumbGate Guide</title>
|
|
7
7
|
<!-- Privacy-friendly analytics by Plausible -->
|
|
8
|
-
<script defer data-domain="thumbgate-production.up.railway.app"
|
|
8
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
9
9
|
<meta name="description" content="The complete guide to preventing AI coding agent mistakes with pre-action gates, history-aware lesson distillation, and automatic prevention rules.">
|
|
10
10
|
<meta name="keywords" content="AI agent mistakes, Claude Code force push, AI coding agent memory, MCP server guardrails, pre-action gates, vibe coding safety, PreToolUse hooks, ThumbGate, SpecLock alternative, Mem0 alternative">
|
|
11
11
|
<meta property="og:title" content="How to Stop AI Coding Agents From Repeating Mistakes">
|
package/public/index.html
CHANGED
|
@@ -27,7 +27,7 @@ __GOOGLE_SITE_VERIFICATION_META__
|
|
|
27
27
|
<meta name="keywords" content="ThumbGate, thumbgate, self-improving AI agents, AI agent self-improvement, AI agent learning, AI agent memory, pre-action gates, human-in-the-loop, MCP server, Claude Code, Cursor, Codex, Gemini, Amp, OpenCode, vibe coding safety, SpecLock alternative, Mem0 alternative, AI coding agent feedback loop, PreToolUse hooks, prevention rules, feedback enforcement, context engineering">
|
|
28
28
|
|
|
29
29
|
<!-- Privacy-friendly analytics by Plausible -->
|
|
30
|
-
<script defer data-domain="thumbgate-production.up.railway.app"
|
|
30
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
31
31
|
__GA_BOOTSTRAP__
|
|
32
32
|
|
|
33
33
|
<script>
|
|
@@ -578,7 +578,7 @@ __GA_BOOTSTRAP__
|
|
|
578
578
|
<!-- HOW IT WORKS -->
|
|
579
579
|
<section class="how-it-works" id="how-it-works">
|
|
580
580
|
<div class="container">
|
|
581
|
-
<div class="section-label">New in
|
|
581
|
+
<div class="section-label">New in v1.0.0</div>
|
|
582
582
|
<h2 class="section-title">Three steps to stop repeated AI failures</h2>
|
|
583
583
|
<div class="steps">
|
|
584
584
|
<div class="step">
|
|
@@ -835,7 +835,7 @@ __GA_BOOTSTRAP__
|
|
|
835
835
|
<a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
|
|
836
836
|
<a href="/blog">Blog</a>
|
|
837
837
|
</div>
|
|
838
|
-
<span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License ·
|
|
838
|
+
<span class="footer-copy">© 2026 Max Smith KDP LLC · MIT License · v1.0.0</span>
|
|
839
839
|
</div>
|
|
840
840
|
</footer>
|
|
841
841
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>The Agent Harness Pattern: Why Your AI Needs a Seatbelt — ThumbGate</title>
|
|
7
|
-
<script defer data-domain="thumbgate-production.up.railway.app"
|
|
7
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
8
8
|
<meta name="description" content="Tsinghua researchers formalized agent harnesses as first-class objects with contracts, verification gates, and durable state. ThumbGate implements this pattern today.">
|
|
9
9
|
<meta name="keywords" content="agent harness pattern, natural language agent harness, NLAH, AI agent safety, pre-action gates, verification gates, agent contracts, ThumbGate, MCP hooks">
|
|
10
10
|
<meta property="og:title" content="The Agent Harness Pattern: Why Your AI Needs a Seatbelt">
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>How to Give Your AI Coding Agent Persistent Memory Across Sessions — ThumbGate</title>
|
|
7
|
-
<script defer data-domain="thumbgate-production.up.railway.app"
|
|
7
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
8
8
|
<meta name="description" content="AI coding agents forget everything when a session ends. Learn how to give Claude Code, Cursor, Codex, and Gemini persistent memory using an MCP memory server that survives restarts.">
|
|
9
9
|
<meta name="keywords" content="ai agent memory, persistent memory, claude code memory, cursor agent memory, MCP memory server, session persistence, agent context, episodic memory, semantic memory">
|
|
10
10
|
<meta property="og:title" content="How to Give Your AI Coding Agent Persistent Memory Across Sessions">
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>MCP Pre-Action Gates Explained — ThumbGate</title>
|
|
7
|
-
<script defer data-domain="thumbgate-production.up.railway.app"
|
|
7
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
8
8
|
<meta name="description" content="What pre-action gates are, how they work in the Model Context Protocol, and why enforcement beats prompt rules for AI coding agent safety.">
|
|
9
9
|
<meta name="keywords" content="MCP pre-action gates, PreToolUse hooks, Model Context Protocol, AI agent enforcement, Claude Code hooks, MCP server guardrails, tool call interception, ThumbGate">
|
|
10
10
|
<meta property="og:title" content="MCP Pre-Action Gates Explained">
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>How to Stop AI Agents From Force-Pushing to Main — ThumbGate</title>
|
|
7
|
-
<script defer data-domain="thumbgate-production.up.railway.app"
|
|
7
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
8
8
|
<meta name="description" content="Your AI coding agent just force-pushed to main. Again. Here is how to make that physically impossible with a pre-action gate in two minutes.">
|
|
9
9
|
<meta name="keywords" content="AI agent force push, Claude Code force push prevention, git push force main, AI coding agent git safety, pre-action gates, ThumbGate">
|
|
10
10
|
<meta property="og:title" content="How to Stop AI Agents From Force-Pushing to Main">
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>The Vibe Coding Safety Net You Are Missing — ThumbGate</title>
|
|
7
|
-
<script defer data-domain="thumbgate-production.up.railway.app"
|
|
7
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
8
8
|
<meta name="description" content="Vibe coding is fast until your AI agent deletes a production table or rewrites a file you did not ask it to touch. Add guardrails without slowing down.">
|
|
9
9
|
<meta name="keywords" content="vibe coding safety, vibe coding guardrails, AI coding mistakes, Claude Code safety net, Cursor agent guardrails, AI agent enforcement, ThumbGate">
|
|
10
10
|
<meta property="og:title" content="The Vibe Coding Safety Net You Are Missing">
|
package/public/learn.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Learn — AI Agent Safety, Pre-Action Gates, and Vibe Coding Guardrails</title>
|
|
7
|
-
<script defer data-domain="thumbgate-production.up.railway.app"
|
|
7
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
8
8
|
<meta name="description" content="Practical guides for stopping AI coding agent mistakes. Learn about pre-action gates, MCP guardrails, feedback-driven enforcement, and vibe coding safety for Claude Code, Cursor, Codex, and more.">
|
|
9
9
|
<meta name="keywords" content="AI agent safety, pre-action gates, vibe coding guardrails, Claude Code mistakes, Cursor agent memory, MCP server hooks, AI coding agent feedback, ThumbGate guides">
|
|
10
10
|
<meta property="og:title" content="Learn — AI Agent Safety Guides by ThumbGate">
|
package/public/lessons.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>ThumbGate — Lessons Learned</title>
|
|
7
|
-
<script defer data-domain="thumbgate-production.up.railway.app"
|
|
7
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
8
8
|
<style>
|
|
9
9
|
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
10
10
|
:root {
|
package/public/pro.html
CHANGED
|
@@ -13,7 +13,7 @@ __GOOGLE_SITE_VERIFICATION_META__
|
|
|
13
13
|
<link rel="canonical" href="__APP_ORIGIN__/pro">
|
|
14
14
|
<meta name="keywords" content="ThumbGate Pro, AI agent reliability, pre-action gates, DPO export, local dashboard, review-ready evidence, Claude Code reliability, Codex reliability, Cursor reliability">
|
|
15
15
|
|
|
16
|
-
<script defer data-domain="thumbgate-production.up.railway.app"
|
|
16
|
+
<script defer data-domain="thumbgate-production.up.railway.app" src="https://plausible.io/js/script.js"></script>
|
|
17
17
|
__GA_BOOTSTRAP__
|
|
18
18
|
|
|
19
19
|
<script>
|
|
Binary file
|
|
@@ -84,10 +84,10 @@ function getCredentialAudit({ periodHours = 24 } = {}) {
|
|
|
84
84
|
|
|
85
85
|
// MCP profile tool allowlists (loaded from config or defaults)
|
|
86
86
|
const PROFILE_ALLOWLISTS = {
|
|
87
|
-
essential: new Set(['capture_feedback', 'recall', 'search_lessons', 'search_thumbgate', 'prevention_rules', 'enforcement_matrix', 'feedback_stats', 'estimate_uncertainty', 'org_dashboard', 'set_task_scope', 'get_scope_state', 'set_branch_governance', 'get_branch_governance', 'approve_protected_action', 'check_operational_integrity']),
|
|
88
|
-
readonly: new Set(['recall', 'feedback_summary', 'search_lessons', 'verify_claim', 'gate_stats', 'search_thumbgate', 'feedback_stats', 'estimate_uncertainty', 'org_dashboard', 'get_scope_state', 'get_branch_governance', 'check_operational_integrity']),
|
|
89
|
-
locked: new Set(['feedback_summary', 'search_lessons', 'diagnose_failure', 'list_intents', 'plan_intent', 'list_harnesses', 'verify_claim', 'get_scope_state', 'get_branch_governance', 'check_operational_integrity']),
|
|
90
|
-
commerce: new Set(['capture_feedback', 'recall', 'search_thumbgate', 'commerce_recall', 'track_action', 'verify_claim', 'feedback_stats', 'set_task_scope', 'get_scope_state', 'set_branch_governance', 'get_branch_governance', 'approve_protected_action', 'check_operational_integrity']),
|
|
87
|
+
essential: new Set(['capture_feedback', 'recall', 'search_lessons', 'search_thumbgate', 'prevention_rules', 'enforcement_matrix', 'feedback_stats', 'estimate_uncertainty', 'org_dashboard', 'set_task_scope', 'get_scope_state', 'set_branch_governance', 'get_branch_governance', 'approve_protected_action', 'check_operational_integrity', 'workflow_sentinel']),
|
|
88
|
+
readonly: new Set(['recall', 'feedback_summary', 'search_lessons', 'verify_claim', 'gate_stats', 'search_thumbgate', 'feedback_stats', 'estimate_uncertainty', 'org_dashboard', 'get_scope_state', 'get_branch_governance', 'check_operational_integrity', 'workflow_sentinel']),
|
|
89
|
+
locked: new Set(['feedback_summary', 'search_lessons', 'diagnose_failure', 'list_intents', 'plan_intent', 'list_harnesses', 'verify_claim', 'get_scope_state', 'get_branch_governance', 'check_operational_integrity', 'workflow_sentinel']),
|
|
90
|
+
commerce: new Set(['capture_feedback', 'recall', 'search_thumbgate', 'commerce_recall', 'track_action', 'verify_claim', 'feedback_stats', 'set_task_scope', 'get_scope_state', 'set_branch_governance', 'get_branch_governance', 'approve_protected_action', 'check_operational_integrity', 'workflow_sentinel']),
|
|
91
91
|
};
|
|
92
92
|
|
|
93
93
|
/**
|
|
@@ -132,11 +132,54 @@ function serializeJobForState(job) {
|
|
|
132
132
|
skill: job.skill || null,
|
|
133
133
|
partnerProfile: job.partnerProfile || null,
|
|
134
134
|
autoImprove: job.autoImprove !== false,
|
|
135
|
+
verificationMode: job.verificationMode === 'none' ? 'none' : 'standard',
|
|
136
|
+
recordFeedback: job.recordFeedback !== false,
|
|
135
137
|
jobFilePath: job.jobFilePath || null,
|
|
136
138
|
stages,
|
|
137
139
|
};
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
function queueJob(job) {
|
|
143
|
+
const normalizedJob = {
|
|
144
|
+
...job,
|
|
145
|
+
id: job.id || generateJobId(),
|
|
146
|
+
tags: Array.isArray(job.tags) ? job.tags : [],
|
|
147
|
+
autoImprove: job.autoImprove !== false,
|
|
148
|
+
verificationMode: job.verificationMode === 'none' ? 'none' : 'standard',
|
|
149
|
+
recordFeedback: job.recordFeedback !== false,
|
|
150
|
+
stages: normalizeStages(job),
|
|
151
|
+
};
|
|
152
|
+
const previousState = readJobState(normalizedJob.id);
|
|
153
|
+
const currentStage = normalizedJob.stages[0] ? normalizedJob.stages[0].name : null;
|
|
154
|
+
return writeJobState({
|
|
155
|
+
jobId: normalizedJob.id,
|
|
156
|
+
status: 'queued',
|
|
157
|
+
createdAt: previousState && previousState.createdAt ? previousState.createdAt : nowIso(),
|
|
158
|
+
startedAt: previousState && previousState.startedAt ? previousState.startedAt : null,
|
|
159
|
+
resumedAt: null,
|
|
160
|
+
updatedAt: nowIso(),
|
|
161
|
+
endedAt: null,
|
|
162
|
+
tags: normalizedJob.tags,
|
|
163
|
+
skill: normalizedJob.skill || null,
|
|
164
|
+
partnerProfile: normalizedJob.partnerProfile || null,
|
|
165
|
+
autoImprove: normalizedJob.autoImprove,
|
|
166
|
+
verificationMode: normalizedJob.verificationMode,
|
|
167
|
+
recordFeedback: normalizedJob.recordFeedback,
|
|
168
|
+
totalStages: normalizedJob.stages.length,
|
|
169
|
+
nextStageIndex: 0,
|
|
170
|
+
currentStage,
|
|
171
|
+
currentContext: '',
|
|
172
|
+
checkpoints: [],
|
|
173
|
+
stageHistory: [],
|
|
174
|
+
jobFilePath: normalizedJob.jobFilePath || null,
|
|
175
|
+
jobSpec: serializeJobForState(normalizedJob),
|
|
176
|
+
lastError: null,
|
|
177
|
+
stopReason: null,
|
|
178
|
+
improvementExperimentId: null,
|
|
179
|
+
verification: null,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
140
183
|
function readJobState(jobId) {
|
|
141
184
|
if (!jobId) return null;
|
|
142
185
|
return readJson(getJobRuntimePaths(jobId).statePath);
|
|
@@ -618,6 +661,8 @@ function executeJob(job, options = {}) {
|
|
|
618
661
|
id: job.id || generateJobId(),
|
|
619
662
|
tags: Array.isArray(job.tags) ? job.tags : [],
|
|
620
663
|
autoImprove: job.autoImprove !== false,
|
|
664
|
+
verificationMode: job.verificationMode === 'none' ? 'none' : 'standard',
|
|
665
|
+
recordFeedback: job.recordFeedback !== false,
|
|
621
666
|
stages: normalizeStages(job),
|
|
622
667
|
};
|
|
623
668
|
const previousState = options.previousState || readJobState(normalizedJob.id);
|
|
@@ -643,6 +688,8 @@ function executeJob(job, options = {}) {
|
|
|
643
688
|
skill: normalizedJob.skill || null,
|
|
644
689
|
partnerProfile: normalizedJob.partnerProfile || null,
|
|
645
690
|
autoImprove: normalizedJob.autoImprove,
|
|
691
|
+
verificationMode: normalizedJob.verificationMode,
|
|
692
|
+
recordFeedback: normalizedJob.recordFeedback,
|
|
646
693
|
totalStages: normalizedJob.stages.length,
|
|
647
694
|
nextStageIndex,
|
|
648
695
|
currentStage: normalizedJob.stages[nextStageIndex] ? normalizedJob.stages[nextStageIndex].name : 'verification',
|
|
@@ -733,7 +780,7 @@ function executeJob(job, options = {}) {
|
|
|
733
780
|
|
|
734
781
|
clearJobControl(normalizedJob.id);
|
|
735
782
|
|
|
736
|
-
const feedback = error && error.code === 'JOB_CANCELLED'
|
|
783
|
+
const feedback = (error && error.code === 'JOB_CANCELLED') || normalizedJob.recordFeedback === false
|
|
737
784
|
? null
|
|
738
785
|
: captureFeedback({
|
|
739
786
|
signal: 'down',
|
|
@@ -756,48 +803,60 @@ function executeJob(job, options = {}) {
|
|
|
756
803
|
}
|
|
757
804
|
}
|
|
758
805
|
|
|
759
|
-
const verification =
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
806
|
+
const verification = normalizedJob.verificationMode === 'none'
|
|
807
|
+
? null
|
|
808
|
+
: runVerificationLoop({
|
|
809
|
+
context: currentContext,
|
|
810
|
+
tags: normalizedJob.tags,
|
|
811
|
+
skill: normalizedJob.skill,
|
|
812
|
+
partnerProfile: normalizedJob.partnerProfile,
|
|
813
|
+
onRetry: normalizedJob.onRetry,
|
|
814
|
+
maxRetries: normalizedJob.maxRetries,
|
|
815
|
+
});
|
|
767
816
|
|
|
768
|
-
const improvementExperiment = verification.accepted
|
|
817
|
+
const improvementExperiment = !verification || verification.accepted
|
|
769
818
|
? null
|
|
770
819
|
: maybeQueueImprovementExperiment(normalizedJob, state, recall, {
|
|
771
820
|
type: 'verification',
|
|
772
821
|
verification,
|
|
773
822
|
});
|
|
774
823
|
|
|
775
|
-
const feedback =
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
:
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
824
|
+
const feedback = normalizedJob.recordFeedback === false
|
|
825
|
+
? null
|
|
826
|
+
: captureFeedback({
|
|
827
|
+
signal: !verification || verification.accepted ? 'up' : 'down',
|
|
828
|
+
context: !verification
|
|
829
|
+
? `Job ${normalizedJob.id} completed without post-run verification`
|
|
830
|
+
: verification.accepted
|
|
831
|
+
? `Job ${normalizedJob.id} passed verification after ${verification.attempts} attempt(s)`
|
|
832
|
+
: `Job ${normalizedJob.id} failed verification after ${verification.attempts} attempt(s): ${(verification.finalVerification.violations || []).map((violation) => violation.pattern).join('; ')}`,
|
|
833
|
+
whatWorked: !verification
|
|
834
|
+
? 'Operational job completed successfully'
|
|
835
|
+
: verification.accepted
|
|
836
|
+
? 'Verification loop accepted output'
|
|
837
|
+
: undefined,
|
|
838
|
+
whatWentWrong: verification && !verification.accepted ? `Failed ${verification.attempts} verification attempts` : undefined,
|
|
839
|
+
whatToChange: verification && !verification.accepted ? 'Improve output to avoid known mistake patterns' : undefined,
|
|
840
|
+
tags: !verification
|
|
841
|
+
? [...normalizedJob.tags, 'async-job-runner', 'verification-skipped']
|
|
842
|
+
: [...normalizedJob.tags, 'verification-loop'],
|
|
843
|
+
skill: normalizedJob.skill || 'async-job-runner',
|
|
844
|
+
});
|
|
786
845
|
|
|
787
846
|
const terminalState = writeJobState({
|
|
788
847
|
...(readJobState(normalizedJob.id) || state),
|
|
789
|
-
status: verification.accepted ? 'completed' : 'failed',
|
|
848
|
+
status: !verification || verification.accepted ? 'completed' : 'failed',
|
|
790
849
|
updatedAt: nowIso(),
|
|
791
850
|
endedAt: nowIso(),
|
|
792
851
|
currentStage: null,
|
|
793
852
|
nextStageIndex: normalizedJob.stages.length,
|
|
794
853
|
currentContext,
|
|
795
854
|
improvementExperimentId: improvementExperiment ? improvementExperiment.id : null,
|
|
796
|
-
verification: {
|
|
855
|
+
verification: verification ? {
|
|
797
856
|
accepted: verification.accepted,
|
|
798
857
|
attempts: verification.attempts,
|
|
799
858
|
score: verification.finalVerification ? verification.finalVerification.score : 0,
|
|
800
|
-
},
|
|
859
|
+
} : null,
|
|
801
860
|
});
|
|
802
861
|
|
|
803
862
|
clearJobControl(normalizedJob.id);
|
|
@@ -880,6 +939,7 @@ function runBatch(jobs) {
|
|
|
880
939
|
module.exports = {
|
|
881
940
|
recallContext,
|
|
882
941
|
executeJob,
|
|
942
|
+
queueJob,
|
|
883
943
|
runBatch,
|
|
884
944
|
appendJobLog,
|
|
885
945
|
readJobLog,
|
|
@@ -53,6 +53,7 @@ function detectAgent(flagAgent) {
|
|
|
53
53
|
if (['claude-code', 'claude'].includes(normalized)) return 'claude-code';
|
|
54
54
|
if (['codex'].includes(normalized)) return 'codex';
|
|
55
55
|
if (['gemini'].includes(normalized)) return 'gemini';
|
|
56
|
+
if (['forge', 'forgecode', 'forge-code'].includes(normalized)) return 'forge';
|
|
56
57
|
return null;
|
|
57
58
|
}
|
|
58
59
|
|
|
@@ -61,6 +62,7 @@ function detectAgent(flagAgent) {
|
|
|
61
62
|
if (fs.existsSync(path.join(home, '.claude'))) return 'claude-code';
|
|
62
63
|
if (fs.existsSync(path.join(home, '.codex'))) return 'codex';
|
|
63
64
|
if (fs.existsSync(path.join(home, '.gemini'))) return 'gemini';
|
|
65
|
+
if (fs.existsSync(path.join(process.cwd(), 'forge.yaml'))) return 'forge';
|
|
64
66
|
return null;
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -310,13 +312,64 @@ function wireGeminiHooks(options) {
|
|
|
310
312
|
return { changed: true, settingsPath, added };
|
|
311
313
|
}
|
|
312
314
|
|
|
315
|
+
// --- ForgeCode wiring ---
|
|
316
|
+
|
|
317
|
+
function forgeConfigPath() {
|
|
318
|
+
return path.join(process.cwd(), 'forge.yaml');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function wireForgeHooks(options) {
|
|
322
|
+
const dryRun = options.dryRun || false;
|
|
323
|
+
|
|
324
|
+
const preToolCmd = preToolHookCommand();
|
|
325
|
+
const userPromptCmd = userPromptHookCommand();
|
|
326
|
+
|
|
327
|
+
// ForgeCode uses YAML config (forge.yaml). We write a JSON-based hooks
|
|
328
|
+
// sidecar file (.thumbgate/forge-hooks.json) and append skill entries to
|
|
329
|
+
// forge.yaml if they are not already present.
|
|
330
|
+
const hooksPath = options.settingsPath || path.join(path.dirname(forgeConfigPath()), '.thumbgate', 'forge-hooks.json');
|
|
331
|
+
let existing = loadJsonFile(hooksPath) || {};
|
|
332
|
+
existing.hooks = existing.hooks || {};
|
|
333
|
+
|
|
334
|
+
const added = [];
|
|
335
|
+
|
|
336
|
+
if (!hookAlreadyPresent(existing.hooks.PreToolUse, preToolCmd)) {
|
|
337
|
+
existing.hooks.PreToolUse = existing.hooks.PreToolUse || [];
|
|
338
|
+
existing.hooks.PreToolUse.push({
|
|
339
|
+
matcher: 'Bash',
|
|
340
|
+
hooks: [{ type: 'command', command: preToolCmd }],
|
|
341
|
+
});
|
|
342
|
+
added.push({ lifecycle: 'PreToolUse', command: preToolCmd });
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (!hookAlreadyPresent(existing.hooks.UserPromptSubmit, userPromptCmd)) {
|
|
346
|
+
existing.hooks.UserPromptSubmit = existing.hooks.UserPromptSubmit || [];
|
|
347
|
+
existing.hooks.UserPromptSubmit.push({
|
|
348
|
+
hooks: [{ type: 'command', command: userPromptCmd }],
|
|
349
|
+
});
|
|
350
|
+
added.push({ lifecycle: 'UserPromptSubmit', command: userPromptCmd });
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (added.length === 0) {
|
|
354
|
+
return { changed: false, settingsPath: hooksPath, added: [] };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (!dryRun) {
|
|
358
|
+
const dir = path.dirname(hooksPath);
|
|
359
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
360
|
+
fs.writeFileSync(hooksPath, JSON.stringify(existing, null, 2) + '\n');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return { changed: true, settingsPath: hooksPath, added };
|
|
364
|
+
}
|
|
365
|
+
|
|
313
366
|
// --- Dispatcher ---
|
|
314
367
|
|
|
315
368
|
function wireHooks(options) {
|
|
316
369
|
const agent = detectAgent(options.agent);
|
|
317
370
|
if (!agent) {
|
|
318
371
|
return {
|
|
319
|
-
error: 'Could not detect AI agent. Use --agent=claude-code|codex|gemini',
|
|
372
|
+
error: 'Could not detect AI agent. Use --agent=claude-code|codex|gemini|forge',
|
|
320
373
|
agent: null,
|
|
321
374
|
changed: false,
|
|
322
375
|
};
|
|
@@ -333,6 +386,9 @@ function wireHooks(options) {
|
|
|
333
386
|
case 'gemini':
|
|
334
387
|
result = wireGeminiHooks(options);
|
|
335
388
|
break;
|
|
389
|
+
case 'forge':
|
|
390
|
+
result = wireForgeHooks(options);
|
|
391
|
+
break;
|
|
336
392
|
default:
|
|
337
393
|
return { error: `Unsupported agent: ${agent}`, agent, changed: false };
|
|
338
394
|
}
|
|
@@ -364,6 +420,7 @@ module.exports = {
|
|
|
364
420
|
wireClaudeHooks,
|
|
365
421
|
wireCodexHooks,
|
|
366
422
|
wireGeminiHooks,
|
|
423
|
+
wireForgeHooks,
|
|
367
424
|
hookAlreadyPresent,
|
|
368
425
|
loadJsonFile,
|
|
369
426
|
parseFlags,
|
|
@@ -372,6 +429,7 @@ module.exports = {
|
|
|
372
429
|
codexConfigPath,
|
|
373
430
|
geminiSettingsPath,
|
|
374
431
|
syncClaudeStatusLine,
|
|
432
|
+
forgeConfigPath,
|
|
375
433
|
CLAUDE_HOOKS,
|
|
376
434
|
preToolHookCommand,
|
|
377
435
|
userPromptHookCommand,
|