thumbgate 0.9.10
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 +134 -0
- package/.claude-plugin/bundle/icon.png +0 -0
- package/.claude-plugin/bundle/icon.svg +18 -0
- package/.claude-plugin/bundle/server/index.js +24 -0
- package/.claude-plugin/marketplace.json +36 -0
- package/.claude-plugin/plugin.json +21 -0
- package/.well-known/mcp/server-card.json +231 -0
- package/LICENSE +21 -0
- package/README.md +375 -0
- package/adapters/README.md +9 -0
- package/adapters/amp/skills/thumbgate-feedback/SKILL.md +22 -0
- package/adapters/chatgpt/INSTALL.md +83 -0
- package/adapters/chatgpt/openapi.yaml +1281 -0
- package/adapters/claude/.mcp.json +14 -0
- package/adapters/codex/config.toml +9 -0
- package/adapters/gemini/function-declarations.json +224 -0
- package/adapters/mcp/server-stdio.js +788 -0
- package/adapters/opencode/opencode.json +15 -0
- package/bin/cli.js +1484 -0
- package/bin/memory.sh +64 -0
- package/bin/obsidian-sync.sh +20 -0
- package/bin/postinstall.js +37 -0
- package/config/build-metadata.json +4 -0
- package/config/e2e-critical-flows.json +45 -0
- package/config/gate-templates.json +77 -0
- package/config/gates/claim-verification.json +29 -0
- package/config/gates/computer-use.json +39 -0
- package/config/gates/default.json +117 -0
- package/config/github-about.json +25 -0
- package/config/mcp-allowlists.json +135 -0
- package/config/model-tiers.json +33 -0
- package/config/partner-routing.json +132 -0
- package/config/policy-bundles/constrained-v1.json +64 -0
- package/config/policy-bundles/default-v1.json +91 -0
- package/config/rubrics/default-v1.json +52 -0
- package/config/skill-packs/react-testing.json +23 -0
- package/config/skill-packs/stripe-integration/references/api-spec.json +1 -0
- package/config/skill-packs/stripe-integration/references/webhook-guide.md +3 -0
- package/config/skill-specs/pr-reviewer.json +9 -0
- package/config/skill-specs/release-status.json +9 -0
- package/config/skill-specs/ticket-triage.json +9 -0
- package/config/subagent-profiles.json +32 -0
- package/config/tessl-tiles.json +29 -0
- package/config/thumbgate-settings.managed.json +12 -0
- package/openapi/openapi.yaml +1281 -0
- package/package.json +283 -0
- package/plugins/amp-skill/INSTALL.md +52 -0
- package/plugins/amp-skill/SKILL.md +64 -0
- package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +22 -0
- package/plugins/claude-codex-bridge/.mcp.json +12 -0
- package/plugins/claude-codex-bridge/INSTALL.md +43 -0
- package/plugins/claude-codex-bridge/README.md +46 -0
- package/plugins/claude-codex-bridge/scripts/codex-bridge.js +288 -0
- package/plugins/claude-codex-bridge/skills/adversarial-review/SKILL.md +24 -0
- package/plugins/claude-codex-bridge/skills/result/SKILL.md +22 -0
- package/plugins/claude-codex-bridge/skills/review/SKILL.md +28 -0
- package/plugins/claude-codex-bridge/skills/second-pass/SKILL.md +27 -0
- package/plugins/claude-codex-bridge/skills/setup/SKILL.md +21 -0
- package/plugins/claude-codex-bridge/skills/status/SKILL.md +19 -0
- package/plugins/claude-skill/INSTALL.md +55 -0
- package/plugins/claude-skill/SKILL.md +46 -0
- package/plugins/codex-profile/.codex-plugin/plugin.json +43 -0
- package/plugins/codex-profile/.mcp.json +12 -0
- package/plugins/codex-profile/AGENTS.md +20 -0
- package/plugins/codex-profile/INSTALL.md +66 -0
- package/plugins/codex-profile/README.md +37 -0
- package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +23 -0
- package/plugins/cursor-marketplace/CHANGELOG.md +30 -0
- package/plugins/cursor-marketplace/LICENSE +21 -0
- package/plugins/cursor-marketplace/README.md +124 -0
- package/plugins/cursor-marketplace/agents/reliability-reviewer.md +31 -0
- package/plugins/cursor-marketplace/assets/logo-400x400.png +0 -0
- package/plugins/cursor-marketplace/commands/capture-feedback.md +33 -0
- package/plugins/cursor-marketplace/commands/check-gates.md +25 -0
- package/plugins/cursor-marketplace/commands/show-lessons.md +27 -0
- package/plugins/cursor-marketplace/hooks/hooks.json +10 -0
- package/plugins/cursor-marketplace/mcp.json +12 -0
- package/plugins/cursor-marketplace/rules/feedback-capture.mdc +34 -0
- package/plugins/cursor-marketplace/rules/pre-action-gates.mdc +30 -0
- package/plugins/cursor-marketplace/rules/session-continuity.mdc +28 -0
- package/plugins/cursor-marketplace/scripts/gate-check.sh +11 -0
- package/plugins/cursor-marketplace/skills/capture-feedback/SKILL.md +47 -0
- package/plugins/cursor-marketplace/skills/prevention-rules/SKILL.md +31 -0
- package/plugins/cursor-marketplace/skills/recall-context/SKILL.md +30 -0
- package/plugins/cursor-marketplace/skills/search-lessons/SKILL.md +33 -0
- package/plugins/gemini-extension/INSTALL.md +92 -0
- package/plugins/gemini-extension/gemini_prompt.txt +14 -0
- package/plugins/gemini-extension/tool_contract.json +45 -0
- package/plugins/opencode-profile/INSTALL.md +57 -0
- package/public/assets/instagram-card.png +0 -0
- package/public/assets/tiktok-agent-memory.mp4 +0 -0
- package/public/blog.html +400 -0
- package/public/dashboard.html +1093 -0
- package/public/guide.html +317 -0
- package/public/index.html +1014 -0
- package/public/learn/agent-harness-pattern.html +180 -0
- package/public/learn/ai-agent-persistent-memory.html +202 -0
- package/public/learn/learn.css +45 -0
- package/public/learn/mcp-pre-action-gates-explained.html +172 -0
- package/public/learn/stop-ai-agent-force-push.html +134 -0
- package/public/learn/vibe-coding-safety-net.html +142 -0
- package/public/learn.html +213 -0
- package/public/lessons.html +650 -0
- package/public/vercel.json +8 -0
- package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
- package/scripts/a2ui-engine.js +73 -0
- package/scripts/access-anomaly-detector.js +12 -0
- package/scripts/adk-consolidator.js +266 -0
- package/scripts/agent-readiness.js +220 -0
- package/scripts/agent-security-hardening.js +227 -0
- package/scripts/agentic-data-pipeline.js +847 -0
- package/scripts/analytics-report.js +328 -0
- package/scripts/analytics-window.js +158 -0
- package/scripts/async-job-runner.js +1001 -0
- package/scripts/audit-trail.js +398 -0
- package/scripts/auto-promote-gates.js +299 -0
- package/scripts/auto-wire-hooks.js +312 -0
- package/scripts/autonomous-sales-agent.js +39 -0
- package/scripts/autoresearch-runner.js +216 -0
- package/scripts/background-agent-governance.js +237 -0
- package/scripts/behavioral-extraction.js +97 -0
- package/scripts/belief-update.js +84 -0
- package/scripts/billing.js +2438 -0
- package/scripts/bot-detector.js +50 -0
- package/scripts/budget-guard.js +173 -0
- package/scripts/build-claude-mcpb.js +189 -0
- package/scripts/build-metadata.js +97 -0
- package/scripts/check-congruence.js +322 -0
- package/scripts/cli-feedback.js +135 -0
- package/scripts/cli-telemetry.js +87 -0
- package/scripts/cloudflare-dynamic-sandbox.js +315 -0
- package/scripts/code-reasoning.js +350 -0
- package/scripts/codegraph-context.js +466 -0
- package/scripts/commercial-offer.js +56 -0
- package/scripts/computer-use-firewall.js +250 -0
- package/scripts/context-engine.js +694 -0
- package/scripts/contextfs.js +1287 -0
- package/scripts/conversation-context.js +119 -0
- package/scripts/creator-campaigns.js +239 -0
- package/scripts/daemon-manager.js +108 -0
- package/scripts/daily-digest.js +11 -0
- package/scripts/dashboard-render-spec.js +395 -0
- package/scripts/dashboard.js +1058 -0
- package/scripts/data-governance.js +173 -0
- package/scripts/delegation-runtime.js +900 -0
- package/scripts/deploy-gcp.sh +44 -0
- package/scripts/deploy-policy.js +263 -0
- package/scripts/disagreement-mining.js +315 -0
- package/scripts/dispatch-brief.js +159 -0
- package/scripts/distribution-surfaces.js +44 -0
- package/scripts/dpo-optimizer.js +209 -0
- package/scripts/ephemeral-agent-store.js +219 -0
- package/scripts/eval-harness.js +56 -0
- package/scripts/evolution-state.js +241 -0
- package/scripts/experiment-tracker.js +267 -0
- package/scripts/export-databricks-bundle.js +242 -0
- package/scripts/export-dpo-pairs.js +345 -0
- package/scripts/export-kto-pairs.js +310 -0
- package/scripts/export-training.js +448 -0
- package/scripts/failure-diagnostics.js +558 -0
- package/scripts/feedback-attribution.js +313 -0
- package/scripts/feedback-fallback.js +111 -0
- package/scripts/feedback-history-distiller.js +391 -0
- package/scripts/feedback-inbox-read.js +162 -0
- package/scripts/feedback-loop.js +1887 -0
- package/scripts/feedback-paths.js +145 -0
- package/scripts/feedback-quality.js +139 -0
- package/scripts/feedback-root-consolidator.js +238 -0
- package/scripts/feedback-schema.js +426 -0
- package/scripts/feedback-session.js +286 -0
- package/scripts/feedback-to-memory.js +185 -0
- package/scripts/feedback-to-rules.js +163 -0
- package/scripts/filesystem-search.js +404 -0
- package/scripts/funnel-analytics.js +35 -0
- package/scripts/gate-satisfy.js +42 -0
- package/scripts/gate-stats.js +116 -0
- package/scripts/gate-templates.js +70 -0
- package/scripts/gates-engine.js +816 -0
- package/scripts/generate-paperbanana-diagrams.sh +99 -0
- package/scripts/generate-pretool-hook.sh +40 -0
- package/scripts/github-about.js +350 -0
- package/scripts/github-outreach.js +65 -0
- package/scripts/gtm-revenue-loop.js +520 -0
- package/scripts/hallucination-detector.js +226 -0
- package/scripts/hf-papers.js +317 -0
- package/scripts/history-distiller.js +200 -0
- package/scripts/hook-auto-capture.sh +95 -0
- package/scripts/hook-stop-pr-thread-check.sh +68 -0
- package/scripts/hook-stop-self-score.sh +51 -0
- package/scripts/hook-stop-verify-deploy.sh +31 -0
- package/scripts/hook-thumbgate-cache-updater.js +48 -0
- package/scripts/hook-verify-before-done.sh +20 -0
- package/scripts/hosted-config.js +170 -0
- package/scripts/hybrid-feedback-context.js +676 -0
- package/scripts/install-mcp.js +159 -0
- package/scripts/intent-router.js +392 -0
- package/scripts/internal-agent-bootstrap.js +490 -0
- package/scripts/jsonl-watcher.js +155 -0
- package/scripts/lesson-db.js +613 -0
- package/scripts/lesson-inference.js +315 -0
- package/scripts/lesson-retrieval.js +95 -0
- package/scripts/lesson-rotation.js +137 -0
- package/scripts/lesson-search.js +644 -0
- package/scripts/lesson-synthesis.js +196 -0
- package/scripts/license.js +50 -0
- package/scripts/local-model-profile.js +383 -0
- package/scripts/markdown-escape.js +12 -0
- package/scripts/marketing-experiment.js +671 -0
- package/scripts/mcp-config.js +149 -0
- package/scripts/mcp-policy.js +99 -0
- package/scripts/memalign-recall.js +111 -0
- package/scripts/memory-firewall.js +222 -0
- package/scripts/memory-migration.js +296 -0
- package/scripts/meta-policy.js +194 -0
- package/scripts/metered-billing.js +16 -0
- package/scripts/model-tier-router.js +301 -0
- package/scripts/money-watcher.js +71 -0
- package/scripts/multi-hop-recall.js +240 -0
- package/scripts/natural-language-harness.js +330 -0
- package/scripts/obsidian-export.js +712 -0
- package/scripts/operational-dashboard.js +103 -0
- package/scripts/operational-summary.js +93 -0
- package/scripts/optimize-context.js +17 -0
- package/scripts/org-dashboard.js +201 -0
- package/scripts/partner-orchestration.js +146 -0
- package/scripts/per-step-scoring.js +165 -0
- package/scripts/perplexity-marketing.js +466 -0
- package/scripts/pii-scanner.js +153 -0
- package/scripts/plan-gate.js +154 -0
- package/scripts/post-everywhere.js +308 -0
- package/scripts/post-to-x-retry.sh +22 -0
- package/scripts/post-to-x.js +369 -0
- package/scripts/pr-manager.js +236 -0
- package/scripts/predictive-insights.js +356 -0
- package/scripts/principle-extractor.js +162 -0
- package/scripts/pro-features.js +40 -0
- package/scripts/pro-local-dashboard.js +174 -0
- package/scripts/problem-detail.js +53 -0
- package/scripts/product-feedback.js +134 -0
- package/scripts/profile-router.js +245 -0
- package/scripts/prompt-dlp.js +221 -0
- package/scripts/prompt-guard.js +83 -0
- package/scripts/prove-adapters.js +863 -0
- package/scripts/prove-attribution.js +365 -0
- package/scripts/prove-automation.js +653 -0
- package/scripts/prove-autoresearch.js +304 -0
- package/scripts/prove-claim-verification.js +277 -0
- package/scripts/prove-cloudflare-sandbox.js +163 -0
- package/scripts/prove-data-pipeline.js +410 -0
- package/scripts/prove-data-quality.js +227 -0
- package/scripts/prove-evolution.js +352 -0
- package/scripts/prove-harnesses.js +287 -0
- package/scripts/prove-intelligence.js +259 -0
- package/scripts/prove-lancedb.js +371 -0
- package/scripts/prove-local-intelligence.js +342 -0
- package/scripts/prove-loop-closure.js +263 -0
- package/scripts/prove-predictive-insights.js +357 -0
- package/scripts/prove-runtime.js +350 -0
- package/scripts/prove-seo-gsd.js +234 -0
- package/scripts/prove-settings.js +279 -0
- package/scripts/prove-subway-upgrades.js +277 -0
- package/scripts/prove-tessl.js +229 -0
- package/scripts/prove-training-export.js +327 -0
- package/scripts/prove-workflow-contract.js +116 -0
- package/scripts/prove-xmemory.js +332 -0
- package/scripts/publish-decision.js +133 -0
- package/scripts/pulse.js +80 -0
- package/scripts/rate-limiter.js +125 -0
- package/scripts/reddit-dm-outreach.js +182 -0
- package/scripts/reddit-monitor-cron.sh +26 -0
- package/scripts/reflector-agent.js +221 -0
- package/scripts/reminder-engine.js +132 -0
- package/scripts/revenue-status.js +472 -0
- package/scripts/risk-scorer.js +458 -0
- package/scripts/rlaif-self-audit.js +129 -0
- package/scripts/rubric-engine.js +230 -0
- package/scripts/schedule-manager.js +251 -0
- package/scripts/secret-scanner.js +414 -0
- package/scripts/self-heal.js +147 -0
- package/scripts/self-healing-check.js +188 -0
- package/scripts/semantic-layer.js +98 -0
- package/scripts/seo-gsd.js +1153 -0
- package/scripts/settings-hierarchy.js +214 -0
- package/scripts/shieldcortex-memory-firewall-runner.mjs +53 -0
- package/scripts/skill-exporter.js +262 -0
- package/scripts/skill-generator.js +446 -0
- package/scripts/skill-materializer.js +134 -0
- package/scripts/skill-packs.js +136 -0
- package/scripts/skill-proposer.js +99 -0
- package/scripts/skill-quality-tracker.js +284 -0
- package/scripts/slo-alert-engine.js +14 -0
- package/scripts/slow-loop.js +72 -0
- package/scripts/social-analytics/db/schema.sql +32 -0
- package/scripts/social-analytics/digest.js +256 -0
- package/scripts/social-analytics/generate-instagram-card.js +97 -0
- package/scripts/social-analytics/instagram-thumbgate-post.js +73 -0
- package/scripts/social-analytics/mcp-server.js +289 -0
- package/scripts/social-analytics/normalizer.js +580 -0
- package/scripts/social-analytics/notify.js +162 -0
- package/scripts/social-analytics/poll-all.js +107 -0
- package/scripts/social-analytics/pollers/github.js +195 -0
- package/scripts/social-analytics/pollers/instagram.js +253 -0
- package/scripts/social-analytics/pollers/linkedin.js +330 -0
- package/scripts/social-analytics/pollers/plausible.js +247 -0
- package/scripts/social-analytics/pollers/reddit.js +306 -0
- package/scripts/social-analytics/pollers/threads.js +233 -0
- package/scripts/social-analytics/pollers/tiktok.js +203 -0
- package/scripts/social-analytics/pollers/x.js +227 -0
- package/scripts/social-analytics/pollers/youtube.js +304 -0
- package/scripts/social-analytics/pollers/zernio.js +180 -0
- package/scripts/social-analytics/publish-instagram-thumbgate.js +85 -0
- package/scripts/social-analytics/publishers/devto.js +122 -0
- package/scripts/social-analytics/publishers/instagram.js +317 -0
- package/scripts/social-analytics/publishers/linkedin.js +294 -0
- package/scripts/social-analytics/publishers/reddit.js +390 -0
- package/scripts/social-analytics/publishers/threads.js +275 -0
- package/scripts/social-analytics/publishers/tiktok.js +217 -0
- package/scripts/social-analytics/publishers/x.js +259 -0
- package/scripts/social-analytics/publishers/youtube.js +223 -0
- package/scripts/social-analytics/publishers/zernio.js +209 -0
- package/scripts/social-analytics/run-digest.js +34 -0
- package/scripts/social-analytics/store.js +257 -0
- package/scripts/social-analytics/utm.js +143 -0
- package/scripts/social-pipeline.js +2628 -0
- package/scripts/social-quality-gate.js +18 -0
- package/scripts/social-reply-monitor.js +445 -0
- package/scripts/status-dashboard.js +155 -0
- package/scripts/statusline-lesson.js +16 -0
- package/scripts/statusline-tower.js +8 -0
- package/scripts/statusline.sh +116 -0
- package/scripts/stripe-live-status.js +115 -0
- package/scripts/subagent-profiles.js +79 -0
- package/scripts/sync-gh-secrets-from-env.sh +70 -0
- package/scripts/sync-github-about.js +52 -0
- package/scripts/sync-version.js +451 -0
- package/scripts/synthetic-dpo.js +234 -0
- package/scripts/telemetry-analytics.js +821 -0
- package/scripts/tessl-export.js +371 -0
- package/scripts/test-coverage.js +120 -0
- package/scripts/thompson-sampling.js +417 -0
- package/scripts/thumbgate-search.js +189 -0
- package/scripts/tool-kpi-tracker.js +12 -0
- package/scripts/tool-registry.js +811 -0
- package/scripts/train_from_feedback.py +910 -0
- package/scripts/user-profile.js +78 -0
- package/scripts/validate-feedback.js +580 -0
- package/scripts/validate-workflow-contract.js +287 -0
- package/scripts/vector-store.js +198 -0
- package/scripts/verification-loop.js +291 -0
- package/scripts/verify-obsidian-setup.sh +269 -0
- package/scripts/verify-run.js +269 -0
- package/scripts/webhook-delivery.js +62 -0
- package/scripts/weekly-auto-post.js +124 -0
- package/scripts/workflow-runs.js +154 -0
- package/scripts/workflow-sprint-intake.js +475 -0
- package/scripts/workspace-evolver.js +374 -0
- package/scripts/x-autonomous-marketing.js +139 -0
- package/scripts/xmemory-lite.js +405 -0
- package/skills/agent-memory/SKILL.md +97 -0
- package/skills/solve-architecture-autonomy/SKILL.md +17 -0
- package/skills/solve-architecture-autonomy/tool.js +33 -0
- package/skills/thumbgate/SKILL.md +114 -0
- package/skills/thumbgate-feedback/SKILL.md +49 -0
- package/src/api/server.js +4208 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { resolveFeedbackDir } = require('./feedback-paths');
|
|
7
|
+
|
|
8
|
+
const NEG = new Set(['negative', 'negative_strong', 'down', 'thumbs_down']);
|
|
9
|
+
const POS = new Set(['positive', 'positive_strong', 'up', 'thumbs_up']);
|
|
10
|
+
|
|
11
|
+
/** Minimum cluster size to generate a skill */
|
|
12
|
+
const MIN_CLUSTER_SIZE = 3;
|
|
13
|
+
|
|
14
|
+
/** Minimum tag overlap to consider two entries related */
|
|
15
|
+
const MIN_TAG_OVERLAP = 2;
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Directory discovery (mirrors feedback-loop.js)
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Discover the feedback directory using the standard ThumbGate resolution order:
|
|
23
|
+
* 1. THUMBGATE_FEEDBACK_DIR env var
|
|
24
|
+
* 2. .thumbgate/ in cwd
|
|
25
|
+
* 3. .claude/memory/feedback/ in cwd
|
|
26
|
+
* 4. ~/.thumbgate/projects/<cwd-basename>/
|
|
27
|
+
* @returns {string} Resolved feedback directory path
|
|
28
|
+
*/
|
|
29
|
+
function discoverFeedbackDir() {
|
|
30
|
+
return resolveFeedbackDir();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Helpers
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Ensure a directory exists, creating it recursively if needed.
|
|
39
|
+
* @param {string} dirPath
|
|
40
|
+
*/
|
|
41
|
+
function ensureDir(dirPath) {
|
|
42
|
+
if (!fs.existsSync(dirPath)) {
|
|
43
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Append a JSON record as a single line to a JSONL file.
|
|
49
|
+
* @param {string} filePath
|
|
50
|
+
* @param {object} record
|
|
51
|
+
*/
|
|
52
|
+
function appendJSONL(filePath, record) {
|
|
53
|
+
ensureDir(path.dirname(filePath));
|
|
54
|
+
fs.appendFileSync(filePath, `${JSON.stringify(record)}\n`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parse a JSONL feedback file into an array of entries.
|
|
59
|
+
* @param {string} filePath
|
|
60
|
+
* @returns {object[]}
|
|
61
|
+
*/
|
|
62
|
+
function parseFeedbackFile(filePath) {
|
|
63
|
+
if (!fs.existsSync(filePath)) return [];
|
|
64
|
+
const entries = [];
|
|
65
|
+
for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) {
|
|
66
|
+
const trimmed = line.trim();
|
|
67
|
+
if (!trimmed) continue;
|
|
68
|
+
try { entries.push(JSON.parse(trimmed)); } catch { /* skip malformed */ }
|
|
69
|
+
}
|
|
70
|
+
return entries;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Classify a feedback entry signal as positive, negative, or null.
|
|
75
|
+
* @param {object} entry
|
|
76
|
+
* @returns {'positive'|'negative'|null}
|
|
77
|
+
*/
|
|
78
|
+
function classifySignal(entry) {
|
|
79
|
+
const sig = (entry.signal || entry.feedback || '').toLowerCase();
|
|
80
|
+
if (NEG.has(sig)) return 'negative';
|
|
81
|
+
if (POS.has(sig)) return 'positive';
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Extract tags from a feedback entry, including richContext.domain.
|
|
87
|
+
* @param {object} entry
|
|
88
|
+
* @returns {string[]}
|
|
89
|
+
*/
|
|
90
|
+
function extractTags(entry) {
|
|
91
|
+
const tags = new Set();
|
|
92
|
+
if (Array.isArray(entry.tags)) {
|
|
93
|
+
for (const t of entry.tags) {
|
|
94
|
+
if (t && typeof t === 'string') tags.add(t.toLowerCase());
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (entry.richContext && entry.richContext.domain) {
|
|
98
|
+
tags.add(entry.richContext.domain.toLowerCase());
|
|
99
|
+
}
|
|
100
|
+
if (entry.task_category) {
|
|
101
|
+
tags.add(entry.task_category.toLowerCase());
|
|
102
|
+
}
|
|
103
|
+
if (entry.category) {
|
|
104
|
+
tags.add(entry.category.toLowerCase());
|
|
105
|
+
}
|
|
106
|
+
return [...tags];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Count overlapping elements between two string arrays.
|
|
111
|
+
* @param {string[]} a
|
|
112
|
+
* @param {string[]} b
|
|
113
|
+
* @returns {number}
|
|
114
|
+
*/
|
|
115
|
+
function tagOverlap(a, b) {
|
|
116
|
+
const setB = new Set(b);
|
|
117
|
+
let count = 0;
|
|
118
|
+
for (const t of a) {
|
|
119
|
+
if (setB.has(t)) count++;
|
|
120
|
+
}
|
|
121
|
+
return count;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Slugify a string for use as a filename.
|
|
126
|
+
* @param {string} str
|
|
127
|
+
* @returns {string}
|
|
128
|
+
*/
|
|
129
|
+
function slugify(str) {
|
|
130
|
+
return str
|
|
131
|
+
.toLowerCase()
|
|
132
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
133
|
+
.replace(/^-|-$/g, '')
|
|
134
|
+
.slice(0, 60);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Clustering
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Cluster negative feedback entries by tag overlap (>= minOverlap shared tags).
|
|
143
|
+
* Uses a single-pass union approach: each entry joins the first cluster
|
|
144
|
+
* it overlaps with, or starts a new one.
|
|
145
|
+
*
|
|
146
|
+
* @param {object[]} negEntries - Negative feedback entries (already classified)
|
|
147
|
+
* @param {number} [minOverlap=2] - Minimum shared tags to merge into a cluster
|
|
148
|
+
* @returns {Map<string, {tags: string[], entries: object[]}>}
|
|
149
|
+
*/
|
|
150
|
+
function clusterByTags(negEntries, minOverlap) {
|
|
151
|
+
if (minOverlap === undefined) minOverlap = MIN_TAG_OVERLAP;
|
|
152
|
+
|
|
153
|
+
/** @type {Array<{tags: Set<string>, entries: object[]}>} */
|
|
154
|
+
const clusters = [];
|
|
155
|
+
|
|
156
|
+
for (const entry of negEntries) {
|
|
157
|
+
const entryTags = extractTags(entry);
|
|
158
|
+
if (entryTags.length === 0) continue;
|
|
159
|
+
|
|
160
|
+
let merged = false;
|
|
161
|
+
for (const cluster of clusters) {
|
|
162
|
+
const clusterTagsArr = [...cluster.tags];
|
|
163
|
+
if (tagOverlap(entryTags, clusterTagsArr) >= minOverlap) {
|
|
164
|
+
cluster.entries.push(entry);
|
|
165
|
+
for (const t of entryTags) cluster.tags.add(t);
|
|
166
|
+
merged = true;
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!merged) {
|
|
172
|
+
clusters.push({ tags: new Set(entryTags), entries: [entry] });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Convert to a map keyed by sorted tag string
|
|
177
|
+
const result = new Map();
|
|
178
|
+
for (const cluster of clusters) {
|
|
179
|
+
const key = [...cluster.tags].sort().join(', ');
|
|
180
|
+
result.set(key, {
|
|
181
|
+
tags: [...cluster.tags].sort(),
|
|
182
|
+
entries: cluster.entries,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
// Skill generation
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Build DO rules from positive feedback entries whose tags overlap with the cluster.
|
|
194
|
+
* @param {object[]} posEntries - All positive feedback entries
|
|
195
|
+
* @param {string[]} clusterTags - Tags from the negative cluster
|
|
196
|
+
* @returns {string[]}
|
|
197
|
+
*/
|
|
198
|
+
function buildDoRules(posEntries, clusterTags) {
|
|
199
|
+
const rules = [];
|
|
200
|
+
const seen = new Set();
|
|
201
|
+
|
|
202
|
+
for (const entry of posEntries) {
|
|
203
|
+
const entryTags = extractTags(entry);
|
|
204
|
+
if (tagOverlap(entryTags, clusterTags) < 1) continue;
|
|
205
|
+
|
|
206
|
+
const text = entry.whatWorked || entry.context || '';
|
|
207
|
+
if (!text || text.length < 10) continue;
|
|
208
|
+
|
|
209
|
+
const normalized = text.slice(0, 120).toLowerCase().trim();
|
|
210
|
+
if (seen.has(normalized)) continue;
|
|
211
|
+
seen.add(normalized);
|
|
212
|
+
|
|
213
|
+
rules.push(text.slice(0, 200));
|
|
214
|
+
}
|
|
215
|
+
return rules;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Build INSTEAD rules from the negative entries in a cluster.
|
|
220
|
+
* @param {object[]} negEntries - Negative entries from the cluster
|
|
221
|
+
* @returns {string[]}
|
|
222
|
+
*/
|
|
223
|
+
function buildInsteadRules(negEntries) {
|
|
224
|
+
const rules = [];
|
|
225
|
+
const seen = new Set();
|
|
226
|
+
|
|
227
|
+
for (const entry of negEntries) {
|
|
228
|
+
const text = entry.whatWentWrong || entry.whatToChange || entry.context || '';
|
|
229
|
+
if (!text || text.length < 10) continue;
|
|
230
|
+
|
|
231
|
+
const normalized = text.slice(0, 120).toLowerCase().trim();
|
|
232
|
+
if (seen.has(normalized)) continue;
|
|
233
|
+
seen.add(normalized);
|
|
234
|
+
|
|
235
|
+
rules.push(text.slice(0, 200));
|
|
236
|
+
}
|
|
237
|
+
return rules;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Generate a SKILL.md string from a single cluster.
|
|
242
|
+
*
|
|
243
|
+
* @param {{tags: string[], entries: object[], doRules: string[], insteadRules: string[], approvalRate: string}} cluster
|
|
244
|
+
* @returns {string}
|
|
245
|
+
*/
|
|
246
|
+
function generateSkillFromCluster(cluster) {
|
|
247
|
+
const { tags, entries, approvalRate } = cluster;
|
|
248
|
+
const doRules = cluster.doRules || (cluster.positiveEntries ? buildDoRules(cluster.positiveEntries, tags) : []);
|
|
249
|
+
const insteadRules = cluster.insteadRules || buildInsteadRules(entries);
|
|
250
|
+
const name = tags.slice(0, 3).join('-') || 'unnamed';
|
|
251
|
+
const description = `Prevention rules for ${tags.join(', ')} domain — auto-generated from ${entries.length} negative feedback signals.`;
|
|
252
|
+
const triggers = tags.map(t => `- Task involves \`${t}\``).join('\n');
|
|
253
|
+
|
|
254
|
+
const lines = [];
|
|
255
|
+
|
|
256
|
+
// Frontmatter
|
|
257
|
+
lines.push('---');
|
|
258
|
+
lines.push(`name: ${name}`);
|
|
259
|
+
lines.push(`description: ${description}`);
|
|
260
|
+
lines.push(`generated: ${new Date().toISOString()}`);
|
|
261
|
+
lines.push(`evidence_count: ${entries.length}`);
|
|
262
|
+
lines.push(`approval_rate: ${approvalRate}`);
|
|
263
|
+
lines.push('---');
|
|
264
|
+
lines.push('');
|
|
265
|
+
|
|
266
|
+
// Trigger conditions
|
|
267
|
+
lines.push('# Trigger Conditions');
|
|
268
|
+
lines.push('');
|
|
269
|
+
lines.push(triggers);
|
|
270
|
+
lines.push('');
|
|
271
|
+
|
|
272
|
+
// DO rules
|
|
273
|
+
lines.push('# DO Rules');
|
|
274
|
+
lines.push('');
|
|
275
|
+
if (doRules.length > 0) {
|
|
276
|
+
for (const rule of doRules) {
|
|
277
|
+
lines.push(`- ${rule}`);
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
lines.push('- No positive patterns recorded yet for this domain.');
|
|
281
|
+
}
|
|
282
|
+
lines.push('');
|
|
283
|
+
|
|
284
|
+
// INSTEAD rules
|
|
285
|
+
lines.push('# INSTEAD Rules');
|
|
286
|
+
lines.push('');
|
|
287
|
+
if (insteadRules.length > 0) {
|
|
288
|
+
for (const rule of insteadRules) {
|
|
289
|
+
lines.push(`- NEVER: ${rule}`);
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
lines.push('- No recurring anti-patterns extracted.');
|
|
293
|
+
}
|
|
294
|
+
lines.push('');
|
|
295
|
+
|
|
296
|
+
// Evidence
|
|
297
|
+
lines.push('# Evidence');
|
|
298
|
+
lines.push('');
|
|
299
|
+
lines.push(`- **Negative signals**: ${entries.length}`);
|
|
300
|
+
lines.push(`- **Domain approval rate**: ${approvalRate}`);
|
|
301
|
+
lines.push(`- **Tags**: ${tags.join(', ')}`);
|
|
302
|
+
lines.push(`- **DO rules**: ${doRules.length}`);
|
|
303
|
+
lines.push(`- **INSTEAD rules**: ${insteadRules.length}`);
|
|
304
|
+
lines.push('');
|
|
305
|
+
|
|
306
|
+
return lines.join('\n');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Generate skill files from ThumbGate feedback logs.
|
|
311
|
+
*
|
|
312
|
+
* Reads the feedback log, clusters negative feedback by tag overlap,
|
|
313
|
+
* and produces SKILL.md files for clusters with 3+ entries.
|
|
314
|
+
*
|
|
315
|
+
* @param {object} [options]
|
|
316
|
+
* @param {string} [options.feedbackDir] - Override feedback directory
|
|
317
|
+
* @param {number} [options.minClusterSize] - Minimum entries per cluster (default 3)
|
|
318
|
+
* @param {number} [options.minTagOverlap] - Minimum tag overlap for clustering (default 2)
|
|
319
|
+
* @param {boolean} [options.dryRun] - If true, return results without writing files
|
|
320
|
+
* @returns {{skillName: string, filePath: string, ruleCount: number, evidenceCount: number}[]}
|
|
321
|
+
*/
|
|
322
|
+
function generateSkills(options) {
|
|
323
|
+
if (!options) options = {};
|
|
324
|
+
const feedbackDir = options.feedbackDir || discoverFeedbackDir();
|
|
325
|
+
const minClusterSize = options.minClusterSize || MIN_CLUSTER_SIZE;
|
|
326
|
+
const minTagOverlap = options.minTagOverlap || MIN_TAG_OVERLAP;
|
|
327
|
+
const dryRun = options.dryRun || false;
|
|
328
|
+
|
|
329
|
+
const logPath = path.join(feedbackDir, 'feedback-log.jsonl');
|
|
330
|
+
const outputDir = path.join(feedbackDir, 'generated-skills');
|
|
331
|
+
const auditLogPath = path.join(feedbackDir, 'skill-generation-audit.jsonl');
|
|
332
|
+
|
|
333
|
+
const entries = parseFeedbackFile(logPath);
|
|
334
|
+
if (entries.length === 0) return [];
|
|
335
|
+
|
|
336
|
+
// Separate positive and negative entries
|
|
337
|
+
const posEntries = [];
|
|
338
|
+
const negEntries = [];
|
|
339
|
+
for (const entry of entries) {
|
|
340
|
+
const cls = classifySignal(entry);
|
|
341
|
+
if (cls === 'positive') posEntries.push(entry);
|
|
342
|
+
else if (cls === 'negative') negEntries.push(entry);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (negEntries.length === 0) return [];
|
|
346
|
+
|
|
347
|
+
// Cluster negative feedback by tag overlap
|
|
348
|
+
const clusters = clusterByTags(negEntries, minTagOverlap);
|
|
349
|
+
|
|
350
|
+
const results = [];
|
|
351
|
+
|
|
352
|
+
for (const [key, cluster] of clusters) {
|
|
353
|
+
if (cluster.entries.length < minClusterSize) continue;
|
|
354
|
+
|
|
355
|
+
// Compute domain-scoped approval rate
|
|
356
|
+
const clusterTags = cluster.tags;
|
|
357
|
+
let domainPos = 0;
|
|
358
|
+
const domainNeg = cluster.entries.length;
|
|
359
|
+
for (const pe of posEntries) {
|
|
360
|
+
if (tagOverlap(extractTags(pe), clusterTags) >= 1) domainPos++;
|
|
361
|
+
}
|
|
362
|
+
const domainTotal = domainPos + domainNeg;
|
|
363
|
+
const approvalRate = domainTotal > 0
|
|
364
|
+
? `${((domainPos / domainTotal) * 100).toFixed(1)}%`
|
|
365
|
+
: '0.0%';
|
|
366
|
+
|
|
367
|
+
const doRules = buildDoRules(posEntries, clusterTags);
|
|
368
|
+
const insteadRules = buildInsteadRules(cluster.entries);
|
|
369
|
+
const ruleCount = doRules.length + insteadRules.length;
|
|
370
|
+
|
|
371
|
+
const skillContent = generateSkillFromCluster({
|
|
372
|
+
tags: clusterTags,
|
|
373
|
+
entries: cluster.entries,
|
|
374
|
+
doRules,
|
|
375
|
+
insteadRules,
|
|
376
|
+
approvalRate,
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const skillName = slugify(clusterTags.slice(0, 3).join('-')) || 'unnamed';
|
|
380
|
+
const fileName = `${skillName}.SKILL.md`;
|
|
381
|
+
const filePath = path.join(outputDir, fileName);
|
|
382
|
+
|
|
383
|
+
if (!dryRun) {
|
|
384
|
+
ensureDir(outputDir);
|
|
385
|
+
fs.writeFileSync(filePath, skillContent, 'utf8');
|
|
386
|
+
|
|
387
|
+
// Audit log
|
|
388
|
+
appendJSONL(auditLogPath, {
|
|
389
|
+
event: 'skill_generated',
|
|
390
|
+
skillName,
|
|
391
|
+
filePath,
|
|
392
|
+
ruleCount,
|
|
393
|
+
evidenceCount: cluster.entries.length,
|
|
394
|
+
tags: clusterTags,
|
|
395
|
+
approvalRate,
|
|
396
|
+
timestamp: new Date().toISOString(),
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
results.push({
|
|
401
|
+
skillName,
|
|
402
|
+
filePath,
|
|
403
|
+
ruleCount,
|
|
404
|
+
evidenceCount: cluster.entries.length,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return results;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ---------------------------------------------------------------------------
|
|
412
|
+
// CLI entry point
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
|
|
415
|
+
if (require.main === module) {
|
|
416
|
+
try {
|
|
417
|
+
const dryRun = process.argv.includes('--dry-run');
|
|
418
|
+
const feedbackDir = process.argv.find(function(a) {
|
|
419
|
+
return !a.startsWith('-') && a !== process.argv[0] && a !== process.argv[1];
|
|
420
|
+
});
|
|
421
|
+
const results = generateSkills({ feedbackDir: feedbackDir || undefined, dryRun: dryRun });
|
|
422
|
+
|
|
423
|
+
if (results.length === 0) {
|
|
424
|
+
console.log('No clusters met the threshold for skill generation.');
|
|
425
|
+
} else {
|
|
426
|
+
console.log('Generated ' + results.length + ' skill(s):');
|
|
427
|
+
for (const r of results) {
|
|
428
|
+
console.log(' ' + r.skillName + ' — ' + r.ruleCount + ' rules, ' + r.evidenceCount + ' signals → ' + r.filePath);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (dryRun) console.log('(dry run — no files written)');
|
|
432
|
+
} catch (err) {
|
|
433
|
+
console.error('Error:', err.message);
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
module.exports = {
|
|
439
|
+
generateSkills,
|
|
440
|
+
generateSkillFromCluster,
|
|
441
|
+
discoverFeedbackDir,
|
|
442
|
+
clusterByTags,
|
|
443
|
+
extractTags,
|
|
444
|
+
parseFeedbackFile,
|
|
445
|
+
classifySignal,
|
|
446
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Skill Materializer (EvoSkill Phase 2)
|
|
4
|
+
*
|
|
5
|
+
* Takes a JSON proposal from Skill Proposer and 'materializes'
|
|
6
|
+
* a functional MCP tool definition + SKILL.md documentation.
|
|
7
|
+
*
|
|
8
|
+
* Ensures every skill has a standardized entry point for the agent.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { discoverFeedbackDir } = require('./skill-generator');
|
|
14
|
+
|
|
15
|
+
function materializeSkills(options = {}) {
|
|
16
|
+
const feedbackDir = options.feedbackDir || discoverFeedbackDir();
|
|
17
|
+
const proposalsDir = path.join(feedbackDir, 'skill-proposals');
|
|
18
|
+
const skillsOutDir = options.skillsOutDir || process.env.THUMBGATE_SKILLS_DIR || path.join(process.cwd(), 'skills');
|
|
19
|
+
|
|
20
|
+
if (!fs.existsSync(proposalsDir)) {
|
|
21
|
+
console.log('No proposals directory found.');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const proposals = fs.readdirSync(proposalsDir)
|
|
26
|
+
.filter(f => f.endsWith('.json'))
|
|
27
|
+
.map(f => JSON.parse(fs.readFileSync(path.join(proposalsDir, f), 'utf-8')));
|
|
28
|
+
|
|
29
|
+
if (proposals.length === 0) {
|
|
30
|
+
console.log('No pending skill proposals.');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!fs.existsSync(skillsOutDir)) fs.mkdirSync(skillsOutDir, { recursive: true });
|
|
35
|
+
|
|
36
|
+
const results = [];
|
|
37
|
+
|
|
38
|
+
for (const proposal of proposals) {
|
|
39
|
+
if (proposal.status !== 'pending') continue;
|
|
40
|
+
|
|
41
|
+
const skillName = proposal.suggestedSkill.name;
|
|
42
|
+
const skillDir = path.join(skillsOutDir, skillName);
|
|
43
|
+
if (!fs.existsSync(skillDir)) fs.mkdirSync(skillDir, { recursive: true });
|
|
44
|
+
|
|
45
|
+
// Generate SKILL.md
|
|
46
|
+
const skillMd = generateSkillMarkdown(proposal);
|
|
47
|
+
fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillMd);
|
|
48
|
+
|
|
49
|
+
// Generate functional tool code (template)
|
|
50
|
+
const toolCode = generateToolCode(proposal);
|
|
51
|
+
fs.writeFileSync(path.join(skillDir, 'tool.js'), toolCode);
|
|
52
|
+
|
|
53
|
+
// Update proposal status
|
|
54
|
+
proposal.status = 'materialized';
|
|
55
|
+
proposal.materializedAt = new Date().toISOString();
|
|
56
|
+
fs.writeFileSync(
|
|
57
|
+
path.join(proposalsDir, `${skillName}.json`),
|
|
58
|
+
JSON.stringify(proposal, null, 2)
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
results.push(skillName);
|
|
62
|
+
console.log(`Materialized skill: ${skillName} -> ${skillDir}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return results;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function generateSkillMarkdown(proposal) {
|
|
69
|
+
const { suggestedSkill, problem, diagnosis } = proposal;
|
|
70
|
+
return `---
|
|
71
|
+
name: ${suggestedSkill.name}
|
|
72
|
+
description: ${suggestedSkill.description}
|
|
73
|
+
diagnosis: ${diagnosis}
|
|
74
|
+
status: materialized
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
# ${suggestedSkill.name.toUpperCase()} Capability
|
|
78
|
+
|
|
79
|
+
## Problem
|
|
80
|
+
${problem}
|
|
81
|
+
|
|
82
|
+
## Automated Diagnosis
|
|
83
|
+
${diagnosis}
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
The agent should call the \`${suggestedSkill.toolSpec.name}\` tool when tasks involve \`${suggestedSkill.tags.join(', ')}\`.
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function generateToolCode(proposal) {
|
|
91
|
+
const { suggestedSkill } = proposal;
|
|
92
|
+
const toolName = suggestedSkill.toolSpec.name;
|
|
93
|
+
|
|
94
|
+
return `/**
|
|
95
|
+
* Automated Skill: ${suggestedSkill.name}
|
|
96
|
+
* Generated: ${new Date().toISOString()}
|
|
97
|
+
*
|
|
98
|
+
* This tool was materialized by the EvoSkill loop to address:
|
|
99
|
+
* "${proposal.problem}"
|
|
100
|
+
*/
|
|
101
|
+
|
|
102
|
+
const { execSync } = require('child_process');
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* ${suggestedSkill.toolSpec.description}
|
|
106
|
+
*/
|
|
107
|
+
async function ${toolName}(args) {
|
|
108
|
+
const { context } = args;
|
|
109
|
+
|
|
110
|
+
// LOGIC: Materialized code should implement the fix derived from the diagnosis.
|
|
111
|
+
// For now, we provide a structured wrapper that logs intent and applies
|
|
112
|
+
// the suggested corrective action.
|
|
113
|
+
|
|
114
|
+
console.log(\`[EVOSKILL] Executing ${toolName} to resolve: ${proposal.problem}\`);
|
|
115
|
+
|
|
116
|
+
// Corrective action placeholder - in a full loop, this would be LLM-generated code
|
|
117
|
+
// derived from the 'how-to-avoid' fields in memory-log.jsonl.
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
status: 'success',
|
|
121
|
+
appliedFix: \`Automated handling of ${proposal.problem} pattern.\`,
|
|
122
|
+
context: context
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = { ${toolName} };
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (require.main === module) {
|
|
131
|
+
materializeSkills();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
module.exports = { materializeSkills };
|