@heretek-ai/epistemic-swarm 0.4.7 → 0.4.8
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/plugin.json +1 -1
- package/bin/cli.js +59 -7
- package/config/mcp_launcher.py +75 -48
- package/extensions/pi/index.js +25 -5
- package/install.sh +5 -5
- package/package.json +1 -1
- package/plugins/antigravity/plugin.json +1 -1
- package/plugins/gemini/gemini-extension.json +1 -1
- package/plugins/opencode/index.js +308 -142
- package/plugins/opencode/tui.js +7 -9
- package/plugins/research-cache/skills/research_cache/hasher.py +27 -9
- package/plugins/socratic-grilling/skills/grilling/socratic_tree.py +26 -18
- package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
- package/runner/__pycache__/auctioneer.cpython-311.pyc +0 -0
- package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
- package/runner/__pycache__/claim_store.cpython-311.pyc +0 -0
- package/runner/__pycache__/claim_witness.cpython-311.pyc +0 -0
- package/runner/__pycache__/living_dossiers.cpython-311.pyc +0 -0
- package/runner/__pycache__/mcp_protocol.cpython-311.pyc +0 -0
- package/runner/__pycache__/mcp_server.cpython-311.pyc +0 -0
- package/runner/__pycache__/path_safety.cpython-311.pyc +0 -0
- package/runner/__pycache__/pcrb.cpython-311.pyc +0 -0
- package/runner/__pycache__/pcrb_verify.cpython-311.pyc +0 -0
- package/runner/__pycache__/refinement.cpython-311.pyc +0 -0
- package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
- package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
- package/runner/auditor_engine.py +122 -102
- package/runner/claim_store.py +5 -4
- package/runner/living_dossiers.py +32 -10
- package/runner/mcp_protocol.py +1 -1
- package/runner/mcp_server.py +23 -11
- package/runner/path_safety.py +62 -0
- package/runner/pcrb.py +68 -39
- package/runner/pcrb_verify.py +82 -52
- package/runner/refinement.py +61 -47
- package/runner/research_swarm.py +61 -7
- package/runner/state_machine.py +9 -6
- package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_bet1_spike.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_claim_store.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_claim_witness.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_opencode_ux.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_pcrb.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_refinement.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_sweep_regressions.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_webcache.cpython-311.pyc +0 -0
- package/runner/tests/test_mcp_server.py +0 -1
- package/runner/tests/test_opencode_ux.py +368 -33
- package/runner/tests/test_swarm.py +20 -9
- package/runner/tests/test_sweep_regressions.py +373 -0
- package/scripts/__pycache__/bet1_advisory_spike.cpython-311.pyc +0 -0
- package/scripts/__pycache__/build_adapters.cpython-311.pyc +0 -0
- package/scripts/__pycache__/divergence_experiment.cpython-311.pyc +0 -0
- package/scripts/auction_experiment.py +38 -30
- package/scripts/bet1_advisory_spike.py +41 -25
- package/scripts/build_adapters.py +52 -37
- package/scripts/divergence_experiment.py +61 -31
- package/skills/brainstorming/scripts/brainstorm.py +3 -2
- package/skills/epistemic_search/scripts/__pycache__/search.cpython-311.pyc +0 -0
- package/skills/epistemic_search/scripts/fetch.py +0 -1
- package/skills/epistemic_search/scripts/search.py +202 -73
- package/skills/epistemic_search/scripts/webcache.py +1 -1
- package/skills/grilling/socratic_tree.py +26 -18
- package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
- package/skills/research_cache/hasher.py +27 -9
- package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
- package/skills/swarm_config/configure.py +32 -28
package/bin/cli.js
CHANGED
|
@@ -5,9 +5,28 @@
|
|
|
5
5
|
* High-Integrity Dialectic Research Harness for Claude Code, Pi, OMP, OpenCode V2, Gemini CLI, Codex CLI, AntiGravity.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const { spawnSync } = require('child_process');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const fs = require('fs');
|
|
8
|
+
const { spawnSync } = require('node:child_process');
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
const fs = require('node:fs');
|
|
11
|
+
|
|
12
|
+
function resolveBinary(name) {
|
|
13
|
+
const pathDirs = (process.env.PATH || '').split(path.delimiter);
|
|
14
|
+
for (const dir of pathDirs) {
|
|
15
|
+
if (!dir || dir === '.' || dir.startsWith('./') || dir.startsWith('../')) continue;
|
|
16
|
+
const full = path.join(dir, name);
|
|
17
|
+
try {
|
|
18
|
+
// Match Node's own PATH search: skip non-files and non-executables so a
|
|
19
|
+
// directory or a non-executable named `python3` earlier on PATH cannot
|
|
20
|
+
// shadow the real binary.
|
|
21
|
+
if (!fs.statSync(full).isFile()) continue;
|
|
22
|
+
fs.accessSync(full, fs.constants.X_OK);
|
|
23
|
+
return fs.realpathSync(full);
|
|
24
|
+
} catch {
|
|
25
|
+
/* not present, not a file, or not executable — keep searching */
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return name;
|
|
29
|
+
}
|
|
11
30
|
|
|
12
31
|
const PKG_ROOT = path.resolve(__dirname, '..');
|
|
13
32
|
const args = process.argv.slice(2);
|
|
@@ -74,7 +93,7 @@ function runPython(scriptRelPath, extraArgs = []) {
|
|
|
74
93
|
const scriptPath = path.join(PKG_ROOT, scriptRelPath);
|
|
75
94
|
finalArgs = [scriptPath, ...extraArgs];
|
|
76
95
|
}
|
|
77
|
-
const result = spawnSync('python3', finalArgs, {
|
|
96
|
+
const result = spawnSync(resolveBinary('python3'), finalArgs, {
|
|
78
97
|
stdio: 'inherit',
|
|
79
98
|
cwd: process.cwd(),
|
|
80
99
|
env: { ...process.env, PYTHONPATH: PKG_ROOT }
|
|
@@ -84,7 +103,7 @@ function runPython(scriptRelPath, extraArgs = []) {
|
|
|
84
103
|
|
|
85
104
|
function runBash(scriptRelPath, extraArgs = []) {
|
|
86
105
|
const scriptPath = path.join(PKG_ROOT, scriptRelPath);
|
|
87
|
-
const result = spawnSync('bash', [scriptPath, ...extraArgs], {
|
|
106
|
+
const result = spawnSync(resolveBinary('bash'), [scriptPath, ...extraArgs], {
|
|
88
107
|
stdio: 'inherit',
|
|
89
108
|
cwd: process.cwd()
|
|
90
109
|
});
|
|
@@ -186,18 +205,51 @@ switch (command) {
|
|
|
186
205
|
|
|
187
206
|
case 'doctor': {
|
|
188
207
|
console.log('🔍 Checking Epistemic Swarm Environment:\n');
|
|
189
|
-
|
|
208
|
+
|
|
209
|
+
let pkgVersion = 'unknown';
|
|
210
|
+
try {
|
|
211
|
+
pkgVersion = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8')).version || 'unknown';
|
|
212
|
+
} catch { /* keep 'unknown' */ }
|
|
213
|
+
console.log(`✅ epistemic-swarm: ${pkgVersion}`);
|
|
214
|
+
|
|
215
|
+
const claudeCheck = spawnSync(resolveBinary('claude'), ['--version'], { encoding: 'utf-8' });
|
|
190
216
|
if (claudeCheck.status === 0) {
|
|
191
217
|
console.log(`✅ Claude Code: ${claudeCheck.stdout.trim()}`);
|
|
192
218
|
} else {
|
|
193
219
|
console.log('⚠️ Claude Code CLI not found in PATH (Install via npm i -g @anthropic-ai/claude-code)');
|
|
194
220
|
}
|
|
195
221
|
|
|
196
|
-
const pyCheck = spawnSync('python3', ['--version'], { encoding: 'utf-8' });
|
|
222
|
+
const pyCheck = spawnSync(resolveBinary('python3'), ['--version'], { encoding: 'utf-8' });
|
|
197
223
|
console.log(`✅ Python: ${pyCheck.stdout ? pyCheck.stdout.trim() : 'python3 missing'}`);
|
|
198
224
|
|
|
199
225
|
console.log(`✅ Node.js: ${process.version}`);
|
|
200
226
|
console.log(`📁 Package Root: ${PKG_ROOT}`);
|
|
227
|
+
|
|
228
|
+
// opencode resolves this package as `@latest` into a per-fetch cache and
|
|
229
|
+
// keeps every generation. A session started against an old generation keeps
|
|
230
|
+
// running the old code until it is refreshed, so report what is cached.
|
|
231
|
+
try {
|
|
232
|
+
const cacheRoot = path.join(process.env.HOME || '', '.cache', 'opencode', 'npm', '@heretek-ai');
|
|
233
|
+
const pkgDir = path.join(cacheRoot, 'epistemic-swarm@latest');
|
|
234
|
+
if (fs.existsSync(pkgDir)) {
|
|
235
|
+
const generations = fs.readdirSync(pkgDir).filter((d) => /^\d+$/.test(d)).sort();
|
|
236
|
+
const newest = generations[generations.length - 1];
|
|
237
|
+
console.log(`\n🧩 OpenCode plugin cache: ${generations.length} generation(s) in ${pkgDir}`);
|
|
238
|
+
for (const g of generations.slice(-5)) {
|
|
239
|
+
const marker = g === newest ? ' <- newest' : '';
|
|
240
|
+
console.log(` - ${g}${marker}`);
|
|
241
|
+
}
|
|
242
|
+
if (generations.length > 1) {
|
|
243
|
+
console.log(' ⚠️ Multiple generations cached. If slash commands vanish or the plugin');
|
|
244
|
+
console.log(' misbehaves, the running session may hold an older one — refresh with');
|
|
245
|
+
console.log(' `opencode plugin update` (or clear the cache dir) and relaunch.');
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
console.log('\n🧩 OpenCode plugin cache: not present (plugin not yet fetched)');
|
|
249
|
+
}
|
|
250
|
+
} catch (err) {
|
|
251
|
+
console.log(`\n🧩 OpenCode plugin cache: could not inspect (${err.message})`);
|
|
252
|
+
}
|
|
201
253
|
break;
|
|
202
254
|
}
|
|
203
255
|
|
package/config/mcp_launcher.py
CHANGED
|
@@ -8,12 +8,11 @@ Safely detects API keys and endpoints:
|
|
|
8
8
|
Prevents startup crashes and missing environment variable errors in Claude Code.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
import sys
|
|
12
|
-
import os
|
|
13
11
|
import json
|
|
14
|
-
import
|
|
12
|
+
import os
|
|
15
13
|
import shutil
|
|
16
|
-
|
|
14
|
+
import sys
|
|
15
|
+
from typing import Any, Dict, List, Optional
|
|
17
16
|
|
|
18
17
|
# Add parent dir to path for imports
|
|
19
18
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
@@ -21,32 +20,30 @@ PROJECT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, ".."))
|
|
|
21
20
|
if PROJECT_ROOT not in sys.path:
|
|
22
21
|
sys.path.insert(0, PROJECT_ROOT)
|
|
23
22
|
|
|
23
|
+
|
|
24
24
|
def get_config_val(key: str, default: str = "") -> str:
|
|
25
25
|
"""Retrieve config from env, userConfig, or defaults."""
|
|
26
26
|
env_val = os.environ.get(key)
|
|
27
27
|
if env_val:
|
|
28
28
|
return env_val
|
|
29
|
-
# Also check user_config format
|
|
30
29
|
user_cfg_val = os.environ.get(f"CLAUDE_PLUGIN_OPTION_{key.upper()}")
|
|
31
30
|
if user_cfg_val:
|
|
32
31
|
return user_cfg_val
|
|
33
32
|
return default
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
|
|
35
|
+
def _build_brave_tools() -> list:
|
|
36
|
+
from runner.mcp_protocol import ToolSpec
|
|
37
37
|
from skills.epistemic_search.scripts.search import search_duckduckgo
|
|
38
|
-
from skills.epistemic_search.scripts.fetch import fetch_and_cache
|
|
39
|
-
from runner.mcp_protocol import StdioJsonRpcServer, ToolSpec
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
)
|
|
47
|
-
return json.dumps(results, indent=2)
|
|
39
|
+
def handle_brave_search(args: Dict[str, Any]) -> str:
|
|
40
|
+
results = search_duckduckgo(
|
|
41
|
+
query=args.get("query", ""), max_results=args.get("count", 10)
|
|
42
|
+
)
|
|
43
|
+
return json.dumps(results, indent=2)
|
|
48
44
|
|
|
49
|
-
|
|
45
|
+
return [
|
|
46
|
+
ToolSpec(
|
|
50
47
|
name="brave_web_search",
|
|
51
48
|
description="Web search with DuckDuckGo fallback (zero API key required). Returns verifiable search results with titles, URLs, and snippets.",
|
|
52
49
|
input_schema={
|
|
@@ -59,12 +56,19 @@ def run_zero_key_mcp_server(service: str):
|
|
|
59
56
|
},
|
|
60
57
|
handler=handle_brave_search,
|
|
61
58
|
aliases=("search",),
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
)
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _build_firecrawl_tools() -> list:
|
|
64
|
+
from runner.mcp_protocol import ToolSpec
|
|
65
|
+
from skills.epistemic_search.scripts.fetch import fetch_and_cache
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
def handle_firecrawl_scrape(args: Dict[str, Any]) -> str:
|
|
68
|
+
return fetch_and_cache(url=args.get("url", ""))
|
|
69
|
+
|
|
70
|
+
return [
|
|
71
|
+
ToolSpec(
|
|
68
72
|
name="firecrawl_scrape",
|
|
69
73
|
description="Scrape and extract clean markdown with automatic content-addressed SHA-256 caching into .research/sources/.",
|
|
70
74
|
input_schema={
|
|
@@ -76,7 +80,20 @@ def run_zero_key_mcp_server(service: str):
|
|
|
76
80
|
},
|
|
77
81
|
handler=handle_firecrawl_scrape,
|
|
78
82
|
aliases=("scrape", "fetch"),
|
|
79
|
-
)
|
|
83
|
+
)
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def run_zero_key_mcp_server(service: str):
|
|
88
|
+
"""Run lightweight JSON-RPC MCP server handling search and fetch without API keys."""
|
|
89
|
+
from runner.mcp_protocol import StdioJsonRpcServer
|
|
90
|
+
|
|
91
|
+
if service == "brave-search":
|
|
92
|
+
tools = _build_brave_tools()
|
|
93
|
+
elif service == "firecrawl":
|
|
94
|
+
tools = _build_firecrawl_tools()
|
|
95
|
+
else:
|
|
96
|
+
tools = []
|
|
80
97
|
|
|
81
98
|
server = StdioJsonRpcServer(
|
|
82
99
|
server_name=f"epistemic-{service}-fallback",
|
|
@@ -86,41 +103,51 @@ def run_zero_key_mcp_server(service: str):
|
|
|
86
103
|
)
|
|
87
104
|
server.serve_forever()
|
|
88
105
|
|
|
106
|
+
|
|
107
|
+
def _launch_brave(npx_bin: Optional[str]) -> None:
|
|
108
|
+
api_key = get_config_val("BRAVE_SEARCH_API_KEY")
|
|
109
|
+
if api_key and npx_bin:
|
|
110
|
+
os.environ["BRAVE_SEARCH_API_KEY"] = api_key
|
|
111
|
+
os.execvp(npx_bin, [npx_bin, "-y", "@modelcontextprotocol/server-brave-search"])
|
|
112
|
+
else:
|
|
113
|
+
run_zero_key_mcp_server("brave-search")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _launch_firecrawl(npx_bin: Optional[str]) -> None:
|
|
117
|
+
api_key = get_config_val("FIRECRAWL_API_KEY")
|
|
118
|
+
api_url = get_config_val("FIRECRAWL_API_URL", "https://api.firecrawl.dev")
|
|
119
|
+
if api_key and npx_bin:
|
|
120
|
+
os.environ["FIRECRAWL_API_KEY"] = api_key
|
|
121
|
+
os.environ["FIRECRAWL_API_URL"] = api_url
|
|
122
|
+
os.execvp(npx_bin, [npx_bin, "-y", "firecrawl-mcp"])
|
|
123
|
+
else:
|
|
124
|
+
run_zero_key_mcp_server("firecrawl")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _launch_searxng() -> None:
|
|
128
|
+
searxng_url = get_config_val("SEARXNG_URL", "http://localhost:8080")
|
|
129
|
+
searxng_script = os.path.join(SCRIPT_DIR, "searxng_mcp.py")
|
|
130
|
+
if os.path.exists(searxng_script):
|
|
131
|
+
os.environ["SEARXNG_URL"] = searxng_url
|
|
132
|
+
os.execvp("python3", ["python3", searxng_script])
|
|
133
|
+
else:
|
|
134
|
+
run_zero_key_mcp_server("searxng")
|
|
135
|
+
|
|
136
|
+
|
|
89
137
|
def main():
|
|
90
138
|
service = sys.argv[1] if len(sys.argv) > 1 else "brave-search"
|
|
91
139
|
npx_bin = shutil.which("npx")
|
|
92
140
|
|
|
93
141
|
if service == "brave-search":
|
|
94
|
-
|
|
95
|
-
if api_key and npx_bin:
|
|
96
|
-
os.environ["BRAVE_SEARCH_API_KEY"] = api_key
|
|
97
|
-
os.execvp(npx_bin, [npx_bin, "-y", "@modelcontextprotocol/server-brave-search"])
|
|
98
|
-
else:
|
|
99
|
-
# Fall back to zero-key search proxy
|
|
100
|
-
run_zero_key_mcp_server("brave-search")
|
|
101
|
-
|
|
142
|
+
_launch_brave(npx_bin)
|
|
102
143
|
elif service == "firecrawl":
|
|
103
|
-
|
|
104
|
-
api_url = get_config_val("FIRECRAWL_API_URL", "https://api.firecrawl.dev")
|
|
105
|
-
if api_key and npx_bin:
|
|
106
|
-
os.environ["FIRECRAWL_API_KEY"] = api_key
|
|
107
|
-
os.environ["FIRECRAWL_API_URL"] = api_url
|
|
108
|
-
os.execvp(npx_bin, [npx_bin, "-y", "firecrawl-mcp"])
|
|
109
|
-
else:
|
|
110
|
-
# Fall back to zero-key readability fetcher + SHA-256 hasher
|
|
111
|
-
run_zero_key_mcp_server("firecrawl")
|
|
112
|
-
|
|
144
|
+
_launch_firecrawl(npx_bin)
|
|
113
145
|
elif service == "searxng":
|
|
114
|
-
|
|
115
|
-
searxng_script = os.path.join(SCRIPT_DIR, "searxng_mcp.py")
|
|
116
|
-
if os.path.exists(searxng_script):
|
|
117
|
-
os.environ["SEARXNG_URL"] = searxng_url
|
|
118
|
-
os.execvp("python3", ["python3", searxng_script])
|
|
119
|
-
else:
|
|
120
|
-
run_zero_key_mcp_server("searxng")
|
|
146
|
+
_launch_searxng()
|
|
121
147
|
else:
|
|
122
148
|
sys.stderr.write(f"Unknown service: {service}\n")
|
|
123
149
|
sys.exit(1)
|
|
124
150
|
|
|
151
|
+
|
|
125
152
|
if __name__ == "__main__":
|
|
126
153
|
main()
|
package/extensions/pi/index.js
CHANGED
|
@@ -12,19 +12,39 @@
|
|
|
12
12
|
* not a duplicate tool implementation.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { spawnSync } from 'child_process';
|
|
16
|
-
import
|
|
17
|
-
import
|
|
15
|
+
import { spawnSync } from 'node:child_process';
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
18
19
|
|
|
19
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
20
21
|
const __dirname = path.dirname(__filename);
|
|
21
22
|
const PKG_ROOT = path.resolve(__dirname, '../..');
|
|
22
23
|
const MCP_SERVER = path.join(PKG_ROOT, 'runner', 'mcp_server.py');
|
|
23
24
|
|
|
25
|
+
function resolveBinary(name) {
|
|
26
|
+
const pathDirs = (process.env.PATH || '').split(path.delimiter);
|
|
27
|
+
for (const dir of pathDirs) {
|
|
28
|
+
if (!dir || dir === '.' || dir.startsWith('./') || dir.startsWith('../')) continue;
|
|
29
|
+
const full = path.join(dir, name);
|
|
30
|
+
try {
|
|
31
|
+
// Match Node's own PATH search: skip non-files and non-executables so a
|
|
32
|
+
// directory or a non-executable named `python3` earlier on PATH cannot
|
|
33
|
+
// shadow the real binary.
|
|
34
|
+
if (!fs.statSync(full).isFile()) continue;
|
|
35
|
+
fs.accessSync(full, fs.constants.X_OK);
|
|
36
|
+
return fs.realpathSync(full);
|
|
37
|
+
} catch {
|
|
38
|
+
/* not present, not a file, or not executable — keep searching */
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return name;
|
|
42
|
+
}
|
|
43
|
+
|
|
24
44
|
/** Uniform dispatch: one-shot MCP call. Returns {ok, text}. */
|
|
25
45
|
function callMcp(tool, args = {}, ctx = {}) {
|
|
26
46
|
const res = spawnSync(
|
|
27
|
-
'python3',
|
|
47
|
+
resolveBinary('python3'),
|
|
28
48
|
[MCP_SERVER, 'call', tool, JSON.stringify(args || {})],
|
|
29
49
|
{
|
|
30
50
|
encoding: 'utf-8',
|
|
@@ -154,7 +174,7 @@ export default function initPiExtension(pi) {
|
|
|
154
174
|
ctx.output?.(`💡 [IUMBTEMS Brainstorm] Diverging on: "${objective}"...`);
|
|
155
175
|
// Skill-local context scaffold (not a tool duplicate) -> then canonical tool.
|
|
156
176
|
const scaffold = path.join(PKG_ROOT, 'skills/brainstorming/scripts/brainstorm.py');
|
|
157
|
-
const pre = spawnSync('python3', [scaffold, '--objective', objective, '--show-context'], {
|
|
177
|
+
const pre = spawnSync(resolveBinary('python3'), [scaffold, '--objective', objective, '--show-context'], {
|
|
158
178
|
encoding: 'utf-8',
|
|
159
179
|
cwd: ctx.cwd || process.cwd()
|
|
160
180
|
});
|
package/install.sh
CHANGED
|
@@ -23,16 +23,16 @@ mkdir -p "$CLAUDE_DIR/skills"
|
|
|
23
23
|
echo "🔗 Linking skills into $CLAUDE_DIR/skills/..."
|
|
24
24
|
for skill in grilling research_cache epistemic_search swarm_config code_audit oss_scout brainstorming; do
|
|
25
25
|
# legacy research-cache dir name kept as alias for older configs
|
|
26
|
-
ln -
|
|
26
|
+
ln -sfn "$REPO_DIR/skills/$skill" "$CLAUDE_DIR/skills/$skill"
|
|
27
27
|
echo " - $CLAUDE_DIR/skills/$skill -> $REPO_DIR/skills/$skill"
|
|
28
28
|
done
|
|
29
|
-
if [ ! -e "$CLAUDE_DIR/skills/research-cache" ]; then
|
|
30
|
-
ln -
|
|
29
|
+
if [[ ! -e "$CLAUDE_DIR/skills/research-cache" ]]; then
|
|
30
|
+
ln -sfn "$REPO_DIR/skills/research_cache" "$CLAUDE_DIR/skills/research-cache"
|
|
31
31
|
echo " - $CLAUDE_DIR/skills/research-cache -> $REPO_DIR/skills/research_cache (legacy alias)"
|
|
32
32
|
fi
|
|
33
33
|
|
|
34
34
|
# 3. Patch ~/.claude.json mcpServers non-destructively
|
|
35
|
-
if [ -f "$CLAUDE_JSON" ]; then
|
|
35
|
+
if [[ -f "$CLAUDE_JSON" ]]; then
|
|
36
36
|
echo "🔧 Merging research MCP servers into $CLAUDE_JSON..."
|
|
37
37
|
python3 - <<EOF
|
|
38
38
|
import json
|
|
@@ -77,7 +77,7 @@ fi
|
|
|
77
77
|
|
|
78
78
|
# 4. Patch ~/.claude/settings.json
|
|
79
79
|
SETTINGS_JSON="$CLAUDE_DIR/settings.json"
|
|
80
|
-
if [ -f "$SETTINGS_JSON" ]; then
|
|
80
|
+
if [[ -f "$SETTINGS_JSON" ]]; then
|
|
81
81
|
echo "🔧 Merging plugin settings into $SETTINGS_JSON..."
|
|
82
82
|
python3 - <<EOF
|
|
83
83
|
import json
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heretek-ai/epistemic-swarm",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"description": "IUMBTEMS: I Use My Brain To Express My Self — High-Integrity Dialectic Research Agent Harness for Claude Code, OpenCode V2, Pi, OMP (oh-my-pi), Gemini CLI, Codex CLI, and AntiGravity",
|
|
5
5
|
"main": "bin/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://antigravity.google/schemas/v1/plugin.json",
|
|
3
3
|
"name": "epistemic-swarm",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.8",
|
|
5
5
|
"description": "IUMBTEMS Epistemic Swarm: dialectic research, code audit, OSS scout, and lateral brainstorming for AntiGravity.",
|
|
6
6
|
"author": "Heretek AI",
|
|
7
7
|
"license": "Apache-2.0",
|