nemoris 0.1.0 → 0.1.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.
- package/.env.example +49 -49
- package/LICENSE +21 -21
- package/README.md +209 -209
- package/SECURITY.md +59 -119
- package/bin/nemoris +46 -46
- package/config/agents/agent.toml.example +28 -28
- package/config/agents/content.toml +23 -0
- package/config/agents/default.toml +22 -22
- package/config/agents/heartbeat.toml +35 -0
- package/config/agents/iris.toml +23 -0
- package/config/agents/lab.toml +23 -0
- package/config/agents/main.toml +45 -0
- package/config/agents/nemo.toml +21 -0
- package/config/agents/ops.toml +38 -0
- package/config/agents/orchestrator.toml +18 -18
- package/config/agents/revenue.toml +23 -0
- package/config/agents/testyboo.toml +19 -0
- package/config/delivery.toml +73 -73
- package/config/embeddings.toml +5 -5
- package/config/identity/content-purpose.md +11 -0
- package/config/identity/content-soul.md +45 -0
- package/config/identity/default-purpose.md +1 -1
- package/config/identity/default-soul.md +3 -3
- package/config/identity/heartbeat-purpose.md +9 -0
- package/config/identity/heartbeat-soul.md +16 -0
- package/config/identity/iris-purpose.md +17 -0
- package/config/identity/iris-soul.md +68 -0
- package/config/identity/lab-purpose.md +10 -0
- package/config/identity/lab-soul.md +38 -0
- package/config/identity/main-purpose.md +17 -0
- package/config/identity/main-soul.md +66 -0
- package/config/identity/main-user.md +22 -0
- package/config/identity/ops-purpose.md +9 -0
- package/config/identity/ops-soul.md +16 -0
- package/config/identity/orchestrator-purpose.md +1 -1
- package/config/identity/orchestrator-soul.md +1 -1
- package/config/identity/revenue-purpose.md +9 -0
- package/config/identity/revenue-soul.md +41 -0
- package/config/identity/testyboo-purpose.md +13 -0
- package/config/identity/testyboo-soul.md +20 -0
- package/config/improvement-targets.toml +15 -15
- package/config/jobs/heartbeat-check.toml +30 -30
- package/config/jobs/memory-rollup.toml +46 -46
- package/config/jobs/workspace-health.toml +63 -63
- package/config/mcp.toml +16 -16
- package/config/output-contracts.toml +17 -17
- package/config/peers.toml +32 -32
- package/config/peers.toml.example +32 -32
- package/config/policies/memory-default.toml +10 -10
- package/config/policies/memory-heartbeat.toml +5 -5
- package/config/policies/memory-ops.toml +10 -10
- package/config/policies/tools-heartbeat-minimal.toml +8 -8
- package/config/policies/tools-interactive-safe.toml +8 -8
- package/config/policies/tools-ops-bounded.toml +8 -8
- package/config/policies/tools-orchestrator.toml +7 -7
- package/config/providers/anthropic.toml +15 -15
- package/config/providers/ollama.toml +5 -5
- package/config/providers/openai-codex.toml +9 -9
- package/config/providers/openrouter.toml +5 -5
- package/config/router.toml +22 -22
- package/config/runtime.toml +114 -114
- package/config/skills/self-improvement.toml +15 -15
- package/config/skills/telegram-onboarding-spec.md +240 -240
- package/config/skills/workspace-monitor.toml +15 -15
- package/config/task-router.toml +42 -42
- package/install.sh +50 -50
- package/package.json +91 -90
- package/src/auth/auth-profiles.js +169 -169
- package/src/auth/openai-codex-oauth.js +285 -285
- package/src/battle.js +449 -449
- package/src/cli/help.js +265 -265
- package/src/cli/output-filter.js +49 -49
- package/src/cli/runtime-control.js +704 -704
- package/src/cli-main.js +2763 -2763
- package/src/cli.js +78 -78
- package/src/config/loader.js +332 -332
- package/src/config/schema-validator.js +214 -214
- package/src/config/toml-lite.js +8 -8
- package/src/daemon/action-handlers.js +71 -71
- package/src/daemon/healing-tick.js +87 -87
- package/src/daemon/health-probes.js +90 -90
- package/src/daemon/notifier.js +57 -57
- package/src/daemon/nurse.js +218 -218
- package/src/daemon/repair-log.js +106 -106
- package/src/daemon/rule-staging.js +90 -90
- package/src/daemon/rules.js +29 -29
- package/src/daemon/telegram-commands.js +54 -54
- package/src/daemon/updater.js +85 -85
- package/src/jobs/job-runner.js +78 -78
- package/src/mcp/consumer.js +129 -129
- package/src/memory/active-recall.js +171 -171
- package/src/memory/backend-manager.js +97 -97
- package/src/memory/backends/file-backend.js +38 -38
- package/src/memory/backends/qmd-backend.js +219 -219
- package/src/memory/embedding-guards.js +24 -24
- package/src/memory/embedding-index.js +118 -118
- package/src/memory/embedding-service.js +179 -179
- package/src/memory/file-index.js +177 -177
- package/src/memory/memory-signature.js +5 -5
- package/src/memory/memory-store.js +648 -648
- package/src/memory/retrieval-planner.js +66 -66
- package/src/memory/scoring.js +145 -145
- package/src/memory/simhash.js +78 -78
- package/src/memory/sqlite-active-store.js +824 -824
- package/src/memory/write-policy.js +36 -36
- package/src/onboarding/aliases.js +33 -33
- package/src/onboarding/auth/api-key.js +224 -224
- package/src/onboarding/auth/ollama-detect.js +42 -42
- package/src/onboarding/clack-prompter.js +77 -77
- package/src/onboarding/doctor.js +530 -530
- package/src/onboarding/lock.js +42 -42
- package/src/onboarding/model-catalog.js +344 -344
- package/src/onboarding/phases/auth.js +576 -589
- package/src/onboarding/phases/build.js +130 -130
- package/src/onboarding/phases/choose.js +82 -82
- package/src/onboarding/phases/detect.js +98 -98
- package/src/onboarding/phases/hatch.js +216 -216
- package/src/onboarding/phases/identity.js +79 -79
- package/src/onboarding/phases/ollama.js +345 -345
- package/src/onboarding/phases/scaffold.js +99 -99
- package/src/onboarding/phases/telegram.js +377 -377
- package/src/onboarding/phases/validate.js +204 -204
- package/src/onboarding/phases/verify.js +206 -206
- package/src/onboarding/platform.js +482 -482
- package/src/onboarding/status-bar.js +95 -95
- package/src/onboarding/templates.js +794 -794
- package/src/onboarding/toml-writer.js +38 -38
- package/src/onboarding/tui.js +250 -250
- package/src/onboarding/uninstall.js +153 -153
- package/src/onboarding/wizard.js +516 -499
- package/src/providers/anthropic.js +168 -168
- package/src/providers/base.js +247 -247
- package/src/providers/circuit-breaker.js +136 -136
- package/src/providers/ollama.js +163 -163
- package/src/providers/openai-codex.js +149 -149
- package/src/providers/openrouter.js +136 -136
- package/src/providers/registry.js +36 -36
- package/src/providers/router.js +16 -16
- package/src/runtime/bootstrap-cache.js +47 -47
- package/src/runtime/capabilities-prompt.js +25 -25
- package/src/runtime/completion-ping.js +99 -99
- package/src/runtime/config-validator.js +121 -121
- package/src/runtime/context-ledger.js +360 -360
- package/src/runtime/cutover-readiness.js +42 -42
- package/src/runtime/daemon.js +729 -729
- package/src/runtime/delivery-ack.js +195 -195
- package/src/runtime/delivery-adapters/local-file.js +41 -41
- package/src/runtime/delivery-adapters/openclaw-cli.js +94 -94
- package/src/runtime/delivery-adapters/openclaw-peer.js +98 -98
- package/src/runtime/delivery-adapters/shadow.js +13 -13
- package/src/runtime/delivery-adapters/standalone-http.js +98 -98
- package/src/runtime/delivery-adapters/telegram.js +104 -104
- package/src/runtime/delivery-adapters/tui.js +128 -128
- package/src/runtime/delivery-manager.js +807 -807
- package/src/runtime/delivery-store.js +168 -168
- package/src/runtime/dependency-health.js +118 -118
- package/src/runtime/envelope.js +114 -114
- package/src/runtime/evaluation.js +1089 -1089
- package/src/runtime/exec-approvals.js +216 -216
- package/src/runtime/executor.js +500 -500
- package/src/runtime/failure-ping.js +67 -67
- package/src/runtime/flows.js +83 -83
- package/src/runtime/guards.js +45 -45
- package/src/runtime/handoff.js +51 -51
- package/src/runtime/identity-cache.js +28 -28
- package/src/runtime/improvement-engine.js +109 -109
- package/src/runtime/improvement-harness.js +581 -581
- package/src/runtime/input-sanitiser.js +72 -72
- package/src/runtime/interaction-contract.js +347 -347
- package/src/runtime/lane-readiness.js +226 -226
- package/src/runtime/migration.js +323 -323
- package/src/runtime/model-resolution.js +78 -78
- package/src/runtime/network.js +64 -64
- package/src/runtime/notification-store.js +97 -97
- package/src/runtime/notifier.js +256 -256
- package/src/runtime/orchestrator.js +53 -53
- package/src/runtime/orphan-reaper.js +41 -41
- package/src/runtime/output-contract-schema.js +139 -139
- package/src/runtime/output-contract-validator.js +439 -439
- package/src/runtime/peer-readiness.js +69 -69
- package/src/runtime/peer-registry.js +133 -133
- package/src/runtime/pilot-status.js +108 -108
- package/src/runtime/prompt-builder.js +261 -261
- package/src/runtime/provider-attempt.js +582 -582
- package/src/runtime/report-fallback.js +71 -71
- package/src/runtime/result-normalizer.js +183 -183
- package/src/runtime/retention.js +74 -74
- package/src/runtime/review.js +244 -244
- package/src/runtime/route-job.js +15 -15
- package/src/runtime/run-store.js +38 -38
- package/src/runtime/schedule.js +88 -88
- package/src/runtime/scheduler-state.js +434 -434
- package/src/runtime/scheduler.js +656 -656
- package/src/runtime/session-compactor.js +182 -182
- package/src/runtime/session-search.js +155 -155
- package/src/runtime/slack-inbound.js +249 -249
- package/src/runtime/ssrf.js +102 -102
- package/src/runtime/status-aggregator.js +330 -330
- package/src/runtime/task-contract.js +140 -140
- package/src/runtime/task-packet.js +107 -107
- package/src/runtime/task-router.js +140 -140
- package/src/runtime/telegram-inbound.js +1565 -1565
- package/src/runtime/token-counter.js +134 -134
- package/src/runtime/token-estimator.js +59 -59
- package/src/runtime/tool-loop.js +200 -200
- package/src/runtime/transport-server.js +311 -311
- package/src/runtime/tui-server.js +411 -411
- package/src/runtime/ulid.js +44 -44
- package/src/security/ssrf-check.js +197 -197
- package/src/setup.js +369 -369
- package/src/shadow/bridge.js +303 -303
- package/src/skills/loader.js +84 -84
- package/src/tools/catalog.json +49 -49
- package/src/tools/cli-delegate.js +44 -44
- package/src/tools/mcp-client.js +106 -106
- package/src/tools/micro/cancel-task.js +6 -6
- package/src/tools/micro/complete-task.js +6 -6
- package/src/tools/micro/fail-task.js +6 -6
- package/src/tools/micro/http-fetch.js +74 -74
- package/src/tools/micro/index.js +36 -36
- package/src/tools/micro/lcm-recall.js +60 -60
- package/src/tools/micro/list-dir.js +17 -17
- package/src/tools/micro/list-skills.js +46 -46
- package/src/tools/micro/load-skill.js +38 -38
- package/src/tools/micro/memory-search.js +45 -45
- package/src/tools/micro/read-file.js +11 -11
- package/src/tools/micro/session-search.js +54 -54
- package/src/tools/micro/shell-exec.js +43 -43
- package/src/tools/micro/trigger-job.js +79 -79
- package/src/tools/micro/web-search.js +58 -58
- package/src/tools/micro/workspace-paths.js +39 -39
- package/src/tools/micro/write-file.js +14 -14
- package/src/tools/micro/write-memory.js +41 -41
- package/src/tools/registry.js +348 -348
- package/src/tools/tool-result-contract.js +36 -36
- package/src/tui/chat.js +835 -835
- package/src/tui/renderer.js +175 -175
- package/src/tui/socket-client.js +217 -217
- package/src/utils/canonical-json.js +29 -29
- package/src/utils/compaction.js +30 -30
- package/src/utils/env-loader.js +5 -5
- package/src/utils/errors.js +80 -80
- package/src/utils/fs.js +101 -101
- package/src/utils/ids.js +5 -5
- package/src/utils/model-context-limits.js +30 -30
- package/src/utils/token-budget.js +74 -74
- package/src/utils/usage-cost.js +25 -25
- package/src/utils/usage-metrics.js +14 -14
package/src/skills/loader.js
CHANGED
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
// src/skills/loader.js
|
|
2
|
-
import { readFile, readdir } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { parseToml } from "../config/toml-lite.js";
|
|
5
|
-
|
|
6
|
-
export class SkillsLoader {
|
|
7
|
-
#skills = new Map();
|
|
8
|
-
|
|
9
|
-
async loadSkills(configDir) {
|
|
10
|
-
try {
|
|
11
|
-
const files = await readdir(configDir);
|
|
12
|
-
for (const file of files) {
|
|
13
|
-
if (!file.endsWith(".toml")) continue;
|
|
14
|
-
const content = await readFile(join(configDir, file), "utf8");
|
|
15
|
-
const parsed = parseToml(content);
|
|
16
|
-
const skill = extractSkill(parsed);
|
|
17
|
-
if (!skill.id) continue;
|
|
18
|
-
this.#skills.set(skill.id, skill);
|
|
19
|
-
}
|
|
20
|
-
} catch (err) {
|
|
21
|
-
if (err.code !== "ENOENT") throw err;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
resolve(skillId) {
|
|
26
|
-
return this.#skills.get(skillId) ?? null;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
isAgentAllowed(skillId, agentId) {
|
|
30
|
-
const skill = this.#skills.get(skillId);
|
|
31
|
-
if (!skill) return false;
|
|
32
|
-
return skill.agent_scope.includes(agentId);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
listAll() {
|
|
36
|
-
return [...this.#skills.entries()];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
compose(skillIds) {
|
|
40
|
-
const prompts = [];
|
|
41
|
-
const allTools = new Set();
|
|
42
|
-
let minMaxTokens = Infinity;
|
|
43
|
-
let minMaxToolCalls = Infinity;
|
|
44
|
-
|
|
45
|
-
for (const id of skillIds) {
|
|
46
|
-
const skill = this.#skills.get(id);
|
|
47
|
-
if (!skill) continue;
|
|
48
|
-
prompts.push(skill.context.prompt);
|
|
49
|
-
for (const t of skill.tools.required) allTools.add(t);
|
|
50
|
-
for (const t of skill.tools.optional) allTools.add(t);
|
|
51
|
-
if (skill.budget.max_tokens < minMaxTokens) minMaxTokens = skill.budget.max_tokens;
|
|
52
|
-
if (skill.budget.max_tool_calls < minMaxToolCalls) minMaxToolCalls = skill.budget.max_tool_calls;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
prompt: prompts.join("\n\n"),
|
|
57
|
-
tools: [...allTools],
|
|
58
|
-
budget: {
|
|
59
|
-
max_tokens: minMaxTokens === Infinity ? undefined : minMaxTokens,
|
|
60
|
-
max_tool_calls: minMaxToolCalls === Infinity ? undefined : minMaxToolCalls,
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function extractSkill(parsed) {
|
|
67
|
-
const raw = parsed.skill ?? {};
|
|
68
|
-
return {
|
|
69
|
-
id: raw.id ?? null,
|
|
70
|
-
description: raw.description ?? "",
|
|
71
|
-
agent_scope: Array.isArray(raw.agent_scope) ? raw.agent_scope : (raw.agent_scope ? [raw.agent_scope] : []),
|
|
72
|
-
context: {
|
|
73
|
-
prompt: raw.context?.prompt ?? "",
|
|
74
|
-
},
|
|
75
|
-
tools: {
|
|
76
|
-
required: Array.isArray(raw.tools?.required) ? raw.tools.required : (raw.tools?.required ? [raw.tools.required] : []),
|
|
77
|
-
optional: Array.isArray(raw.tools?.optional) ? raw.tools.optional : (raw.tools?.optional ? [raw.tools.optional] : []),
|
|
78
|
-
},
|
|
79
|
-
budget: {
|
|
80
|
-
max_tokens: raw.budget?.max_tokens ?? 8192,
|
|
81
|
-
max_tool_calls: raw.budget?.max_tool_calls ?? 20,
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
}
|
|
1
|
+
// src/skills/loader.js
|
|
2
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { parseToml } from "../config/toml-lite.js";
|
|
5
|
+
|
|
6
|
+
export class SkillsLoader {
|
|
7
|
+
#skills = new Map();
|
|
8
|
+
|
|
9
|
+
async loadSkills(configDir) {
|
|
10
|
+
try {
|
|
11
|
+
const files = await readdir(configDir);
|
|
12
|
+
for (const file of files) {
|
|
13
|
+
if (!file.endsWith(".toml")) continue;
|
|
14
|
+
const content = await readFile(join(configDir, file), "utf8");
|
|
15
|
+
const parsed = parseToml(content);
|
|
16
|
+
const skill = extractSkill(parsed);
|
|
17
|
+
if (!skill.id) continue;
|
|
18
|
+
this.#skills.set(skill.id, skill);
|
|
19
|
+
}
|
|
20
|
+
} catch (err) {
|
|
21
|
+
if (err.code !== "ENOENT") throw err;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
resolve(skillId) {
|
|
26
|
+
return this.#skills.get(skillId) ?? null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
isAgentAllowed(skillId, agentId) {
|
|
30
|
+
const skill = this.#skills.get(skillId);
|
|
31
|
+
if (!skill) return false;
|
|
32
|
+
return skill.agent_scope.includes(agentId);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
listAll() {
|
|
36
|
+
return [...this.#skills.entries()];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
compose(skillIds) {
|
|
40
|
+
const prompts = [];
|
|
41
|
+
const allTools = new Set();
|
|
42
|
+
let minMaxTokens = Infinity;
|
|
43
|
+
let minMaxToolCalls = Infinity;
|
|
44
|
+
|
|
45
|
+
for (const id of skillIds) {
|
|
46
|
+
const skill = this.#skills.get(id);
|
|
47
|
+
if (!skill) continue;
|
|
48
|
+
prompts.push(skill.context.prompt);
|
|
49
|
+
for (const t of skill.tools.required) allTools.add(t);
|
|
50
|
+
for (const t of skill.tools.optional) allTools.add(t);
|
|
51
|
+
if (skill.budget.max_tokens < minMaxTokens) minMaxTokens = skill.budget.max_tokens;
|
|
52
|
+
if (skill.budget.max_tool_calls < minMaxToolCalls) minMaxToolCalls = skill.budget.max_tool_calls;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
prompt: prompts.join("\n\n"),
|
|
57
|
+
tools: [...allTools],
|
|
58
|
+
budget: {
|
|
59
|
+
max_tokens: minMaxTokens === Infinity ? undefined : minMaxTokens,
|
|
60
|
+
max_tool_calls: minMaxToolCalls === Infinity ? undefined : minMaxToolCalls,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function extractSkill(parsed) {
|
|
67
|
+
const raw = parsed.skill ?? {};
|
|
68
|
+
return {
|
|
69
|
+
id: raw.id ?? null,
|
|
70
|
+
description: raw.description ?? "",
|
|
71
|
+
agent_scope: Array.isArray(raw.agent_scope) ? raw.agent_scope : (raw.agent_scope ? [raw.agent_scope] : []),
|
|
72
|
+
context: {
|
|
73
|
+
prompt: raw.context?.prompt ?? "",
|
|
74
|
+
},
|
|
75
|
+
tools: {
|
|
76
|
+
required: Array.isArray(raw.tools?.required) ? raw.tools.required : (raw.tools?.required ? [raw.tools.required] : []),
|
|
77
|
+
optional: Array.isArray(raw.tools?.optional) ? raw.tools.optional : (raw.tools?.optional ? [raw.tools.optional] : []),
|
|
78
|
+
},
|
|
79
|
+
budget: {
|
|
80
|
+
max_tokens: raw.budget?.max_tokens ?? 8192,
|
|
81
|
+
max_tool_calls: raw.budget?.max_tool_calls ?? 20,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
package/src/tools/catalog.json
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"github": {
|
|
3
|
-
"tier": "mcp",
|
|
4
|
-
"description": "GitHub operations via MCP",
|
|
5
|
-
"command": "npx",
|
|
6
|
-
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
7
|
-
"env": { "GITHUB_TOKEN": "env:NEMORIS_GITHUB_TOKEN" },
|
|
8
|
-
"env_prompts": { "NEMORIS_GITHUB_TOKEN": "GitHub personal access token" }
|
|
9
|
-
},
|
|
10
|
-
"supabase": {
|
|
11
|
-
"tier": "mcp",
|
|
12
|
-
"description": "Supabase database and auth operations",
|
|
13
|
-
"command": "npx",
|
|
14
|
-
"args": ["-y", "@supabase/mcp-server"],
|
|
15
|
-
"env": { "SUPABASE_URL": "env:NEMORIS_SUPABASE_URL", "SUPABASE_KEY": "env:NEMORIS_SUPABASE_KEY" },
|
|
16
|
-
"env_prompts": { "NEMORIS_SUPABASE_URL": "Supabase project URL", "NEMORIS_SUPABASE_KEY": "Supabase service role key" }
|
|
17
|
-
},
|
|
18
|
-
"slack": {
|
|
19
|
-
"tier": "mcp",
|
|
20
|
-
"description": "Slack messaging operations",
|
|
21
|
-
"command": "npx",
|
|
22
|
-
"args": ["-y", "@anthropic/mcp-server-slack"],
|
|
23
|
-
"env": { "SLACK_TOKEN": "env:NEMORIS_SLACK_TOKEN" },
|
|
24
|
-
"env_prompts": { "NEMORIS_SLACK_TOKEN": "Slack bot OAuth token" }
|
|
25
|
-
},
|
|
26
|
-
"filesystem": {
|
|
27
|
-
"tier": "mcp",
|
|
28
|
-
"description": "Extended filesystem operations",
|
|
29
|
-
"command": "npx",
|
|
30
|
-
"args": ["-y", "@modelcontextprotocol/server-filesystem"],
|
|
31
|
-
"env": {}
|
|
32
|
-
},
|
|
33
|
-
"claude_code": {
|
|
34
|
-
"tier": "cli",
|
|
35
|
-
"description": "Delegate complex coding tasks to Claude Code",
|
|
36
|
-
"command": "claude",
|
|
37
|
-
"args": ["-p", "--output-format", "json"],
|
|
38
|
-
"env": {},
|
|
39
|
-
"env_prompts": {}
|
|
40
|
-
},
|
|
41
|
-
"gemini_cli": {
|
|
42
|
-
"tier": "cli",
|
|
43
|
-
"description": "Delegate tasks to Gemini CLI",
|
|
44
|
-
"command": "gemini",
|
|
45
|
-
"args": [],
|
|
46
|
-
"env": {},
|
|
47
|
-
"env_prompts": {}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"github": {
|
|
3
|
+
"tier": "mcp",
|
|
4
|
+
"description": "GitHub operations via MCP",
|
|
5
|
+
"command": "npx",
|
|
6
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
7
|
+
"env": { "GITHUB_TOKEN": "env:NEMORIS_GITHUB_TOKEN" },
|
|
8
|
+
"env_prompts": { "NEMORIS_GITHUB_TOKEN": "GitHub personal access token" }
|
|
9
|
+
},
|
|
10
|
+
"supabase": {
|
|
11
|
+
"tier": "mcp",
|
|
12
|
+
"description": "Supabase database and auth operations",
|
|
13
|
+
"command": "npx",
|
|
14
|
+
"args": ["-y", "@supabase/mcp-server"],
|
|
15
|
+
"env": { "SUPABASE_URL": "env:NEMORIS_SUPABASE_URL", "SUPABASE_KEY": "env:NEMORIS_SUPABASE_KEY" },
|
|
16
|
+
"env_prompts": { "NEMORIS_SUPABASE_URL": "Supabase project URL", "NEMORIS_SUPABASE_KEY": "Supabase service role key" }
|
|
17
|
+
},
|
|
18
|
+
"slack": {
|
|
19
|
+
"tier": "mcp",
|
|
20
|
+
"description": "Slack messaging operations",
|
|
21
|
+
"command": "npx",
|
|
22
|
+
"args": ["-y", "@anthropic/mcp-server-slack"],
|
|
23
|
+
"env": { "SLACK_TOKEN": "env:NEMORIS_SLACK_TOKEN" },
|
|
24
|
+
"env_prompts": { "NEMORIS_SLACK_TOKEN": "Slack bot OAuth token" }
|
|
25
|
+
},
|
|
26
|
+
"filesystem": {
|
|
27
|
+
"tier": "mcp",
|
|
28
|
+
"description": "Extended filesystem operations",
|
|
29
|
+
"command": "npx",
|
|
30
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem"],
|
|
31
|
+
"env": {}
|
|
32
|
+
},
|
|
33
|
+
"claude_code": {
|
|
34
|
+
"tier": "cli",
|
|
35
|
+
"description": "Delegate complex coding tasks to Claude Code",
|
|
36
|
+
"command": "claude",
|
|
37
|
+
"args": ["-p", "--output-format", "json"],
|
|
38
|
+
"env": {},
|
|
39
|
+
"env_prompts": {}
|
|
40
|
+
},
|
|
41
|
+
"gemini_cli": {
|
|
42
|
+
"tier": "cli",
|
|
43
|
+
"description": "Delegate tasks to Gemini CLI",
|
|
44
|
+
"command": "gemini",
|
|
45
|
+
"args": [],
|
|
46
|
+
"env": {},
|
|
47
|
+
"env_prompts": {}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
// src/tools/cli-delegate.js
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
|
|
4
|
-
export function cliDelegate({ command, args = [], input = "", timeout = 120000, env = {} }) {
|
|
5
|
-
return new Promise((resolve) => {
|
|
6
|
-
let stdout = "";
|
|
7
|
-
let stderr = "";
|
|
8
|
-
|
|
9
|
-
const child = spawn(command, args, {
|
|
10
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
11
|
-
env: { ...process.env, ...env },
|
|
12
|
-
// Note: spawn() does not support timeout option — handled manually below
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
child.stdout.on("data", (d) => { stdout += d.toString(); });
|
|
16
|
-
child.stderr.on("data", (d) => { stderr += d.toString(); });
|
|
17
|
-
|
|
18
|
-
if (input) {
|
|
19
|
-
child.stdin.write(input);
|
|
20
|
-
child.stdin.end();
|
|
21
|
-
} else {
|
|
22
|
-
child.stdin.end();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const timer = setTimeout(() => {
|
|
26
|
-
child.kill();
|
|
27
|
-
resolve(`Error: command timed out after ${timeout}ms`);
|
|
28
|
-
}, timeout);
|
|
29
|
-
|
|
30
|
-
child.on("close", (code) => {
|
|
31
|
-
clearTimeout(timer);
|
|
32
|
-
if (code === 0) {
|
|
33
|
-
resolve(stdout || "(no output)");
|
|
34
|
-
} else {
|
|
35
|
-
resolve(`Exit ${code}:\n${stderr || stdout || "(no output)"}`);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
child.on("error", (err) => {
|
|
40
|
-
clearTimeout(timer);
|
|
41
|
-
resolve(`Error: ${err.message}`);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
}
|
|
1
|
+
// src/tools/cli-delegate.js
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
|
|
4
|
+
export function cliDelegate({ command, args = [], input = "", timeout = 120000, env = {} }) {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
let stdout = "";
|
|
7
|
+
let stderr = "";
|
|
8
|
+
|
|
9
|
+
const child = spawn(command, args, {
|
|
10
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
11
|
+
env: { ...process.env, ...env },
|
|
12
|
+
// Note: spawn() does not support timeout option — handled manually below
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
child.stdout.on("data", (d) => { stdout += d.toString(); });
|
|
16
|
+
child.stderr.on("data", (d) => { stderr += d.toString(); });
|
|
17
|
+
|
|
18
|
+
if (input) {
|
|
19
|
+
child.stdin.write(input);
|
|
20
|
+
child.stdin.end();
|
|
21
|
+
} else {
|
|
22
|
+
child.stdin.end();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const timer = setTimeout(() => {
|
|
26
|
+
child.kill();
|
|
27
|
+
resolve(`Error: command timed out after ${timeout}ms`);
|
|
28
|
+
}, timeout);
|
|
29
|
+
|
|
30
|
+
child.on("close", (code) => {
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
if (code === 0) {
|
|
33
|
+
resolve(stdout || "(no output)");
|
|
34
|
+
} else {
|
|
35
|
+
resolve(`Exit ${code}:\n${stderr || stdout || "(no output)"}`);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
child.on("error", (err) => {
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
resolve(`Error: ${err.message}`);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
package/src/tools/mcp-client.js
CHANGED
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
// src/tools/mcp-client.js
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
|
|
4
|
-
export class McpClient {
|
|
5
|
-
#process = null;
|
|
6
|
-
#config;
|
|
7
|
-
#requestId = 0;
|
|
8
|
-
#pending = new Map();
|
|
9
|
-
#buffer = "";
|
|
10
|
-
|
|
11
|
-
constructor(config) {
|
|
12
|
-
this.#config = config;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async spawn() {
|
|
16
|
-
if (this.#process) return;
|
|
17
|
-
const { command, args = [], env = {} } = this.#config;
|
|
18
|
-
this.#process = spawn(command, args, {
|
|
19
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
20
|
-
env: { ...process.env, ...env },
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
this.#process.stdout.on("data", (data) => {
|
|
24
|
-
this.#buffer += data.toString();
|
|
25
|
-
this.#drainBuffer();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
this.#process.on("exit", () => {
|
|
29
|
-
this.#process = null;
|
|
30
|
-
for (const [, { reject }] of this.#pending) {
|
|
31
|
-
reject(new Error("MCP server exited unexpectedly"));
|
|
32
|
-
}
|
|
33
|
-
this.#pending.clear();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// Send initialize request per MCP protocol
|
|
37
|
-
await this.#send("initialize", {
|
|
38
|
-
protocolVersion: "2024-11-05",
|
|
39
|
-
capabilities: {},
|
|
40
|
-
clientInfo: { name: "nemoris", version: "0.1.0" },
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// Send initialized notification (required by MCP protocol)
|
|
44
|
-
this.#process.stdin.write(JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }) + "\n");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async callTool(name, args) {
|
|
48
|
-
if (!this.#process) throw new Error("MCP server not running");
|
|
49
|
-
const result = await this.#send("tools/call", { name, arguments: args });
|
|
50
|
-
if (result.content && Array.isArray(result.content)) {
|
|
51
|
-
return result.content.map(c => c.text || JSON.stringify(c)).join("\n");
|
|
52
|
-
}
|
|
53
|
-
return JSON.stringify(result);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async listTools() {
|
|
57
|
-
if (!this.#process) throw new Error("MCP server not running");
|
|
58
|
-
const result = await this.#send("tools/list", {});
|
|
59
|
-
return result.tools || [];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
isAlive() {
|
|
63
|
-
return this.#process !== null && !this.#process.killed;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
kill() {
|
|
67
|
-
if (this.#process) {
|
|
68
|
-
this.#process.kill();
|
|
69
|
-
this.#process = null;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
#send(method, params) {
|
|
74
|
-
const id = ++this.#requestId;
|
|
75
|
-
const message = JSON.stringify({ jsonrpc: "2.0", id, method, params });
|
|
76
|
-
this.#process.stdin.write(message + "\n");
|
|
77
|
-
|
|
78
|
-
const timeout = this.#config.timeout || 30000;
|
|
79
|
-
return new Promise((resolve, reject) => {
|
|
80
|
-
const timer = setTimeout(() => {
|
|
81
|
-
this.#pending.delete(id);
|
|
82
|
-
reject(new Error(`MCP request timed out after ${timeout}ms`));
|
|
83
|
-
}, timeout);
|
|
84
|
-
this.#pending.set(id, { resolve: (v) => { clearTimeout(timer); resolve(v); }, reject: (e) => { clearTimeout(timer); reject(e); } });
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
#drainBuffer() {
|
|
89
|
-
const lines = this.#buffer.split("\n");
|
|
90
|
-
this.#buffer = lines.pop() || "";
|
|
91
|
-
for (const line of lines) {
|
|
92
|
-
if (!line.trim()) continue;
|
|
93
|
-
try {
|
|
94
|
-
const msg = JSON.parse(line);
|
|
95
|
-
if (msg.id && this.#pending.has(msg.id)) {
|
|
96
|
-
const { resolve, reject } = this.#pending.get(msg.id);
|
|
97
|
-
this.#pending.delete(msg.id);
|
|
98
|
-
if (msg.error) reject(new Error(msg.error.message || "MCP error"));
|
|
99
|
-
else resolve(msg.result);
|
|
100
|
-
}
|
|
101
|
-
} catch {
|
|
102
|
-
// Non-JSON line — ignore
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
1
|
+
// src/tools/mcp-client.js
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
|
|
4
|
+
export class McpClient {
|
|
5
|
+
#process = null;
|
|
6
|
+
#config;
|
|
7
|
+
#requestId = 0;
|
|
8
|
+
#pending = new Map();
|
|
9
|
+
#buffer = "";
|
|
10
|
+
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.#config = config;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async spawn() {
|
|
16
|
+
if (this.#process) return;
|
|
17
|
+
const { command, args = [], env = {} } = this.#config;
|
|
18
|
+
this.#process = spawn(command, args, {
|
|
19
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
20
|
+
env: { ...process.env, ...env },
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
this.#process.stdout.on("data", (data) => {
|
|
24
|
+
this.#buffer += data.toString();
|
|
25
|
+
this.#drainBuffer();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
this.#process.on("exit", () => {
|
|
29
|
+
this.#process = null;
|
|
30
|
+
for (const [, { reject }] of this.#pending) {
|
|
31
|
+
reject(new Error("MCP server exited unexpectedly"));
|
|
32
|
+
}
|
|
33
|
+
this.#pending.clear();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Send initialize request per MCP protocol
|
|
37
|
+
await this.#send("initialize", {
|
|
38
|
+
protocolVersion: "2024-11-05",
|
|
39
|
+
capabilities: {},
|
|
40
|
+
clientInfo: { name: "nemoris", version: "0.1.0" },
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Send initialized notification (required by MCP protocol)
|
|
44
|
+
this.#process.stdin.write(JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }) + "\n");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async callTool(name, args) {
|
|
48
|
+
if (!this.#process) throw new Error("MCP server not running");
|
|
49
|
+
const result = await this.#send("tools/call", { name, arguments: args });
|
|
50
|
+
if (result.content && Array.isArray(result.content)) {
|
|
51
|
+
return result.content.map(c => c.text || JSON.stringify(c)).join("\n");
|
|
52
|
+
}
|
|
53
|
+
return JSON.stringify(result);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async listTools() {
|
|
57
|
+
if (!this.#process) throw new Error("MCP server not running");
|
|
58
|
+
const result = await this.#send("tools/list", {});
|
|
59
|
+
return result.tools || [];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
isAlive() {
|
|
63
|
+
return this.#process !== null && !this.#process.killed;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
kill() {
|
|
67
|
+
if (this.#process) {
|
|
68
|
+
this.#process.kill();
|
|
69
|
+
this.#process = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#send(method, params) {
|
|
74
|
+
const id = ++this.#requestId;
|
|
75
|
+
const message = JSON.stringify({ jsonrpc: "2.0", id, method, params });
|
|
76
|
+
this.#process.stdin.write(message + "\n");
|
|
77
|
+
|
|
78
|
+
const timeout = this.#config.timeout || 30000;
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const timer = setTimeout(() => {
|
|
81
|
+
this.#pending.delete(id);
|
|
82
|
+
reject(new Error(`MCP request timed out after ${timeout}ms`));
|
|
83
|
+
}, timeout);
|
|
84
|
+
this.#pending.set(id, { resolve: (v) => { clearTimeout(timer); resolve(v); }, reject: (e) => { clearTimeout(timer); reject(e); } });
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#drainBuffer() {
|
|
89
|
+
const lines = this.#buffer.split("\n");
|
|
90
|
+
this.#buffer = lines.pop() || "";
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
if (!line.trim()) continue;
|
|
93
|
+
try {
|
|
94
|
+
const msg = JSON.parse(line);
|
|
95
|
+
if (msg.id && this.#pending.has(msg.id)) {
|
|
96
|
+
const { resolve, reject } = this.#pending.get(msg.id);
|
|
97
|
+
this.#pending.delete(msg.id);
|
|
98
|
+
if (msg.error) reject(new Error(msg.error.message || "MCP error"));
|
|
99
|
+
else resolve(msg.result);
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
// Non-JSON line — ignore
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export function executeCancelTask(input, context) {
|
|
2
|
-
const { taskId } = input;
|
|
3
|
-
if (!taskId) throw new Error("taskId is required");
|
|
4
|
-
const task = context.taskContract.transition(taskId, "cancelled", context.callerAgentId);
|
|
5
|
-
return { status: "cancelled", taskId: task.task_id };
|
|
6
|
-
}
|
|
1
|
+
export function executeCancelTask(input, context) {
|
|
2
|
+
const { taskId } = input;
|
|
3
|
+
if (!taskId) throw new Error("taskId is required");
|
|
4
|
+
const task = context.taskContract.transition(taskId, "cancelled", context.callerAgentId);
|
|
5
|
+
return { status: "cancelled", taskId: task.task_id };
|
|
6
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export function executeCompleteTask(input, context) {
|
|
2
|
-
const { taskId, result } = input;
|
|
3
|
-
if (!taskId) throw new Error("taskId is required");
|
|
4
|
-
const task = context.taskContract.transition(taskId, "completed", context.callerAgentId, { result });
|
|
5
|
-
return { status: "completed", taskId: task.task_id };
|
|
6
|
-
}
|
|
1
|
+
export function executeCompleteTask(input, context) {
|
|
2
|
+
const { taskId, result } = input;
|
|
3
|
+
if (!taskId) throw new Error("taskId is required");
|
|
4
|
+
const task = context.taskContract.transition(taskId, "completed", context.callerAgentId, { result });
|
|
5
|
+
return { status: "completed", taskId: task.task_id };
|
|
6
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export function executeFailTask(input, context) {
|
|
2
|
-
const { taskId, error } = input;
|
|
3
|
-
if (!taskId) throw new Error("taskId is required");
|
|
4
|
-
const task = context.taskContract.transition(taskId, "failed", context.callerAgentId, { error });
|
|
5
|
-
return { status: "failed", taskId: task.task_id };
|
|
6
|
-
}
|
|
1
|
+
export function executeFailTask(input, context) {
|
|
2
|
+
const { taskId, error } = input;
|
|
3
|
+
if (!taskId) throw new Error("taskId is required");
|
|
4
|
+
const task = context.taskContract.transition(taskId, "failed", context.callerAgentId, { error });
|
|
5
|
+
return { status: "failed", taskId: task.task_id };
|
|
6
|
+
}
|