@heretek-ai/epistemic-swarm 0.1.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.claude-plugin/marketplace.json +36 -9
  2. package/.claude-plugin/plugin.json +47 -41
  3. package/MARKETPLACE.md +93 -0
  4. package/README.md +117 -74
  5. package/bin/cli.js +73 -8
  6. package/config/mcp_launcher.py +215 -0
  7. package/config/opencode-snippet.json +48 -0
  8. package/extensions/pi/index.js +90 -0
  9. package/hooks/hooks.json +24 -0
  10. package/package.json +52 -5
  11. package/plugins/opencode/index.js +149 -0
  12. package/plugins/research-cache/.claude-plugin/plugin.json +15 -0
  13. package/plugins/socratic-grilling/.claude-plugin/plugin.json +15 -0
  14. package/plugins/socratic-grilling/skills/grilling/SKILL.md +48 -0
  15. package/plugins/socratic-grilling/skills/grilling/__init__.py +0 -0
  16. package/plugins/socratic-grilling/skills/grilling/socratic_tree.py +148 -0
  17. package/prompts/agent_code_auditor.md +88 -0
  18. package/prompts/agent_oss_scout.md +78 -0
  19. package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
  20. package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
  21. package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
  22. package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
  23. package/runner/research_swarm.py +230 -81
  24. package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
  25. package/runner/tests/test_swarm.py +66 -0
  26. package/skills/code_audit/SKILL.md +45 -0
  27. package/skills/epistemic_search/SKILL.md +35 -0
  28. package/skills/epistemic_search/__init__.py +1 -0
  29. package/skills/epistemic_search/scripts/fetch.py +157 -0
  30. package/skills/epistemic_search/scripts/search.py +162 -0
  31. package/skills/oss_scout/SKILL.md +49 -0
  32. package/skills/research_cache/SKILL.md +36 -0
  33. package/skills/research_cache/__init__.py +0 -0
  34. package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
  35. package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
  36. package/skills/research_cache/hasher.py +195 -0
  37. package/skills/swarm_config/SKILL.md +72 -0
  38. package/skills/swarm_config/__init__.py +4 -0
  39. package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
  40. package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
  41. package/skills/swarm_config/configure.py +167 -0
  42. package/runner/__pycache__/__init__.cpython-314.pyc +0 -0
  43. package/runner/__pycache__/auditor_engine.cpython-314.pyc +0 -0
  44. package/runner/__pycache__/research_swarm.cpython-314.pyc +0 -0
  45. package/runner/__pycache__/state_machine.cpython-314.pyc +0 -0
  46. package/runner/tests/__pycache__/test_swarm.cpython-314.pyc +0 -0
  47. package/skills/research-cache/__pycache__/__init__.cpython-314.pyc +0 -0
  48. package/skills/research-cache/__pycache__/hasher.cpython-314.pyc +0 -0
  49. /package/{skills/research-cache → plugins/research-cache/skills/research_cache}/SKILL.md +0 -0
  50. /package/{skills/research-cache → plugins/research-cache/skills/research_cache}/__init__.py +0 -0
  51. /package/{skills/research-cache → plugins/research-cache/skills/research_cache}/hasher.py +0 -0
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Intelligent MCP Server Launcher & Epistemic Fallback Proxy for IUMBTEMS.
4
+ Safely detects API keys and endpoints:
5
+ - If keys are present (via env or userConfig): launches the official MCP server.
6
+ - If keys are absent: smoothly provides a zero-key JSON-RPC fallback MCP server
7
+ backed by DuckDuckGo Lite search and content-addressed SHA256 caching.
8
+ Prevents startup crashes and missing environment variable errors in Claude Code.
9
+ """
10
+
11
+ import sys
12
+ import os
13
+ import json
14
+ import subprocess
15
+ import shutil
16
+ from typing import Dict, Any, List
17
+
18
+ # Add parent dir to path for imports
19
+ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
20
+ PROJECT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, ".."))
21
+ if PROJECT_ROOT not in sys.path:
22
+ sys.path.insert(0, PROJECT_ROOT)
23
+
24
+ def get_config_val(key: str, default: str = "") -> str:
25
+ """Retrieve config from env, userConfig, or defaults."""
26
+ env_val = os.environ.get(key)
27
+ if env_val:
28
+ return env_val
29
+ # Also check user_config format
30
+ user_cfg_val = os.environ.get(f"CLAUDE_PLUGIN_OPTION_{key.upper()}")
31
+ if user_cfg_val:
32
+ return user_cfg_val
33
+ return default
34
+
35
+ def run_zero_key_mcp_server(service: str):
36
+ """Run lightweight JSON-RPC MCP server handling search and fetch without API keys."""
37
+ from skills.epistemic_search.scripts.search import search_duckduckgo
38
+ from skills.epistemic_search.scripts.fetch import fetch_and_cache
39
+
40
+ def send_response(response: Dict[str, Any]):
41
+ out = json.dumps(response)
42
+ sys.stdout.write(f"Content-Length: {len(out)}\r\n\r\n{out}")
43
+ sys.stdout.flush()
44
+
45
+ def send_json(response: Dict[str, Any]):
46
+ # Direct newline JSON-RPC for stdio transports
47
+ sys.stdout.write(json.dumps(response) + "\n")
48
+ sys.stdout.flush()
49
+
50
+ while True:
51
+ try:
52
+ line = sys.stdin.readline()
53
+ if not line:
54
+ break
55
+ line = line.strip()
56
+ if not line:
57
+ continue
58
+
59
+ # Check if line is Content-Length header or raw JSON
60
+ if line.startswith("Content-Length:"):
61
+ length = int(line.split(":")[1].strip())
62
+ # Read blank line
63
+ sys.stdin.readline()
64
+ body = sys.stdin.read(length)
65
+ req = json.loads(body)
66
+ use_headers = True
67
+ else:
68
+ req = json.loads(line)
69
+ use_headers = False
70
+
71
+ req_id = req.get("id")
72
+ method = req.get("method")
73
+ params = req.get("params", {})
74
+
75
+ if method == "initialize":
76
+ res = {
77
+ "jsonrpc": "2.0",
78
+ "id": req_id,
79
+ "result": {
80
+ "protocolVersion": "2024-11-05",
81
+ "serverInfo": {
82
+ "name": f"epistemic-{service}-fallback",
83
+ "version": "0.2.1"
84
+ },
85
+ "capabilities": {
86
+ "tools": {}
87
+ }
88
+ }
89
+ }
90
+ elif method == "tools/list":
91
+ if service == "brave-search":
92
+ tools = [
93
+ {
94
+ "name": "brave_web_search",
95
+ "description": "Web search with DuckDuckGo fallback (zero API key required). Returns verifiable search results with titles, URLs, and snippets.",
96
+ "inputSchema": {
97
+ "type": "object",
98
+ "properties": {
99
+ "query": {"type": "string", "description": "The search query terms"},
100
+ "count": {"type": "number", "description": "Number of results (1-20)", "default": 10}
101
+ },
102
+ "required": ["query"]
103
+ }
104
+ }
105
+ ]
106
+ elif service == "firecrawl":
107
+ tools = [
108
+ {
109
+ "name": "firecrawl_scrape",
110
+ "description": "Scrape and extract clean markdown with automatic content-addressed SHA-256 caching into .research/sources/.",
111
+ "inputSchema": {
112
+ "type": "object",
113
+ "properties": {
114
+ "url": {"type": "string", "description": "URL to scrape and cache"}
115
+ },
116
+ "required": ["url"]
117
+ }
118
+ }
119
+ ]
120
+ else:
121
+ tools = []
122
+
123
+ res = {
124
+ "jsonrpc": "2.0",
125
+ "id": req_id,
126
+ "result": {"tools": tools}
127
+ }
128
+ elif method == "tools/call":
129
+ tool_name = params.get("name")
130
+ args = params.get("arguments", {})
131
+
132
+ if tool_name in ("brave_web_search", "search"):
133
+ q = args.get("query", "")
134
+ count = args.get("count", 10)
135
+ results = search_duckduckgo(query=q, max_results=count)
136
+ text_out = json.dumps(results, indent=2)
137
+ res = {
138
+ "jsonrpc": "2.0",
139
+ "id": req_id,
140
+ "result": {
141
+ "content": [{"type": "text", "text": text_out}]
142
+ }
143
+ }
144
+ elif tool_name in ("firecrawl_scrape", "scrape", "fetch"):
145
+ target_url = args.get("url", "")
146
+ content = fetch_and_cache(url=target_url)
147
+ res = {
148
+ "jsonrpc": "2.0",
149
+ "id": req_id,
150
+ "result": {
151
+ "content": [{"type": "text", "text": content}]
152
+ }
153
+ }
154
+ else:
155
+ res = {
156
+ "jsonrpc": "2.0",
157
+ "id": req_id,
158
+ "error": {"code": -32601, "message": f"Tool {tool_name} not found"}
159
+ }
160
+ elif method == "notifications/initialized":
161
+ continue
162
+ else:
163
+ res = {
164
+ "jsonrpc": "2.0",
165
+ "id": req_id,
166
+ "error": {"code": -32601, "message": f"Method {method} not handled"}
167
+ }
168
+
169
+ if use_headers:
170
+ send_response(res)
171
+ else:
172
+ send_json(res)
173
+
174
+ except Exception as e:
175
+ sys.stderr.write(f"[mcp-launcher] Error handling request: {e}\n")
176
+ break
177
+
178
+ def main():
179
+ service = sys.argv[1] if len(sys.argv) > 1 else "brave-search"
180
+ npx_bin = shutil.which("npx")
181
+
182
+ if service == "brave-search":
183
+ api_key = get_config_val("BRAVE_SEARCH_API_KEY")
184
+ if api_key and npx_bin:
185
+ os.environ["BRAVE_SEARCH_API_KEY"] = api_key
186
+ os.execvp(npx_bin, [npx_bin, "-y", "@modelcontextprotocol/server-brave-search"])
187
+ else:
188
+ # Fall back to zero-key search proxy
189
+ run_zero_key_mcp_server("brave-search")
190
+
191
+ elif service == "firecrawl":
192
+ api_key = get_config_val("FIRECRAWL_API_KEY")
193
+ api_url = get_config_val("FIRECRAWL_API_URL", "https://api.firecrawl.dev")
194
+ if api_key and npx_bin:
195
+ os.environ["FIRECRAWL_API_KEY"] = api_key
196
+ os.environ["FIRECRAWL_API_URL"] = api_url
197
+ os.execvp(npx_bin, [npx_bin, "-y", "firecrawl-mcp"])
198
+ else:
199
+ # Fall back to zero-key readability fetcher + SHA-256 hasher
200
+ run_zero_key_mcp_server("firecrawl")
201
+
202
+ elif service == "searxng":
203
+ searxng_url = get_config_val("SEARXNG_URL", "http://localhost:8080")
204
+ searxng_script = os.path.join(SCRIPT_DIR, "searxng_mcp.py")
205
+ if os.path.exists(searxng_script):
206
+ os.environ["SEARXNG_URL"] = searxng_url
207
+ os.execvp("python3", ["python3", searxng_script])
208
+ else:
209
+ run_zero_key_mcp_server("searxng")
210
+ else:
211
+ sys.stderr.write(f"Unknown service: {service}\n")
212
+ sys.exit(1)
213
+
214
+ if __name__ == "__main__":
215
+ main()
@@ -0,0 +1,48 @@
1
+ {
2
+ "$schema": "https://opencode.ai/config.json",
3
+ "plugin": [
4
+ "@heretek-ai/epistemic-swarm"
5
+ ],
6
+ "agent": {
7
+ "alpha-thesis": {
8
+ "description": "IUMBTEMS Proponent Agent: Gathers corroborating primary sources, benchmark results, and empirical proofs.",
9
+ "tools": {
10
+ "bash": true,
11
+ "read": true,
12
+ "write": true,
13
+ "grep": true,
14
+ "glob": true,
15
+ "task": true,
16
+ "iumbtems_verify_quote": true
17
+ }
18
+ },
19
+ "beta-redteam": {
20
+ "description": "IUMBTEMS Adversary Agent: Active falsification, red-teaming, hunting for counter-benchmarks, and edge-case failure modes.",
21
+ "tools": {
22
+ "bash": true,
23
+ "read": true,
24
+ "write": true,
25
+ "grep": true,
26
+ "glob": true,
27
+ "task": true,
28
+ "iumbtems_verify_quote": true
29
+ }
30
+ },
31
+ "epistemic-auditor": {
32
+ "description": "IUMBTEMS Synthesizer & Auditor: Verifies verbatim source quotes, downgrades unverified claims, and calculates divergence.",
33
+ "tools": {
34
+ "read": true,
35
+ "write": true,
36
+ "grep": true,
37
+ "glob": true,
38
+ "iumbtems_verify_quote": true
39
+ }
40
+ }
41
+ },
42
+ "skills": {
43
+ "paths": [
44
+ "./skills/grilling",
45
+ "./skills/research-cache"
46
+ ]
47
+ }
48
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * IUMBTEMS: I Use My Brain To Express My Self
3
+ * Native Extension for Pi (pi.dev)
4
+ *
5
+ * Exposes /grill and /swarm slash commands and epistemic verification tools in Pi.
6
+ */
7
+
8
+ import { spawnSync } from 'child_process';
9
+ import path from 'path';
10
+ import { fileURLToPath } from 'url';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+ const PKG_ROOT = path.resolve(__dirname, '../..');
15
+
16
+ export default function initPiExtension(pi) {
17
+ // Check if Pi extension API is available
18
+ if (!pi) return;
19
+
20
+ // 1. Register /swarm slash command
21
+ if (typeof pi.registerCommand === 'function') {
22
+ pi.registerCommand('swarm', {
23
+ description: 'Run IUMBTEMS dialectic research swarm on an objective',
24
+ usage: '/swarm <objective>',
25
+ handler: async (args, ctx) => {
26
+ const objective = args.trim();
27
+ if (!objective) {
28
+ ctx.output?.('⚠️ Please provide a research objective. Example: /swarm "ZK prover latency bounds"');
29
+ return;
30
+ }
31
+
32
+ ctx.output?.(`🌟 [IUMBTEMS] Dispatching Dialectic Swarm: "${objective}"...`);
33
+ const runnerPath = path.join(PKG_ROOT, 'runner/research_swarm.py');
34
+ const res = spawnSync('python3', [runnerPath, '--objective', objective], {
35
+ encoding: 'utf-8',
36
+ cwd: ctx.cwd || process.cwd(),
37
+ env: { ...process.env, PYTHONPATH: PKG_ROOT }
38
+ });
39
+
40
+ if (res.status === 0) {
41
+ ctx.output?.(res.stdout);
42
+ ctx.output?.('\n✅ Research complete! Summary written to .research/final_synthesis.md');
43
+ } else {
44
+ ctx.output?.(`❌ Swarm failed:\n${res.stderr || res.stdout}`);
45
+ }
46
+ }
47
+ });
48
+
49
+ // 2. Register /grill slash command
50
+ pi.registerCommand('grill', {
51
+ description: 'Launch Socratic grilling and decision tree frontier exploration',
52
+ usage: '/grill [--objective <text>]',
53
+ handler: async (args, ctx) => {
54
+ ctx.output?.('🧠 [IUMBTEMS] Launching Socratic Grilling decision tree...');
55
+ const treePath = path.join(PKG_ROOT, 'skills/grilling/socratic_tree.py');
56
+ const forwardArgs = args.trim() ? ['--objective', args.trim()] : ['--show-frontier'];
57
+ const res = spawnSync('python3', [treePath, ...forwardArgs], {
58
+ encoding: 'utf-8',
59
+ cwd: ctx.cwd || process.cwd()
60
+ });
61
+ ctx.output?.(res.stdout || res.stderr);
62
+ }
63
+ });
64
+ }
65
+
66
+ // 3. Register tool for epistemic quote verification
67
+ if (typeof pi.registerTool === 'function') {
68
+ pi.registerTool({
69
+ name: 'iumbtems_verify_quote',
70
+ description: 'Verify that an empirical claim quote matches verbatim text in the source cache',
71
+ parameters: {
72
+ type: 'object',
73
+ properties: {
74
+ hash: { type: 'string', description: 'SHA-256 hash of cached source' },
75
+ quote: { type: 'string', description: 'Verbatim quote string to verify' }
76
+ },
77
+ required: ['hash', 'quote']
78
+ },
79
+ execute: async ({ hash, quote }) => {
80
+ const hasherPath = path.join(PKG_ROOT, 'skills/research-cache/hasher.py');
81
+ const res = spawnSync('python3', [hasherPath, 'verify', '--hash', hash, '--quote', quote], {
82
+ encoding: 'utf-8'
83
+ });
84
+ return {
85
+ content: [{ type: 'text', text: res.stdout || res.stderr }]
86
+ };
87
+ }
88
+ });
89
+ }
90
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "WebSearch",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "echo '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"WebSearch intercepted by epistemic-swarm to enforce verified citation integrity. Use epistemic search (python3 ${CLAUDE_PLUGIN_ROOT}/skills/epistemic_search/scripts/search.py \\\"<query>\\\") instead.\"}}'"
10
+ }
11
+ ]
12
+ },
13
+ {
14
+ "matcher": "WebFetch",
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "echo '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"WebFetch intercepted by epistemic-swarm to enforce content-addressed caching. Use epistemic fetch (python3 ${CLAUDE_PLUGIN_ROOT}/skills/epistemic_search/scripts/fetch.py \\\"<url>\\\") which automatically stores documents in .research/sources/<sha256>.md for mathematical auditability.\"}}'"
19
+ }
20
+ ]
21
+ }
22
+ ]
23
+ }
24
+ }
package/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "@heretek-ai/epistemic-swarm",
3
- "version": "0.1.0",
4
- "description": "Epistemic Swarm: High-Integrity Dialectic Research Agent Harness for Claude Code",
3
+ "version": "0.2.2",
4
+ "description": "IUMBTEMS: I Use My Brain To Express My Self — High-Integrity Dialectic Research Agent Harness for Claude Code, Pi (pi.dev), and OpenCode V2",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {
7
- "epistemic-swarm": "bin/cli.js",
8
- "iumbtems": "bin/cli.js"
7
+ "iumbtems": "bin/cli.js",
8
+ "epistemic-swarm": "bin/cli.js"
9
+ },
10
+ "exports": {
11
+ ".": "./bin/cli.js",
12
+ "./opencode": "./plugins/opencode/index.js",
13
+ "./server": "./plugins/opencode/index.js",
14
+ "./pi": "./extensions/pi/index.js"
9
15
  },
10
16
  "publishConfig": {
11
17
  "access": "public"
@@ -19,26 +25,66 @@
19
25
  },
20
26
  "homepage": "https://github.com/Heretek-AI/IUMBTEMS#readme",
21
27
  "keywords": [
28
+ "iumbtems",
29
+ "i-use-my-brain-to-express-my-self",
30
+ "pi-package",
31
+ "opencode",
32
+ "opencode-plugin",
22
33
  "claude-code",
23
34
  "ai-agents",
35
+ "autonomous-agents",
36
+ "multi-agent-systems",
24
37
  "dialectic-swarm",
38
+ "dialectic",
25
39
  "epistemic-integrity",
26
40
  "research-harness",
41
+ "research-agent",
27
42
  "mcp",
43
+ "model-context-protocol",
44
+ "mcp-servers",
28
45
  "deep-research",
29
- "socratic-grilling"
46
+ "socratic-grilling",
47
+ "socratic-questioning",
48
+ "fact-checking",
49
+ "red-teaming",
50
+ "citation-validation",
51
+ "hallucination-prevention",
52
+ "swarm-intelligence",
53
+ "code-audit",
54
+ "oss-scout",
55
+ "clean-room-engineering"
30
56
  ],
57
+ "pi": {
58
+ "extensions": [
59
+ "./extensions/pi/index.js"
60
+ ],
61
+ "skills": [
62
+ "./skills/grilling",
63
+ "./skills/research_cache",
64
+ "./skills/epistemic_search",
65
+ "./skills/swarm_config",
66
+ "./skills/code_audit",
67
+ "./skills/oss_scout"
68
+ ],
69
+ "prompts": [
70
+ "./prompts/*.md"
71
+ ]
72
+ },
31
73
  "author": "Heretek AI",
32
74
  "license": "Apache-2.0",
33
75
  "files": [
34
76
  "bin",
35
77
  "prompts",
36
78
  "skills",
79
+ "hooks",
37
80
  "config",
38
81
  "runner",
82
+ "extensions",
83
+ "plugins",
39
84
  ".claude-plugin",
40
85
  "install.sh",
41
86
  "README.md",
87
+ "MARKETPLACE.md",
42
88
  "LICENSE"
43
89
  ],
44
90
  "engines": {
@@ -46,6 +92,7 @@
46
92
  },
47
93
  "scripts": {
48
94
  "test": "python3 -m unittest discover -s runner/tests",
95
+ "validate:claude": "claude plugin validate --strict .claude-plugin/marketplace.json && claude plugin validate --strict .",
49
96
  "prepack": "python3 -m unittest discover -s runner/tests",
50
97
  "install-local": "bash install.sh"
51
98
  }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * IUMBTEMS: I Use My Brain To Express My Self
3
+ * Native Plugin for OpenCode V2 (opencode.ai)
4
+ *
5
+ * Exposes dialectic swarm research tools, Socratic decision frontier calculation,
6
+ * and content-addressed quote verification directly into OpenCode V2 agents.
7
+ */
8
+
9
+ import { spawnSync } from 'child_process';
10
+ import path from 'path';
11
+ import { fileURLToPath } from 'url';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+ const PKG_ROOT = path.resolve(__dirname, '../..');
16
+
17
+ export function createOpenCodePlugin(context = {}) {
18
+ return {
19
+ id: "heretek.iumbtems.epistemic-swarm",
20
+ name: "IUMBTEMS Epistemic Swarm",
21
+ version: "0.1.0",
22
+ description: "I Use My Brain To Express My Self: Dialectic research swarm enforcing empirical verification",
23
+
24
+ server: (toolContext) => [
25
+ {
26
+ name: "iumbtems_swarm_research",
27
+ description: "Execute an autonomous dialectic research swarm on an objective using Agent Alpha (Thesis) and Agent Beta (Red Team).",
28
+ input: {
29
+ type: "object",
30
+ properties: {
31
+ objective: {
32
+ type: "string",
33
+ description: "The research question or hypothesis to investigate"
34
+ },
35
+ mock_mode: {
36
+ type: "boolean",
37
+ description: "Run in mock/dry-run mode without external API charges",
38
+ default: false
39
+ }
40
+ },
41
+ required: ["objective"]
42
+ },
43
+ options: { codemode: false },
44
+ execute: async (args) => {
45
+ const runnerPath = path.join(PKG_ROOT, "runner/research_swarm.py");
46
+ const cmdArgs = [runnerPath, "--objective", args.objective];
47
+ if (args.mock_mode) {
48
+ cmdArgs.push("--mock-claude");
49
+ }
50
+
51
+ const res = spawnSync("python3", cmdArgs, {
52
+ encoding: "utf-8",
53
+ cwd: toolContext?.cwd || process.cwd(),
54
+ env: { ...process.env, PYTHONPATH: PKG_ROOT }
55
+ });
56
+
57
+ return {
58
+ content: res.stdout || res.stderr,
59
+ status: res.status === 0 ? "success" : "error"
60
+ };
61
+ }
62
+ },
63
+
64
+ {
65
+ name: "iumbtems_verify_quote",
66
+ description: "Cryptographically verify whether a cited quote matches verbatim text inside the content-addressed source cache.",
67
+ input: {
68
+ type: "object",
69
+ properties: {
70
+ hash: {
71
+ type: "string",
72
+ description: "SHA-256 hash or prefix of the cached source"
73
+ },
74
+ quote: {
75
+ type: "string",
76
+ description: "The verbatim excerpt to verify"
77
+ }
78
+ },
79
+ required: ["hash", "quote"]
80
+ },
81
+ options: { codemode: false },
82
+ execute: async (args) => {
83
+ const hasherPath = path.join(PKG_ROOT, "skills/research-cache/hasher.py");
84
+ const res = spawnSync("python3", [hasherPath, "verify", "--hash", args.hash, "--quote", args.quote], {
85
+ encoding: "utf-8",
86
+ cwd: toolContext?.cwd || process.cwd()
87
+ });
88
+
89
+ return {
90
+ content: res.stdout || res.stderr,
91
+ status: res.status === 0 ? "verified" : "failed"
92
+ };
93
+ }
94
+ },
95
+
96
+ {
97
+ name: "iumbtems_socratic_frontier",
98
+ description: "Inspect or advance the Socratic grilling decision frontier for research framing.",
99
+ input: {
100
+ type: "object",
101
+ properties: {
102
+ objective: {
103
+ type: "string",
104
+ description: "Research objective to inspect"
105
+ },
106
+ settle_node: {
107
+ type: "string",
108
+ description: "Optional node ID to settle with an answer"
109
+ },
110
+ settle_answer: {
111
+ type: "string",
112
+ description: "Answer for the settled node"
113
+ }
114
+ }
115
+ },
116
+ options: { codemode: false },
117
+ execute: async (args) => {
118
+ const treePath = path.join(PKG_ROOT, "skills/grilling/socratic_tree.py");
119
+ const cmdArgs = [treePath];
120
+
121
+ if (args.settle_node && args.settle_answer) {
122
+ cmdArgs.push("--settle", args.settle_node, args.settle_answer);
123
+ } else if (args.objective) {
124
+ cmdArgs.push("--objective", args.objective, "--show-frontier");
125
+ } else {
126
+ cmdArgs.push("--show-frontier");
127
+ }
128
+
129
+ const res = spawnSync("python3", cmdArgs, {
130
+ encoding: "utf-8",
131
+ cwd: toolContext?.cwd || process.cwd()
132
+ });
133
+
134
+ return {
135
+ content: res.stdout || res.stderr
136
+ };
137
+ }
138
+ }
139
+ ],
140
+
141
+ setup: async (appContext) => {
142
+ // OpenCode V2 lifecycle hook
143
+ return { initialized: true, platform: "opencode-v2" };
144
+ }
145
+ };
146
+ }
147
+
148
+ const defaultPlugin = createOpenCodePlugin();
149
+ export default defaultPlugin;
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "research-cache",
3
+ "version": "0.2.0",
4
+ "description": "Content-addressed SHA256 Markdown source hashing and verbatim quote verification engine.",
5
+ "author": {
6
+ "name": "Heretek AI",
7
+ "email": "dev@heretek.ai"
8
+ },
9
+ "license": "Apache-2.0",
10
+ "repository": "https://github.com/Heretek-AI/IUMBTEMS",
11
+ "homepage": "https://github.com/Heretek-AI/IUMBTEMS#readme",
12
+ "skills": [
13
+ "./skills/research_cache"
14
+ ]
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "socratic-grilling",
3
+ "version": "0.2.0",
4
+ "description": "Matt Pocock-style Socratic interrogation, premise inversion, and lateral exploration skill for Claude Code.",
5
+ "author": {
6
+ "name": "Heretek AI",
7
+ "email": "dev@heretek.ai"
8
+ },
9
+ "license": "Apache-2.0",
10
+ "repository": "https://github.com/Heretek-AI/IUMBTEMS",
11
+ "homepage": "https://github.com/Heretek-AI/IUMBTEMS#readme",
12
+ "skills": [
13
+ "./skills/grilling"
14
+ ]
15
+ }