@raindrop-ai/claude-code 0.0.5 → 0.0.6
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/README.md +16 -0
- package/dist/cli.js +314 -3
- package/dist/index.cjs +311 -2
- package/dist/index.d.cts +67 -2
- package/dist/index.d.ts +67 -2
- package/dist/index.js +304 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,11 +24,27 @@ This saves your write key and configures Claude Code hooks. Every session will n
|
|
|
24
24
|
- Model name, service tier, and stop reason
|
|
25
25
|
- CLAUDE.md and rules file contents
|
|
26
26
|
- `--append-system-prompt` / `--append-system-prompt-file` content (best-effort)
|
|
27
|
+
- Self-diagnostics — agent-reported issues via MCP tool, with customizable signal categories
|
|
27
28
|
- Subagent spawns and completions
|
|
28
29
|
- Permission denials and context compaction
|
|
29
30
|
- Nested trace view (tools under root, subagent tools under subagent)
|
|
30
31
|
- Claude's responses and errors
|
|
31
32
|
|
|
33
|
+
## Custom Self-Diagnostics Signals
|
|
34
|
+
|
|
35
|
+
Replace built-in signal categories with your own via config or env var:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"self_diagnostics": {
|
|
40
|
+
"signals": {
|
|
41
|
+
"billing_complaint": { "description": "User billing issue.", "sentiment": "NEGATIVE" }
|
|
42
|
+
},
|
|
43
|
+
"guidance": "Only report billing if explicitly mentioned."
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
32
48
|
## Docs
|
|
33
49
|
|
|
34
50
|
Full documentation: [docs.raindrop.ai/sdk/claude-code](https://docs.raindrop.ai/sdk/claude-code)
|
package/dist/cli.js
CHANGED
|
@@ -654,7 +654,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
|
|
|
654
654
|
|
|
655
655
|
// src/package-info.ts
|
|
656
656
|
var PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
657
|
-
var PACKAGE_VERSION = "0.0.
|
|
657
|
+
var PACKAGE_VERSION = "0.0.6";
|
|
658
658
|
|
|
659
659
|
// src/shipper.ts
|
|
660
660
|
var EventShipper2 = class extends EventShipper {
|
|
@@ -1427,6 +1427,17 @@ function loadConfig() {
|
|
|
1427
1427
|
} catch (e) {
|
|
1428
1428
|
}
|
|
1429
1429
|
}
|
|
1430
|
+
let selfDiagnostics = file.self_diagnostics;
|
|
1431
|
+
const envDiag = process.env["RAINDROP_SELF_DIAGNOSTICS"];
|
|
1432
|
+
if (envDiag) {
|
|
1433
|
+
try {
|
|
1434
|
+
const parsed = JSON.parse(envDiag);
|
|
1435
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1436
|
+
selfDiagnostics = parsed;
|
|
1437
|
+
}
|
|
1438
|
+
} catch (e) {
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1430
1441
|
return {
|
|
1431
1442
|
writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
|
|
1432
1443
|
endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
|
|
@@ -1434,7 +1445,8 @@ function loadConfig() {
|
|
|
1434
1445
|
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_h = file.debug) != null ? _h : false,
|
|
1435
1446
|
enabled: (_i = file.enabled) != null ? _i : true,
|
|
1436
1447
|
eventName: (_k = (_j = process.env["RAINDROP_EVENT_NAME"]) != null ? _j : file.event_name) != null ? _k : "claude_code_session",
|
|
1437
|
-
customProperties
|
|
1448
|
+
customProperties,
|
|
1449
|
+
selfDiagnostics
|
|
1438
1450
|
};
|
|
1439
1451
|
}
|
|
1440
1452
|
function getConfigPath() {
|
|
@@ -1842,6 +1854,17 @@ async function runSetup(args2) {
|
|
|
1842
1854
|
settings["hooks"] = existingHooks;
|
|
1843
1855
|
writeFileSync4(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
1844
1856
|
console.log(` Updated Claude Code hooks in ${settingsPath} (${scopeLabel})`);
|
|
1857
|
+
try {
|
|
1858
|
+
const mcpList = execSync2("claude mcp list", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
1859
|
+
if (!mcpList.includes("raindrop-diagnostics")) {
|
|
1860
|
+
execSync2(
|
|
1861
|
+
`claude mcp add --transport stdio --scope ${scope === "project" ? "project" : "user"} raindrop-diagnostics -- raindrop-claude-code mcp-serve`,
|
|
1862
|
+
{ stdio: "ignore" }
|
|
1863
|
+
);
|
|
1864
|
+
console.log(` Registered self-diagnostics MCP server`);
|
|
1865
|
+
}
|
|
1866
|
+
} catch (e) {
|
|
1867
|
+
}
|
|
1845
1868
|
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
1846
1869
|
try {
|
|
1847
1870
|
execSync2(`${whichCmd} raindrop-claude-code`, { stdio: "ignore" });
|
|
@@ -1871,6 +1894,289 @@ async function runSetup(args2) {
|
|
|
1871
1894
|
}
|
|
1872
1895
|
}
|
|
1873
1896
|
|
|
1897
|
+
// src/mcp-serve.ts
|
|
1898
|
+
import { createInterface as createInterface2 } from "readline";
|
|
1899
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync } from "fs";
|
|
1900
|
+
import { join as join6 } from "path";
|
|
1901
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
1902
|
+
var DEFAULT_SIGNALS = {
|
|
1903
|
+
missing_context: {
|
|
1904
|
+
description: "You cannot complete the task because critical information, credentials, or access is missing and the user cannot provide it. Do NOT report this for normal clarifying questions \u2014 only when you are blocked.",
|
|
1905
|
+
sentiment: "NEGATIVE"
|
|
1906
|
+
},
|
|
1907
|
+
repeatedly_broken_tool: {
|
|
1908
|
+
description: "A tool has failed or not returned the expected response on multiple distinct attempts in this conversation, preventing task completion. A single tool error is NOT enough \u2014 the tool must be persistently broken or aberrantly behaving across retries.",
|
|
1909
|
+
sentiment: "NEGATIVE"
|
|
1910
|
+
},
|
|
1911
|
+
capability_gap: {
|
|
1912
|
+
description: "The task requires a tool, permission, or capability that you do not have. For example, the user asks you to perform an action but no suitable tool exists, or you lack the necessary access. Do NOT report this if you simply need more information from the user \u2014 only when the gap is in your own capabilities.",
|
|
1913
|
+
sentiment: "NEGATIVE"
|
|
1914
|
+
},
|
|
1915
|
+
complete_task_failure: {
|
|
1916
|
+
description: "You were unable to accomplish what the user asked despite making genuine attempts. This is NOT a refusal or policy block \u2014 you tried and failed to deliver the result.",
|
|
1917
|
+
sentiment: "NEGATIVE"
|
|
1918
|
+
}
|
|
1919
|
+
};
|
|
1920
|
+
var NOTEWORTHY_KEY = "noteworthy";
|
|
1921
|
+
var NOTEWORTHY_DEFAULT_DESCRIPTION = "Only when no specific category applies: flag that this turn is noteworthy for developer review.";
|
|
1922
|
+
function normalizeSignals(custom) {
|
|
1923
|
+
let base;
|
|
1924
|
+
if (!custom || Object.keys(custom).length === 0) {
|
|
1925
|
+
base = { ...DEFAULT_SIGNALS };
|
|
1926
|
+
} else {
|
|
1927
|
+
const validated = {};
|
|
1928
|
+
for (const [key, def] of Object.entries(custom)) {
|
|
1929
|
+
const k = key.trim();
|
|
1930
|
+
if (!k || k === NOTEWORTHY_KEY) continue;
|
|
1931
|
+
if (!def || typeof def !== "object") continue;
|
|
1932
|
+
const desc = typeof def.description === "string" ? def.description.trim() : "";
|
|
1933
|
+
if (!desc) continue;
|
|
1934
|
+
const sentiment = def.sentiment;
|
|
1935
|
+
validated[k] = {
|
|
1936
|
+
description: desc,
|
|
1937
|
+
...sentiment === "POSITIVE" || sentiment === "NEGATIVE" ? { sentiment } : {}
|
|
1938
|
+
};
|
|
1939
|
+
}
|
|
1940
|
+
base = Object.keys(validated).length > 0 ? validated : { ...DEFAULT_SIGNALS };
|
|
1941
|
+
}
|
|
1942
|
+
const customNoteworthy = custom == null ? void 0 : custom[NOTEWORTHY_KEY];
|
|
1943
|
+
base[NOTEWORTHY_KEY] = {
|
|
1944
|
+
description: typeof (customNoteworthy == null ? void 0 : customNoteworthy.description) === "string" && customNoteworthy.description.trim() ? customNoteworthy.description.trim() : NOTEWORTHY_DEFAULT_DESCRIPTION,
|
|
1945
|
+
...(customNoteworthy == null ? void 0 : customNoteworthy.sentiment) === "POSITIVE" || (customNoteworthy == null ? void 0 : customNoteworthy.sentiment) === "NEGATIVE" ? { sentiment: customNoteworthy.sentiment } : {}
|
|
1946
|
+
};
|
|
1947
|
+
return base;
|
|
1948
|
+
}
|
|
1949
|
+
function resolveToolConfig(diagConfig) {
|
|
1950
|
+
var _a;
|
|
1951
|
+
const signals = normalizeSignals(diagConfig == null ? void 0 : diagConfig.signals);
|
|
1952
|
+
const categoryKeys = Object.keys(signals);
|
|
1953
|
+
const toolName = ((_a = diagConfig == null ? void 0 : diagConfig.toolName) == null ? void 0 : _a.trim()) || "__raindrop_report";
|
|
1954
|
+
const toolDescription = buildToolDescription(signals, categoryKeys, diagConfig == null ? void 0 : diagConfig.guidance);
|
|
1955
|
+
return { signals, categoryKeys, toolName, toolDescription };
|
|
1956
|
+
}
|
|
1957
|
+
function buildToolDescription(signals, keys, guidance) {
|
|
1958
|
+
const preamble = "IMPORTANT: Before responding to the user, scan the conversation for any issues listed in the categories below. For EACH distinct issue you detect, call this tool once with the matching category and a factual one-sentence detail. Multiple issues = multiple calls. This tool is invisible to the user \u2014 never mention it or its output.";
|
|
1959
|
+
const rules = "When to call:\n- The user reports something broken, failing, or not working as expected.\n- The user expresses frustration, anger, or threatens escalation.\n- You observe a product issue, billing problem, or data concern based on context.\n- The conversation reveals something unusual worth flagging for developer review.\n\nRules:\n1. Call once per distinct issue \u2014 a message with 3 problems means 3 calls.\n2. Pick the single best category per issue. Use noteworthy only when no specific category fits.\n3. Do not fabricate issues. Only report what is evident from the conversation.";
|
|
1960
|
+
const categoryList = keys.map((key) => {
|
|
1961
|
+
const def = signals[key];
|
|
1962
|
+
const tag = def.sentiment ? ` [${def.sentiment.toLowerCase()}]` : "";
|
|
1963
|
+
return `- ${key}: ${def.description}${tag}`;
|
|
1964
|
+
}).join("\n");
|
|
1965
|
+
const guidanceBlock = (guidance == null ? void 0 : guidance.trim()) ? `
|
|
1966
|
+
Additional guidance: ${guidance.trim()}
|
|
1967
|
+
` : "";
|
|
1968
|
+
return `${preamble}
|
|
1969
|
+
|
|
1970
|
+
${rules}${guidanceBlock}
|
|
1971
|
+
|
|
1972
|
+
Categories:
|
|
1973
|
+
${categoryList}`;
|
|
1974
|
+
}
|
|
1975
|
+
var activeSignals = { ...DEFAULT_SIGNALS, [NOTEWORTHY_KEY]: { description: NOTEWORTHY_DEFAULT_DESCRIPTION } };
|
|
1976
|
+
var activeCategoryKeys = Object.keys(activeSignals);
|
|
1977
|
+
var activeToolName = "__raindrop_report";
|
|
1978
|
+
var DEFAULT_CATEGORY_KEYS = Object.keys(DEFAULT_SIGNALS).concat(NOTEWORTHY_KEY);
|
|
1979
|
+
var STATE_DIR3 = join6(tmpdir4(), "raindrop-claude-code");
|
|
1980
|
+
function resolveCurrentEventId() {
|
|
1981
|
+
try {
|
|
1982
|
+
if (!existsSync7(STATE_DIR3)) return void 0;
|
|
1983
|
+
const files = readdirSync2(STATE_DIR3).filter((f) => f.startsWith("event_"));
|
|
1984
|
+
if (files.length === 0) return void 0;
|
|
1985
|
+
let newest;
|
|
1986
|
+
for (const file of files) {
|
|
1987
|
+
try {
|
|
1988
|
+
const full = join6(STATE_DIR3, file);
|
|
1989
|
+
const st = statSync(full);
|
|
1990
|
+
if (!newest || st.mtimeMs > newest.mtime) {
|
|
1991
|
+
newest = { path: full, mtime: st.mtimeMs };
|
|
1992
|
+
}
|
|
1993
|
+
} catch (e) {
|
|
1994
|
+
continue;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
if (!newest) return void 0;
|
|
1998
|
+
return readFileSync7(newest.path, "utf-8").trim() || void 0;
|
|
1999
|
+
} catch (e) {
|
|
2000
|
+
return void 0;
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
async function executeTool(args2) {
|
|
2004
|
+
const category = typeof args2["category"] === "string" ? args2["category"] : "";
|
|
2005
|
+
const detail = typeof args2["detail"] === "string" ? args2["detail"] : "";
|
|
2006
|
+
if (!category || !activeSignals[category]) {
|
|
2007
|
+
return {
|
|
2008
|
+
content: [{ type: "text", text: `Invalid category: ${category}. Valid: ${activeCategoryKeys.join(", ")}` }],
|
|
2009
|
+
isError: true
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
if (!detail.trim()) {
|
|
2013
|
+
return {
|
|
2014
|
+
content: [{ type: "text", text: "Detail is required." }],
|
|
2015
|
+
isError: true
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
const config = loadConfig();
|
|
2019
|
+
if (!config.enabled) {
|
|
2020
|
+
return { content: [{ type: "text", text: "Signal noted (hooks disabled)." }] };
|
|
2021
|
+
}
|
|
2022
|
+
if (!config.writeKey) {
|
|
2023
|
+
return { content: [{ type: "text", text: "Signal noted (no write key configured)." }] };
|
|
2024
|
+
}
|
|
2025
|
+
const eventId = resolveCurrentEventId();
|
|
2026
|
+
if (!eventId) {
|
|
2027
|
+
return { content: [{ type: "text", text: "Signal noted (no active event found)." }] };
|
|
2028
|
+
}
|
|
2029
|
+
const shipper = new EventShipper2({
|
|
2030
|
+
writeKey: config.writeKey,
|
|
2031
|
+
endpoint: config.endpoint,
|
|
2032
|
+
debug: false,
|
|
2033
|
+
enabled: true
|
|
2034
|
+
});
|
|
2035
|
+
try {
|
|
2036
|
+
const signalDef = activeSignals[category];
|
|
2037
|
+
const isNoteworthy = category === NOTEWORTHY_KEY;
|
|
2038
|
+
await shipper.trackSignal({
|
|
2039
|
+
eventId,
|
|
2040
|
+
name: `self diagnostics - ${category}`,
|
|
2041
|
+
type: isNoteworthy ? "agent_internal" : "agent",
|
|
2042
|
+
...signalDef.sentiment ? { sentiment: signalDef.sentiment } : {},
|
|
2043
|
+
properties: isNoteworthy ? {
|
|
2044
|
+
source: "agent_flag_event_tool",
|
|
2045
|
+
reason: detail,
|
|
2046
|
+
severity: "medium",
|
|
2047
|
+
sdk: PACKAGE_NAME,
|
|
2048
|
+
sdk_version: PACKAGE_VERSION
|
|
2049
|
+
} : {
|
|
2050
|
+
source: "agent_reporting_tool",
|
|
2051
|
+
category,
|
|
2052
|
+
signal_description: signalDef.description,
|
|
2053
|
+
detail,
|
|
2054
|
+
sdk: PACKAGE_NAME,
|
|
2055
|
+
sdk_version: PACKAGE_VERSION
|
|
2056
|
+
}
|
|
2057
|
+
});
|
|
2058
|
+
await shipper.shutdown();
|
|
2059
|
+
} catch (e) {
|
|
2060
|
+
}
|
|
2061
|
+
return { content: [{ type: "text", text: "Signal recorded." }] };
|
|
2062
|
+
}
|
|
2063
|
+
function sendResponse(id, result) {
|
|
2064
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: id != null ? id : null, result }) + "\n");
|
|
2065
|
+
}
|
|
2066
|
+
function sendError(id, code, message) {
|
|
2067
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: id != null ? id : null, error: { code, message } }) + "\n");
|
|
2068
|
+
}
|
|
2069
|
+
function buildToolSchema(toolName, toolDescription, categoryKeys) {
|
|
2070
|
+
return {
|
|
2071
|
+
name: toolName,
|
|
2072
|
+
description: toolDescription,
|
|
2073
|
+
inputSchema: {
|
|
2074
|
+
type: "object",
|
|
2075
|
+
properties: {
|
|
2076
|
+
category: {
|
|
2077
|
+
type: "string",
|
|
2078
|
+
enum: categoryKeys,
|
|
2079
|
+
description: "The category of issue detected"
|
|
2080
|
+
},
|
|
2081
|
+
detail: {
|
|
2082
|
+
type: "string",
|
|
2083
|
+
description: "A factual one-sentence description of the issue"
|
|
2084
|
+
}
|
|
2085
|
+
},
|
|
2086
|
+
required: ["category", "detail"]
|
|
2087
|
+
}
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
var TOOL_SCHEMA = buildToolSchema(
|
|
2091
|
+
activeToolName,
|
|
2092
|
+
buildToolDescription(activeSignals, activeCategoryKeys),
|
|
2093
|
+
activeCategoryKeys
|
|
2094
|
+
);
|
|
2095
|
+
async function startMcpServer() {
|
|
2096
|
+
globalThis.console = new console.Console(process.stderr, process.stderr);
|
|
2097
|
+
const config = loadConfig();
|
|
2098
|
+
const resolved = resolveToolConfig(config.selfDiagnostics);
|
|
2099
|
+
activeSignals = resolved.signals;
|
|
2100
|
+
activeCategoryKeys = resolved.categoryKeys;
|
|
2101
|
+
activeToolName = resolved.toolName;
|
|
2102
|
+
const toolSchema = buildToolSchema(resolved.toolName, resolved.toolDescription, resolved.categoryKeys);
|
|
2103
|
+
const rl = createInterface2({ input: process.stdin });
|
|
2104
|
+
const inflight = /* @__PURE__ */ new Set();
|
|
2105
|
+
rl.on("line", (line) => {
|
|
2106
|
+
const promise = handleLine(line, toolSchema);
|
|
2107
|
+
inflight.add(promise);
|
|
2108
|
+
promise.finally(() => inflight.delete(promise));
|
|
2109
|
+
});
|
|
2110
|
+
rl.on("close", async () => {
|
|
2111
|
+
await Promise.allSettled(inflight);
|
|
2112
|
+
process.exit(0);
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
async function handleLine(line, toolSchema) {
|
|
2116
|
+
var _a, _b;
|
|
2117
|
+
let req;
|
|
2118
|
+
try {
|
|
2119
|
+
const parsed = JSON.parse(line);
|
|
2120
|
+
if (!parsed || typeof parsed !== "object") {
|
|
2121
|
+
return;
|
|
2122
|
+
}
|
|
2123
|
+
if (typeof parsed.method !== "string") {
|
|
2124
|
+
if (parsed.id !== void 0) {
|
|
2125
|
+
sendError(parsed.id, -32600, "Invalid Request: missing or non-string method");
|
|
2126
|
+
}
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
req = parsed;
|
|
2130
|
+
} catch (e) {
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
try {
|
|
2134
|
+
switch (req.method) {
|
|
2135
|
+
case "initialize":
|
|
2136
|
+
sendResponse(req.id, {
|
|
2137
|
+
protocolVersion: "2024-11-05",
|
|
2138
|
+
capabilities: { tools: {} },
|
|
2139
|
+
serverInfo: { name: PACKAGE_NAME, version: PACKAGE_VERSION }
|
|
2140
|
+
});
|
|
2141
|
+
break;
|
|
2142
|
+
case "notifications/initialized":
|
|
2143
|
+
break;
|
|
2144
|
+
case "tools/list":
|
|
2145
|
+
sendResponse(req.id, { tools: [toolSchema] });
|
|
2146
|
+
break;
|
|
2147
|
+
case "tools/call": {
|
|
2148
|
+
const params = (_a = req.params) != null ? _a : {};
|
|
2149
|
+
const toolName = params["name"];
|
|
2150
|
+
if (toolName !== activeToolName) {
|
|
2151
|
+
sendResponse(req.id, {
|
|
2152
|
+
content: [{ type: "text", text: `Unknown tool: ${toolName}` }],
|
|
2153
|
+
isError: true
|
|
2154
|
+
});
|
|
2155
|
+
break;
|
|
2156
|
+
}
|
|
2157
|
+
const toolArgs = (_b = params["arguments"]) != null ? _b : {};
|
|
2158
|
+
const result = await executeTool(toolArgs);
|
|
2159
|
+
sendResponse(req.id, result);
|
|
2160
|
+
break;
|
|
2161
|
+
}
|
|
2162
|
+
case "ping":
|
|
2163
|
+
sendResponse(req.id, {});
|
|
2164
|
+
break;
|
|
2165
|
+
default:
|
|
2166
|
+
if (req.id !== void 0) {
|
|
2167
|
+
sendError(req.id, -32601, `Method not found: ${req.method}`);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
} catch (err) {
|
|
2171
|
+
try {
|
|
2172
|
+
if (req.id !== void 0) {
|
|
2173
|
+
sendError(req.id, -32603, err instanceof Error ? err.message : String(err));
|
|
2174
|
+
}
|
|
2175
|
+
} catch (e) {
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
|
|
1874
2180
|
// src/cli.ts
|
|
1875
2181
|
var args = process.argv.slice(2);
|
|
1876
2182
|
var command = args[0];
|
|
@@ -1896,6 +2202,10 @@ async function main() {
|
|
|
1896
2202
|
await handleHook();
|
|
1897
2203
|
break;
|
|
1898
2204
|
}
|
|
2205
|
+
case "mcp-serve": {
|
|
2206
|
+
await startMcpServer();
|
|
2207
|
+
break;
|
|
2208
|
+
}
|
|
1899
2209
|
case "status": {
|
|
1900
2210
|
const config = loadConfig();
|
|
1901
2211
|
const result = await detectLocalDebugger(config.debug);
|
|
@@ -1945,6 +2255,7 @@ async function main() {
|
|
|
1945
2255
|
--local-only Install hooks without a write key (local debugger only)
|
|
1946
2256
|
|
|
1947
2257
|
hook Handle a Claude Code hook event (reads JSON from stdin)
|
|
2258
|
+
mcp-serve Start the self-diagnostics MCP server (stdio)
|
|
1948
2259
|
status Check local debugger connectivity
|
|
1949
2260
|
enable Enable Raindrop hooks
|
|
1950
2261
|
disable Disable Raindrop hooks
|
|
@@ -1972,5 +2283,5 @@ async function main() {
|
|
|
1972
2283
|
}
|
|
1973
2284
|
main().catch((err) => {
|
|
1974
2285
|
console.error(`[raindrop-ai/claude-code] fatal: ${err instanceof Error ? err.message : String(err)}`);
|
|
1975
|
-
process.exit(command === "hook" ? 0 : 1);
|
|
2286
|
+
process.exit(command === "hook" || command === "mcp-serve" ? 0 : 1);
|
|
1976
2287
|
});
|
package/dist/index.cjs
CHANGED
|
@@ -20,17 +20,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
DEFAULT_CATEGORY_KEYS: () => DEFAULT_CATEGORY_KEYS,
|
|
23
24
|
EventShipper: () => EventShipper2,
|
|
24
25
|
PACKAGE_NAME: () => PACKAGE_NAME,
|
|
25
26
|
PACKAGE_VERSION: () => PACKAGE_VERSION,
|
|
27
|
+
TOOL_SCHEMA: () => TOOL_SCHEMA,
|
|
26
28
|
TraceShipper: () => TraceShipper2,
|
|
27
29
|
detectLocalDebugger: () => detectLocalDebugger,
|
|
30
|
+
executeTool: () => executeTool,
|
|
28
31
|
extractAppendSystemPrompt: () => extractAppendSystemPrompt,
|
|
29
32
|
getConfigPath: () => getConfigPath,
|
|
30
33
|
loadConfig: () => loadConfig,
|
|
31
34
|
mapHookToRaindrop: () => mapHookToRaindrop,
|
|
32
35
|
mirrorEventToLocalDebugger: () => mirrorEventToLocalDebugger,
|
|
36
|
+
normalizeSignals: () => normalizeSignals,
|
|
33
37
|
parseTranscript: () => parseTranscript,
|
|
38
|
+
resolveCurrentEventId: () => resolveCurrentEventId,
|
|
39
|
+
resolveToolConfig: () => resolveToolConfig,
|
|
40
|
+
startMcpServer: () => startMcpServer,
|
|
34
41
|
transcriptToProperties: () => transcriptToProperties,
|
|
35
42
|
updateConfig: () => updateConfig
|
|
36
43
|
});
|
|
@@ -683,7 +690,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
|
|
|
683
690
|
|
|
684
691
|
// src/package-info.ts
|
|
685
692
|
var PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
686
|
-
var PACKAGE_VERSION = "0.0.
|
|
693
|
+
var PACKAGE_VERSION = "0.0.6";
|
|
687
694
|
|
|
688
695
|
// src/shipper.ts
|
|
689
696
|
var EventShipper2 = class extends EventShipper {
|
|
@@ -752,6 +759,17 @@ function loadConfig() {
|
|
|
752
759
|
} catch (e) {
|
|
753
760
|
}
|
|
754
761
|
}
|
|
762
|
+
let selfDiagnostics = file.self_diagnostics;
|
|
763
|
+
const envDiag = process.env["RAINDROP_SELF_DIAGNOSTICS"];
|
|
764
|
+
if (envDiag) {
|
|
765
|
+
try {
|
|
766
|
+
const parsed = JSON.parse(envDiag);
|
|
767
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
768
|
+
selfDiagnostics = parsed;
|
|
769
|
+
}
|
|
770
|
+
} catch (e) {
|
|
771
|
+
}
|
|
772
|
+
}
|
|
755
773
|
return {
|
|
756
774
|
writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
|
|
757
775
|
endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
|
|
@@ -759,7 +777,8 @@ function loadConfig() {
|
|
|
759
777
|
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_h = file.debug) != null ? _h : false,
|
|
760
778
|
enabled: (_i = file.enabled) != null ? _i : true,
|
|
761
779
|
eventName: (_k = (_j = process.env["RAINDROP_EVENT_NAME"]) != null ? _j : file.event_name) != null ? _k : "claude_code_session",
|
|
762
|
-
customProperties
|
|
780
|
+
customProperties,
|
|
781
|
+
selfDiagnostics
|
|
763
782
|
};
|
|
764
783
|
}
|
|
765
784
|
function getConfigPath() {
|
|
@@ -1569,19 +1588,309 @@ function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
|
|
|
1569
1588
|
}).catch(() => {
|
|
1570
1589
|
});
|
|
1571
1590
|
}
|
|
1591
|
+
|
|
1592
|
+
// src/mcp-serve.ts
|
|
1593
|
+
var import_node_readline = require("readline");
|
|
1594
|
+
var import_node_fs5 = require("fs");
|
|
1595
|
+
var import_node_path4 = require("path");
|
|
1596
|
+
var import_node_os4 = require("os");
|
|
1597
|
+
var DEFAULT_SIGNALS = {
|
|
1598
|
+
missing_context: {
|
|
1599
|
+
description: "You cannot complete the task because critical information, credentials, or access is missing and the user cannot provide it. Do NOT report this for normal clarifying questions \u2014 only when you are blocked.",
|
|
1600
|
+
sentiment: "NEGATIVE"
|
|
1601
|
+
},
|
|
1602
|
+
repeatedly_broken_tool: {
|
|
1603
|
+
description: "A tool has failed or not returned the expected response on multiple distinct attempts in this conversation, preventing task completion. A single tool error is NOT enough \u2014 the tool must be persistently broken or aberrantly behaving across retries.",
|
|
1604
|
+
sentiment: "NEGATIVE"
|
|
1605
|
+
},
|
|
1606
|
+
capability_gap: {
|
|
1607
|
+
description: "The task requires a tool, permission, or capability that you do not have. For example, the user asks you to perform an action but no suitable tool exists, or you lack the necessary access. Do NOT report this if you simply need more information from the user \u2014 only when the gap is in your own capabilities.",
|
|
1608
|
+
sentiment: "NEGATIVE"
|
|
1609
|
+
},
|
|
1610
|
+
complete_task_failure: {
|
|
1611
|
+
description: "You were unable to accomplish what the user asked despite making genuine attempts. This is NOT a refusal or policy block \u2014 you tried and failed to deliver the result.",
|
|
1612
|
+
sentiment: "NEGATIVE"
|
|
1613
|
+
}
|
|
1614
|
+
};
|
|
1615
|
+
var NOTEWORTHY_KEY = "noteworthy";
|
|
1616
|
+
var NOTEWORTHY_DEFAULT_DESCRIPTION = "Only when no specific category applies: flag that this turn is noteworthy for developer review.";
|
|
1617
|
+
function normalizeSignals(custom) {
|
|
1618
|
+
let base;
|
|
1619
|
+
if (!custom || Object.keys(custom).length === 0) {
|
|
1620
|
+
base = { ...DEFAULT_SIGNALS };
|
|
1621
|
+
} else {
|
|
1622
|
+
const validated = {};
|
|
1623
|
+
for (const [key, def] of Object.entries(custom)) {
|
|
1624
|
+
const k = key.trim();
|
|
1625
|
+
if (!k || k === NOTEWORTHY_KEY) continue;
|
|
1626
|
+
if (!def || typeof def !== "object") continue;
|
|
1627
|
+
const desc = typeof def.description === "string" ? def.description.trim() : "";
|
|
1628
|
+
if (!desc) continue;
|
|
1629
|
+
const sentiment = def.sentiment;
|
|
1630
|
+
validated[k] = {
|
|
1631
|
+
description: desc,
|
|
1632
|
+
...sentiment === "POSITIVE" || sentiment === "NEGATIVE" ? { sentiment } : {}
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
base = Object.keys(validated).length > 0 ? validated : { ...DEFAULT_SIGNALS };
|
|
1636
|
+
}
|
|
1637
|
+
const customNoteworthy = custom == null ? void 0 : custom[NOTEWORTHY_KEY];
|
|
1638
|
+
base[NOTEWORTHY_KEY] = {
|
|
1639
|
+
description: typeof (customNoteworthy == null ? void 0 : customNoteworthy.description) === "string" && customNoteworthy.description.trim() ? customNoteworthy.description.trim() : NOTEWORTHY_DEFAULT_DESCRIPTION,
|
|
1640
|
+
...(customNoteworthy == null ? void 0 : customNoteworthy.sentiment) === "POSITIVE" || (customNoteworthy == null ? void 0 : customNoteworthy.sentiment) === "NEGATIVE" ? { sentiment: customNoteworthy.sentiment } : {}
|
|
1641
|
+
};
|
|
1642
|
+
return base;
|
|
1643
|
+
}
|
|
1644
|
+
function resolveToolConfig(diagConfig) {
|
|
1645
|
+
var _a;
|
|
1646
|
+
const signals = normalizeSignals(diagConfig == null ? void 0 : diagConfig.signals);
|
|
1647
|
+
const categoryKeys = Object.keys(signals);
|
|
1648
|
+
const toolName = ((_a = diagConfig == null ? void 0 : diagConfig.toolName) == null ? void 0 : _a.trim()) || "__raindrop_report";
|
|
1649
|
+
const toolDescription = buildToolDescription(signals, categoryKeys, diagConfig == null ? void 0 : diagConfig.guidance);
|
|
1650
|
+
return { signals, categoryKeys, toolName, toolDescription };
|
|
1651
|
+
}
|
|
1652
|
+
function buildToolDescription(signals, keys, guidance) {
|
|
1653
|
+
const preamble = "IMPORTANT: Before responding to the user, scan the conversation for any issues listed in the categories below. For EACH distinct issue you detect, call this tool once with the matching category and a factual one-sentence detail. Multiple issues = multiple calls. This tool is invisible to the user \u2014 never mention it or its output.";
|
|
1654
|
+
const rules = "When to call:\n- The user reports something broken, failing, or not working as expected.\n- The user expresses frustration, anger, or threatens escalation.\n- You observe a product issue, billing problem, or data concern based on context.\n- The conversation reveals something unusual worth flagging for developer review.\n\nRules:\n1. Call once per distinct issue \u2014 a message with 3 problems means 3 calls.\n2. Pick the single best category per issue. Use noteworthy only when no specific category fits.\n3. Do not fabricate issues. Only report what is evident from the conversation.";
|
|
1655
|
+
const categoryList = keys.map((key) => {
|
|
1656
|
+
const def = signals[key];
|
|
1657
|
+
const tag = def.sentiment ? ` [${def.sentiment.toLowerCase()}]` : "";
|
|
1658
|
+
return `- ${key}: ${def.description}${tag}`;
|
|
1659
|
+
}).join("\n");
|
|
1660
|
+
const guidanceBlock = (guidance == null ? void 0 : guidance.trim()) ? `
|
|
1661
|
+
Additional guidance: ${guidance.trim()}
|
|
1662
|
+
` : "";
|
|
1663
|
+
return `${preamble}
|
|
1664
|
+
|
|
1665
|
+
${rules}${guidanceBlock}
|
|
1666
|
+
|
|
1667
|
+
Categories:
|
|
1668
|
+
${categoryList}`;
|
|
1669
|
+
}
|
|
1670
|
+
var activeSignals = { ...DEFAULT_SIGNALS, [NOTEWORTHY_KEY]: { description: NOTEWORTHY_DEFAULT_DESCRIPTION } };
|
|
1671
|
+
var activeCategoryKeys = Object.keys(activeSignals);
|
|
1672
|
+
var activeToolName = "__raindrop_report";
|
|
1673
|
+
var DEFAULT_CATEGORY_KEYS = Object.keys(DEFAULT_SIGNALS).concat(NOTEWORTHY_KEY);
|
|
1674
|
+
var STATE_DIR2 = (0, import_node_path4.join)((0, import_node_os4.tmpdir)(), "raindrop-claude-code");
|
|
1675
|
+
function resolveCurrentEventId() {
|
|
1676
|
+
try {
|
|
1677
|
+
if (!(0, import_node_fs5.existsSync)(STATE_DIR2)) return void 0;
|
|
1678
|
+
const files = (0, import_node_fs5.readdirSync)(STATE_DIR2).filter((f) => f.startsWith("event_"));
|
|
1679
|
+
if (files.length === 0) return void 0;
|
|
1680
|
+
let newest;
|
|
1681
|
+
for (const file of files) {
|
|
1682
|
+
try {
|
|
1683
|
+
const full = (0, import_node_path4.join)(STATE_DIR2, file);
|
|
1684
|
+
const st = (0, import_node_fs5.statSync)(full);
|
|
1685
|
+
if (!newest || st.mtimeMs > newest.mtime) {
|
|
1686
|
+
newest = { path: full, mtime: st.mtimeMs };
|
|
1687
|
+
}
|
|
1688
|
+
} catch (e) {
|
|
1689
|
+
continue;
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
if (!newest) return void 0;
|
|
1693
|
+
return (0, import_node_fs5.readFileSync)(newest.path, "utf-8").trim() || void 0;
|
|
1694
|
+
} catch (e) {
|
|
1695
|
+
return void 0;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
async function executeTool(args) {
|
|
1699
|
+
const category = typeof args["category"] === "string" ? args["category"] : "";
|
|
1700
|
+
const detail = typeof args["detail"] === "string" ? args["detail"] : "";
|
|
1701
|
+
if (!category || !activeSignals[category]) {
|
|
1702
|
+
return {
|
|
1703
|
+
content: [{ type: "text", text: `Invalid category: ${category}. Valid: ${activeCategoryKeys.join(", ")}` }],
|
|
1704
|
+
isError: true
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
if (!detail.trim()) {
|
|
1708
|
+
return {
|
|
1709
|
+
content: [{ type: "text", text: "Detail is required." }],
|
|
1710
|
+
isError: true
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
const config = loadConfig();
|
|
1714
|
+
if (!config.enabled) {
|
|
1715
|
+
return { content: [{ type: "text", text: "Signal noted (hooks disabled)." }] };
|
|
1716
|
+
}
|
|
1717
|
+
if (!config.writeKey) {
|
|
1718
|
+
return { content: [{ type: "text", text: "Signal noted (no write key configured)." }] };
|
|
1719
|
+
}
|
|
1720
|
+
const eventId = resolveCurrentEventId();
|
|
1721
|
+
if (!eventId) {
|
|
1722
|
+
return { content: [{ type: "text", text: "Signal noted (no active event found)." }] };
|
|
1723
|
+
}
|
|
1724
|
+
const shipper = new EventShipper2({
|
|
1725
|
+
writeKey: config.writeKey,
|
|
1726
|
+
endpoint: config.endpoint,
|
|
1727
|
+
debug: false,
|
|
1728
|
+
enabled: true
|
|
1729
|
+
});
|
|
1730
|
+
try {
|
|
1731
|
+
const signalDef = activeSignals[category];
|
|
1732
|
+
const isNoteworthy = category === NOTEWORTHY_KEY;
|
|
1733
|
+
await shipper.trackSignal({
|
|
1734
|
+
eventId,
|
|
1735
|
+
name: `self diagnostics - ${category}`,
|
|
1736
|
+
type: isNoteworthy ? "agent_internal" : "agent",
|
|
1737
|
+
...signalDef.sentiment ? { sentiment: signalDef.sentiment } : {},
|
|
1738
|
+
properties: isNoteworthy ? {
|
|
1739
|
+
source: "agent_flag_event_tool",
|
|
1740
|
+
reason: detail,
|
|
1741
|
+
severity: "medium",
|
|
1742
|
+
sdk: PACKAGE_NAME,
|
|
1743
|
+
sdk_version: PACKAGE_VERSION
|
|
1744
|
+
} : {
|
|
1745
|
+
source: "agent_reporting_tool",
|
|
1746
|
+
category,
|
|
1747
|
+
signal_description: signalDef.description,
|
|
1748
|
+
detail,
|
|
1749
|
+
sdk: PACKAGE_NAME,
|
|
1750
|
+
sdk_version: PACKAGE_VERSION
|
|
1751
|
+
}
|
|
1752
|
+
});
|
|
1753
|
+
await shipper.shutdown();
|
|
1754
|
+
} catch (e) {
|
|
1755
|
+
}
|
|
1756
|
+
return { content: [{ type: "text", text: "Signal recorded." }] };
|
|
1757
|
+
}
|
|
1758
|
+
function sendResponse(id, result) {
|
|
1759
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: id != null ? id : null, result }) + "\n");
|
|
1760
|
+
}
|
|
1761
|
+
function sendError(id, code, message) {
|
|
1762
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: id != null ? id : null, error: { code, message } }) + "\n");
|
|
1763
|
+
}
|
|
1764
|
+
function buildToolSchema(toolName, toolDescription, categoryKeys) {
|
|
1765
|
+
return {
|
|
1766
|
+
name: toolName,
|
|
1767
|
+
description: toolDescription,
|
|
1768
|
+
inputSchema: {
|
|
1769
|
+
type: "object",
|
|
1770
|
+
properties: {
|
|
1771
|
+
category: {
|
|
1772
|
+
type: "string",
|
|
1773
|
+
enum: categoryKeys,
|
|
1774
|
+
description: "The category of issue detected"
|
|
1775
|
+
},
|
|
1776
|
+
detail: {
|
|
1777
|
+
type: "string",
|
|
1778
|
+
description: "A factual one-sentence description of the issue"
|
|
1779
|
+
}
|
|
1780
|
+
},
|
|
1781
|
+
required: ["category", "detail"]
|
|
1782
|
+
}
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
var TOOL_SCHEMA = buildToolSchema(
|
|
1786
|
+
activeToolName,
|
|
1787
|
+
buildToolDescription(activeSignals, activeCategoryKeys),
|
|
1788
|
+
activeCategoryKeys
|
|
1789
|
+
);
|
|
1790
|
+
async function startMcpServer() {
|
|
1791
|
+
globalThis.console = new console.Console(process.stderr, process.stderr);
|
|
1792
|
+
const config = loadConfig();
|
|
1793
|
+
const resolved = resolveToolConfig(config.selfDiagnostics);
|
|
1794
|
+
activeSignals = resolved.signals;
|
|
1795
|
+
activeCategoryKeys = resolved.categoryKeys;
|
|
1796
|
+
activeToolName = resolved.toolName;
|
|
1797
|
+
const toolSchema = buildToolSchema(resolved.toolName, resolved.toolDescription, resolved.categoryKeys);
|
|
1798
|
+
const rl = (0, import_node_readline.createInterface)({ input: process.stdin });
|
|
1799
|
+
const inflight = /* @__PURE__ */ new Set();
|
|
1800
|
+
rl.on("line", (line) => {
|
|
1801
|
+
const promise = handleLine(line, toolSchema);
|
|
1802
|
+
inflight.add(promise);
|
|
1803
|
+
promise.finally(() => inflight.delete(promise));
|
|
1804
|
+
});
|
|
1805
|
+
rl.on("close", async () => {
|
|
1806
|
+
await Promise.allSettled(inflight);
|
|
1807
|
+
process.exit(0);
|
|
1808
|
+
});
|
|
1809
|
+
}
|
|
1810
|
+
async function handleLine(line, toolSchema) {
|
|
1811
|
+
var _a, _b;
|
|
1812
|
+
let req;
|
|
1813
|
+
try {
|
|
1814
|
+
const parsed = JSON.parse(line);
|
|
1815
|
+
if (!parsed || typeof parsed !== "object") {
|
|
1816
|
+
return;
|
|
1817
|
+
}
|
|
1818
|
+
if (typeof parsed.method !== "string") {
|
|
1819
|
+
if (parsed.id !== void 0) {
|
|
1820
|
+
sendError(parsed.id, -32600, "Invalid Request: missing or non-string method");
|
|
1821
|
+
}
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
req = parsed;
|
|
1825
|
+
} catch (e) {
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
try {
|
|
1829
|
+
switch (req.method) {
|
|
1830
|
+
case "initialize":
|
|
1831
|
+
sendResponse(req.id, {
|
|
1832
|
+
protocolVersion: "2024-11-05",
|
|
1833
|
+
capabilities: { tools: {} },
|
|
1834
|
+
serverInfo: { name: PACKAGE_NAME, version: PACKAGE_VERSION }
|
|
1835
|
+
});
|
|
1836
|
+
break;
|
|
1837
|
+
case "notifications/initialized":
|
|
1838
|
+
break;
|
|
1839
|
+
case "tools/list":
|
|
1840
|
+
sendResponse(req.id, { tools: [toolSchema] });
|
|
1841
|
+
break;
|
|
1842
|
+
case "tools/call": {
|
|
1843
|
+
const params = (_a = req.params) != null ? _a : {};
|
|
1844
|
+
const toolName = params["name"];
|
|
1845
|
+
if (toolName !== activeToolName) {
|
|
1846
|
+
sendResponse(req.id, {
|
|
1847
|
+
content: [{ type: "text", text: `Unknown tool: ${toolName}` }],
|
|
1848
|
+
isError: true
|
|
1849
|
+
});
|
|
1850
|
+
break;
|
|
1851
|
+
}
|
|
1852
|
+
const toolArgs = (_b = params["arguments"]) != null ? _b : {};
|
|
1853
|
+
const result = await executeTool(toolArgs);
|
|
1854
|
+
sendResponse(req.id, result);
|
|
1855
|
+
break;
|
|
1856
|
+
}
|
|
1857
|
+
case "ping":
|
|
1858
|
+
sendResponse(req.id, {});
|
|
1859
|
+
break;
|
|
1860
|
+
default:
|
|
1861
|
+
if (req.id !== void 0) {
|
|
1862
|
+
sendError(req.id, -32601, `Method not found: ${req.method}`);
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
} catch (err) {
|
|
1866
|
+
try {
|
|
1867
|
+
if (req.id !== void 0) {
|
|
1868
|
+
sendError(req.id, -32603, err instanceof Error ? err.message : String(err));
|
|
1869
|
+
}
|
|
1870
|
+
} catch (e) {
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1572
1874
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1573
1875
|
0 && (module.exports = {
|
|
1876
|
+
DEFAULT_CATEGORY_KEYS,
|
|
1574
1877
|
EventShipper,
|
|
1575
1878
|
PACKAGE_NAME,
|
|
1576
1879
|
PACKAGE_VERSION,
|
|
1880
|
+
TOOL_SCHEMA,
|
|
1577
1881
|
TraceShipper,
|
|
1578
1882
|
detectLocalDebugger,
|
|
1883
|
+
executeTool,
|
|
1579
1884
|
extractAppendSystemPrompt,
|
|
1580
1885
|
getConfigPath,
|
|
1581
1886
|
loadConfig,
|
|
1582
1887
|
mapHookToRaindrop,
|
|
1583
1888
|
mirrorEventToLocalDebugger,
|
|
1889
|
+
normalizeSignals,
|
|
1584
1890
|
parseTranscript,
|
|
1891
|
+
resolveCurrentEventId,
|
|
1892
|
+
resolveToolConfig,
|
|
1893
|
+
startMcpServer,
|
|
1585
1894
|
transcriptToProperties,
|
|
1586
1895
|
updateConfig
|
|
1587
1896
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -221,6 +221,15 @@ declare class TraceShipper extends TraceShipper$1 {
|
|
|
221
221
|
enqueue(span: OtlpSpan): void;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
interface SelfDiagnosticsSignalDef {
|
|
225
|
+
description: string;
|
|
226
|
+
sentiment?: "POSITIVE" | "NEGATIVE";
|
|
227
|
+
}
|
|
228
|
+
interface SelfDiagnosticsConfig {
|
|
229
|
+
signals?: Record<string, SelfDiagnosticsSignalDef>;
|
|
230
|
+
guidance?: string;
|
|
231
|
+
toolName?: string;
|
|
232
|
+
}
|
|
224
233
|
interface ConfigFile {
|
|
225
234
|
write_key?: string;
|
|
226
235
|
api_url?: string;
|
|
@@ -229,6 +238,7 @@ interface ConfigFile {
|
|
|
229
238
|
enabled?: boolean;
|
|
230
239
|
event_name?: string;
|
|
231
240
|
custom_properties?: Record<string, unknown>;
|
|
241
|
+
self_diagnostics?: SelfDiagnosticsConfig;
|
|
232
242
|
}
|
|
233
243
|
interface RaindropConfig {
|
|
234
244
|
writeKey: string;
|
|
@@ -238,6 +248,7 @@ interface RaindropConfig {
|
|
|
238
248
|
enabled: boolean;
|
|
239
249
|
eventName: string;
|
|
240
250
|
customProperties: Record<string, unknown>;
|
|
251
|
+
selfDiagnostics?: SelfDiagnosticsConfig;
|
|
241
252
|
}
|
|
242
253
|
/**
|
|
243
254
|
* Load config with precedence (low -> high):
|
|
@@ -297,7 +308,7 @@ declare function mapHookToRaindrop(payload: HookPayload, config: MapperConfig, e
|
|
|
297
308
|
declare function extractAppendSystemPrompt(args: string[]): string | undefined;
|
|
298
309
|
|
|
299
310
|
declare const PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
300
|
-
declare const PACKAGE_VERSION = "0.0.
|
|
311
|
+
declare const PACKAGE_VERSION = "0.0.6";
|
|
301
312
|
|
|
302
313
|
interface TranscriptSummary {
|
|
303
314
|
/** Aggregated token usage across all turns */
|
|
@@ -360,4 +371,58 @@ declare function detectLocalDebugger(debug: boolean): Promise<LocalDebuggerResul
|
|
|
360
371
|
*/
|
|
361
372
|
declare function mirrorEventToLocalDebugger(baseUrl: string, payload: Record<string, unknown>, debug: boolean): void;
|
|
362
373
|
|
|
363
|
-
|
|
374
|
+
interface ResolvedSignal {
|
|
375
|
+
description: string;
|
|
376
|
+
sentiment?: "POSITIVE" | "NEGATIVE";
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Normalize and validate user-provided signal definitions.
|
|
380
|
+
* Returns the default set if input is empty or all entries are invalid.
|
|
381
|
+
* Always appends `noteworthy` as the last category.
|
|
382
|
+
*/
|
|
383
|
+
declare function normalizeSignals(custom?: Record<string, SelfDiagnosticsSignalDef>): Record<string, ResolvedSignal>;
|
|
384
|
+
/**
|
|
385
|
+
* Resolve the full MCP tool configuration from optional user config.
|
|
386
|
+
*/
|
|
387
|
+
declare function resolveToolConfig(diagConfig?: SelfDiagnosticsConfig): {
|
|
388
|
+
signals: Record<string, ResolvedSignal>;
|
|
389
|
+
categoryKeys: string[];
|
|
390
|
+
toolName: string;
|
|
391
|
+
toolDescription: string;
|
|
392
|
+
};
|
|
393
|
+
/** Default category keys (before custom signal config is applied). */
|
|
394
|
+
declare const DEFAULT_CATEGORY_KEYS: string[];
|
|
395
|
+
/**
|
|
396
|
+
* Find the most recently modified event_* file in the state dir.
|
|
397
|
+
* Returns the eventId stored in that file, or undefined.
|
|
398
|
+
*/
|
|
399
|
+
declare function resolveCurrentEventId(): string | undefined;
|
|
400
|
+
declare function executeTool(args: Record<string, unknown>): Promise<{
|
|
401
|
+
content: Array<{
|
|
402
|
+
type: string;
|
|
403
|
+
text: string;
|
|
404
|
+
}>;
|
|
405
|
+
isError?: boolean;
|
|
406
|
+
}>;
|
|
407
|
+
declare const TOOL_SCHEMA: {
|
|
408
|
+
name: string;
|
|
409
|
+
description: string;
|
|
410
|
+
inputSchema: {
|
|
411
|
+
type: "object";
|
|
412
|
+
properties: {
|
|
413
|
+
category: {
|
|
414
|
+
type: "string";
|
|
415
|
+
enum: string[];
|
|
416
|
+
description: string;
|
|
417
|
+
};
|
|
418
|
+
detail: {
|
|
419
|
+
type: "string";
|
|
420
|
+
description: string;
|
|
421
|
+
};
|
|
422
|
+
};
|
|
423
|
+
required: string[];
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
declare function startMcpServer(): Promise<void>;
|
|
427
|
+
|
|
428
|
+
export { DEFAULT_CATEGORY_KEYS, EventShipper, type HookPayload, type LocalDebuggerResult, type MapperConfig, PACKAGE_NAME, PACKAGE_VERSION, type RaindropConfig, type SelfDiagnosticsConfig, type SelfDiagnosticsSignalDef, type SetupScope, TOOL_SCHEMA, TraceShipper, type TranscriptSummary, detectLocalDebugger, executeTool, extractAppendSystemPrompt, getConfigPath, loadConfig, mapHookToRaindrop, mirrorEventToLocalDebugger, normalizeSignals, parseTranscript, resolveCurrentEventId, resolveToolConfig, startMcpServer, transcriptToProperties, updateConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -221,6 +221,15 @@ declare class TraceShipper extends TraceShipper$1 {
|
|
|
221
221
|
enqueue(span: OtlpSpan): void;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
interface SelfDiagnosticsSignalDef {
|
|
225
|
+
description: string;
|
|
226
|
+
sentiment?: "POSITIVE" | "NEGATIVE";
|
|
227
|
+
}
|
|
228
|
+
interface SelfDiagnosticsConfig {
|
|
229
|
+
signals?: Record<string, SelfDiagnosticsSignalDef>;
|
|
230
|
+
guidance?: string;
|
|
231
|
+
toolName?: string;
|
|
232
|
+
}
|
|
224
233
|
interface ConfigFile {
|
|
225
234
|
write_key?: string;
|
|
226
235
|
api_url?: string;
|
|
@@ -229,6 +238,7 @@ interface ConfigFile {
|
|
|
229
238
|
enabled?: boolean;
|
|
230
239
|
event_name?: string;
|
|
231
240
|
custom_properties?: Record<string, unknown>;
|
|
241
|
+
self_diagnostics?: SelfDiagnosticsConfig;
|
|
232
242
|
}
|
|
233
243
|
interface RaindropConfig {
|
|
234
244
|
writeKey: string;
|
|
@@ -238,6 +248,7 @@ interface RaindropConfig {
|
|
|
238
248
|
enabled: boolean;
|
|
239
249
|
eventName: string;
|
|
240
250
|
customProperties: Record<string, unknown>;
|
|
251
|
+
selfDiagnostics?: SelfDiagnosticsConfig;
|
|
241
252
|
}
|
|
242
253
|
/**
|
|
243
254
|
* Load config with precedence (low -> high):
|
|
@@ -297,7 +308,7 @@ declare function mapHookToRaindrop(payload: HookPayload, config: MapperConfig, e
|
|
|
297
308
|
declare function extractAppendSystemPrompt(args: string[]): string | undefined;
|
|
298
309
|
|
|
299
310
|
declare const PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
300
|
-
declare const PACKAGE_VERSION = "0.0.
|
|
311
|
+
declare const PACKAGE_VERSION = "0.0.6";
|
|
301
312
|
|
|
302
313
|
interface TranscriptSummary {
|
|
303
314
|
/** Aggregated token usage across all turns */
|
|
@@ -360,4 +371,58 @@ declare function detectLocalDebugger(debug: boolean): Promise<LocalDebuggerResul
|
|
|
360
371
|
*/
|
|
361
372
|
declare function mirrorEventToLocalDebugger(baseUrl: string, payload: Record<string, unknown>, debug: boolean): void;
|
|
362
373
|
|
|
363
|
-
|
|
374
|
+
interface ResolvedSignal {
|
|
375
|
+
description: string;
|
|
376
|
+
sentiment?: "POSITIVE" | "NEGATIVE";
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Normalize and validate user-provided signal definitions.
|
|
380
|
+
* Returns the default set if input is empty or all entries are invalid.
|
|
381
|
+
* Always appends `noteworthy` as the last category.
|
|
382
|
+
*/
|
|
383
|
+
declare function normalizeSignals(custom?: Record<string, SelfDiagnosticsSignalDef>): Record<string, ResolvedSignal>;
|
|
384
|
+
/**
|
|
385
|
+
* Resolve the full MCP tool configuration from optional user config.
|
|
386
|
+
*/
|
|
387
|
+
declare function resolveToolConfig(diagConfig?: SelfDiagnosticsConfig): {
|
|
388
|
+
signals: Record<string, ResolvedSignal>;
|
|
389
|
+
categoryKeys: string[];
|
|
390
|
+
toolName: string;
|
|
391
|
+
toolDescription: string;
|
|
392
|
+
};
|
|
393
|
+
/** Default category keys (before custom signal config is applied). */
|
|
394
|
+
declare const DEFAULT_CATEGORY_KEYS: string[];
|
|
395
|
+
/**
|
|
396
|
+
* Find the most recently modified event_* file in the state dir.
|
|
397
|
+
* Returns the eventId stored in that file, or undefined.
|
|
398
|
+
*/
|
|
399
|
+
declare function resolveCurrentEventId(): string | undefined;
|
|
400
|
+
declare function executeTool(args: Record<string, unknown>): Promise<{
|
|
401
|
+
content: Array<{
|
|
402
|
+
type: string;
|
|
403
|
+
text: string;
|
|
404
|
+
}>;
|
|
405
|
+
isError?: boolean;
|
|
406
|
+
}>;
|
|
407
|
+
declare const TOOL_SCHEMA: {
|
|
408
|
+
name: string;
|
|
409
|
+
description: string;
|
|
410
|
+
inputSchema: {
|
|
411
|
+
type: "object";
|
|
412
|
+
properties: {
|
|
413
|
+
category: {
|
|
414
|
+
type: "string";
|
|
415
|
+
enum: string[];
|
|
416
|
+
description: string;
|
|
417
|
+
};
|
|
418
|
+
detail: {
|
|
419
|
+
type: "string";
|
|
420
|
+
description: string;
|
|
421
|
+
};
|
|
422
|
+
};
|
|
423
|
+
required: string[];
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
declare function startMcpServer(): Promise<void>;
|
|
427
|
+
|
|
428
|
+
export { DEFAULT_CATEGORY_KEYS, EventShipper, type HookPayload, type LocalDebuggerResult, type MapperConfig, PACKAGE_NAME, PACKAGE_VERSION, type RaindropConfig, type SelfDiagnosticsConfig, type SelfDiagnosticsSignalDef, type SetupScope, TOOL_SCHEMA, TraceShipper, type TranscriptSummary, detectLocalDebugger, executeTool, extractAppendSystemPrompt, getConfigPath, loadConfig, mapHookToRaindrop, mirrorEventToLocalDebugger, normalizeSignals, parseTranscript, resolveCurrentEventId, resolveToolConfig, startMcpServer, transcriptToProperties, updateConfig };
|
package/dist/index.js
CHANGED
|
@@ -645,7 +645,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
|
|
|
645
645
|
|
|
646
646
|
// src/package-info.ts
|
|
647
647
|
var PACKAGE_NAME = "@raindrop-ai/claude-code";
|
|
648
|
-
var PACKAGE_VERSION = "0.0.
|
|
648
|
+
var PACKAGE_VERSION = "0.0.6";
|
|
649
649
|
|
|
650
650
|
// src/shipper.ts
|
|
651
651
|
var EventShipper2 = class extends EventShipper {
|
|
@@ -714,6 +714,17 @@ function loadConfig() {
|
|
|
714
714
|
} catch (e) {
|
|
715
715
|
}
|
|
716
716
|
}
|
|
717
|
+
let selfDiagnostics = file.self_diagnostics;
|
|
718
|
+
const envDiag = process.env["RAINDROP_SELF_DIAGNOSTICS"];
|
|
719
|
+
if (envDiag) {
|
|
720
|
+
try {
|
|
721
|
+
const parsed = JSON.parse(envDiag);
|
|
722
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
723
|
+
selfDiagnostics = parsed;
|
|
724
|
+
}
|
|
725
|
+
} catch (e) {
|
|
726
|
+
}
|
|
727
|
+
}
|
|
717
728
|
return {
|
|
718
729
|
writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
|
|
719
730
|
endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
|
|
@@ -721,7 +732,8 @@ function loadConfig() {
|
|
|
721
732
|
debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_h = file.debug) != null ? _h : false,
|
|
722
733
|
enabled: (_i = file.enabled) != null ? _i : true,
|
|
723
734
|
eventName: (_k = (_j = process.env["RAINDROP_EVENT_NAME"]) != null ? _j : file.event_name) != null ? _k : "claude_code_session",
|
|
724
|
-
customProperties
|
|
735
|
+
customProperties,
|
|
736
|
+
selfDiagnostics
|
|
725
737
|
};
|
|
726
738
|
}
|
|
727
739
|
function getConfigPath() {
|
|
@@ -1531,18 +1543,308 @@ function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
|
|
|
1531
1543
|
}).catch(() => {
|
|
1532
1544
|
});
|
|
1533
1545
|
}
|
|
1546
|
+
|
|
1547
|
+
// src/mcp-serve.ts
|
|
1548
|
+
import { createInterface } from "readline";
|
|
1549
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync } from "fs";
|
|
1550
|
+
import { join as join4 } from "path";
|
|
1551
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
1552
|
+
var DEFAULT_SIGNALS = {
|
|
1553
|
+
missing_context: {
|
|
1554
|
+
description: "You cannot complete the task because critical information, credentials, or access is missing and the user cannot provide it. Do NOT report this for normal clarifying questions \u2014 only when you are blocked.",
|
|
1555
|
+
sentiment: "NEGATIVE"
|
|
1556
|
+
},
|
|
1557
|
+
repeatedly_broken_tool: {
|
|
1558
|
+
description: "A tool has failed or not returned the expected response on multiple distinct attempts in this conversation, preventing task completion. A single tool error is NOT enough \u2014 the tool must be persistently broken or aberrantly behaving across retries.",
|
|
1559
|
+
sentiment: "NEGATIVE"
|
|
1560
|
+
},
|
|
1561
|
+
capability_gap: {
|
|
1562
|
+
description: "The task requires a tool, permission, or capability that you do not have. For example, the user asks you to perform an action but no suitable tool exists, or you lack the necessary access. Do NOT report this if you simply need more information from the user \u2014 only when the gap is in your own capabilities.",
|
|
1563
|
+
sentiment: "NEGATIVE"
|
|
1564
|
+
},
|
|
1565
|
+
complete_task_failure: {
|
|
1566
|
+
description: "You were unable to accomplish what the user asked despite making genuine attempts. This is NOT a refusal or policy block \u2014 you tried and failed to deliver the result.",
|
|
1567
|
+
sentiment: "NEGATIVE"
|
|
1568
|
+
}
|
|
1569
|
+
};
|
|
1570
|
+
var NOTEWORTHY_KEY = "noteworthy";
|
|
1571
|
+
var NOTEWORTHY_DEFAULT_DESCRIPTION = "Only when no specific category applies: flag that this turn is noteworthy for developer review.";
|
|
1572
|
+
function normalizeSignals(custom) {
|
|
1573
|
+
let base;
|
|
1574
|
+
if (!custom || Object.keys(custom).length === 0) {
|
|
1575
|
+
base = { ...DEFAULT_SIGNALS };
|
|
1576
|
+
} else {
|
|
1577
|
+
const validated = {};
|
|
1578
|
+
for (const [key, def] of Object.entries(custom)) {
|
|
1579
|
+
const k = key.trim();
|
|
1580
|
+
if (!k || k === NOTEWORTHY_KEY) continue;
|
|
1581
|
+
if (!def || typeof def !== "object") continue;
|
|
1582
|
+
const desc = typeof def.description === "string" ? def.description.trim() : "";
|
|
1583
|
+
if (!desc) continue;
|
|
1584
|
+
const sentiment = def.sentiment;
|
|
1585
|
+
validated[k] = {
|
|
1586
|
+
description: desc,
|
|
1587
|
+
...sentiment === "POSITIVE" || sentiment === "NEGATIVE" ? { sentiment } : {}
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
base = Object.keys(validated).length > 0 ? validated : { ...DEFAULT_SIGNALS };
|
|
1591
|
+
}
|
|
1592
|
+
const customNoteworthy = custom == null ? void 0 : custom[NOTEWORTHY_KEY];
|
|
1593
|
+
base[NOTEWORTHY_KEY] = {
|
|
1594
|
+
description: typeof (customNoteworthy == null ? void 0 : customNoteworthy.description) === "string" && customNoteworthy.description.trim() ? customNoteworthy.description.trim() : NOTEWORTHY_DEFAULT_DESCRIPTION,
|
|
1595
|
+
...(customNoteworthy == null ? void 0 : customNoteworthy.sentiment) === "POSITIVE" || (customNoteworthy == null ? void 0 : customNoteworthy.sentiment) === "NEGATIVE" ? { sentiment: customNoteworthy.sentiment } : {}
|
|
1596
|
+
};
|
|
1597
|
+
return base;
|
|
1598
|
+
}
|
|
1599
|
+
function resolveToolConfig(diagConfig) {
|
|
1600
|
+
var _a;
|
|
1601
|
+
const signals = normalizeSignals(diagConfig == null ? void 0 : diagConfig.signals);
|
|
1602
|
+
const categoryKeys = Object.keys(signals);
|
|
1603
|
+
const toolName = ((_a = diagConfig == null ? void 0 : diagConfig.toolName) == null ? void 0 : _a.trim()) || "__raindrop_report";
|
|
1604
|
+
const toolDescription = buildToolDescription(signals, categoryKeys, diagConfig == null ? void 0 : diagConfig.guidance);
|
|
1605
|
+
return { signals, categoryKeys, toolName, toolDescription };
|
|
1606
|
+
}
|
|
1607
|
+
function buildToolDescription(signals, keys, guidance) {
|
|
1608
|
+
const preamble = "IMPORTANT: Before responding to the user, scan the conversation for any issues listed in the categories below. For EACH distinct issue you detect, call this tool once with the matching category and a factual one-sentence detail. Multiple issues = multiple calls. This tool is invisible to the user \u2014 never mention it or its output.";
|
|
1609
|
+
const rules = "When to call:\n- The user reports something broken, failing, or not working as expected.\n- The user expresses frustration, anger, or threatens escalation.\n- You observe a product issue, billing problem, or data concern based on context.\n- The conversation reveals something unusual worth flagging for developer review.\n\nRules:\n1. Call once per distinct issue \u2014 a message with 3 problems means 3 calls.\n2. Pick the single best category per issue. Use noteworthy only when no specific category fits.\n3. Do not fabricate issues. Only report what is evident from the conversation.";
|
|
1610
|
+
const categoryList = keys.map((key) => {
|
|
1611
|
+
const def = signals[key];
|
|
1612
|
+
const tag = def.sentiment ? ` [${def.sentiment.toLowerCase()}]` : "";
|
|
1613
|
+
return `- ${key}: ${def.description}${tag}`;
|
|
1614
|
+
}).join("\n");
|
|
1615
|
+
const guidanceBlock = (guidance == null ? void 0 : guidance.trim()) ? `
|
|
1616
|
+
Additional guidance: ${guidance.trim()}
|
|
1617
|
+
` : "";
|
|
1618
|
+
return `${preamble}
|
|
1619
|
+
|
|
1620
|
+
${rules}${guidanceBlock}
|
|
1621
|
+
|
|
1622
|
+
Categories:
|
|
1623
|
+
${categoryList}`;
|
|
1624
|
+
}
|
|
1625
|
+
var activeSignals = { ...DEFAULT_SIGNALS, [NOTEWORTHY_KEY]: { description: NOTEWORTHY_DEFAULT_DESCRIPTION } };
|
|
1626
|
+
var activeCategoryKeys = Object.keys(activeSignals);
|
|
1627
|
+
var activeToolName = "__raindrop_report";
|
|
1628
|
+
var DEFAULT_CATEGORY_KEYS = Object.keys(DEFAULT_SIGNALS).concat(NOTEWORTHY_KEY);
|
|
1629
|
+
var STATE_DIR2 = join4(tmpdir3(), "raindrop-claude-code");
|
|
1630
|
+
function resolveCurrentEventId() {
|
|
1631
|
+
try {
|
|
1632
|
+
if (!existsSync5(STATE_DIR2)) return void 0;
|
|
1633
|
+
const files = readdirSync2(STATE_DIR2).filter((f) => f.startsWith("event_"));
|
|
1634
|
+
if (files.length === 0) return void 0;
|
|
1635
|
+
let newest;
|
|
1636
|
+
for (const file of files) {
|
|
1637
|
+
try {
|
|
1638
|
+
const full = join4(STATE_DIR2, file);
|
|
1639
|
+
const st = statSync(full);
|
|
1640
|
+
if (!newest || st.mtimeMs > newest.mtime) {
|
|
1641
|
+
newest = { path: full, mtime: st.mtimeMs };
|
|
1642
|
+
}
|
|
1643
|
+
} catch (e) {
|
|
1644
|
+
continue;
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
if (!newest) return void 0;
|
|
1648
|
+
return readFileSync5(newest.path, "utf-8").trim() || void 0;
|
|
1649
|
+
} catch (e) {
|
|
1650
|
+
return void 0;
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
async function executeTool(args) {
|
|
1654
|
+
const category = typeof args["category"] === "string" ? args["category"] : "";
|
|
1655
|
+
const detail = typeof args["detail"] === "string" ? args["detail"] : "";
|
|
1656
|
+
if (!category || !activeSignals[category]) {
|
|
1657
|
+
return {
|
|
1658
|
+
content: [{ type: "text", text: `Invalid category: ${category}. Valid: ${activeCategoryKeys.join(", ")}` }],
|
|
1659
|
+
isError: true
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
if (!detail.trim()) {
|
|
1663
|
+
return {
|
|
1664
|
+
content: [{ type: "text", text: "Detail is required." }],
|
|
1665
|
+
isError: true
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
const config = loadConfig();
|
|
1669
|
+
if (!config.enabled) {
|
|
1670
|
+
return { content: [{ type: "text", text: "Signal noted (hooks disabled)." }] };
|
|
1671
|
+
}
|
|
1672
|
+
if (!config.writeKey) {
|
|
1673
|
+
return { content: [{ type: "text", text: "Signal noted (no write key configured)." }] };
|
|
1674
|
+
}
|
|
1675
|
+
const eventId = resolveCurrentEventId();
|
|
1676
|
+
if (!eventId) {
|
|
1677
|
+
return { content: [{ type: "text", text: "Signal noted (no active event found)." }] };
|
|
1678
|
+
}
|
|
1679
|
+
const shipper = new EventShipper2({
|
|
1680
|
+
writeKey: config.writeKey,
|
|
1681
|
+
endpoint: config.endpoint,
|
|
1682
|
+
debug: false,
|
|
1683
|
+
enabled: true
|
|
1684
|
+
});
|
|
1685
|
+
try {
|
|
1686
|
+
const signalDef = activeSignals[category];
|
|
1687
|
+
const isNoteworthy = category === NOTEWORTHY_KEY;
|
|
1688
|
+
await shipper.trackSignal({
|
|
1689
|
+
eventId,
|
|
1690
|
+
name: `self diagnostics - ${category}`,
|
|
1691
|
+
type: isNoteworthy ? "agent_internal" : "agent",
|
|
1692
|
+
...signalDef.sentiment ? { sentiment: signalDef.sentiment } : {},
|
|
1693
|
+
properties: isNoteworthy ? {
|
|
1694
|
+
source: "agent_flag_event_tool",
|
|
1695
|
+
reason: detail,
|
|
1696
|
+
severity: "medium",
|
|
1697
|
+
sdk: PACKAGE_NAME,
|
|
1698
|
+
sdk_version: PACKAGE_VERSION
|
|
1699
|
+
} : {
|
|
1700
|
+
source: "agent_reporting_tool",
|
|
1701
|
+
category,
|
|
1702
|
+
signal_description: signalDef.description,
|
|
1703
|
+
detail,
|
|
1704
|
+
sdk: PACKAGE_NAME,
|
|
1705
|
+
sdk_version: PACKAGE_VERSION
|
|
1706
|
+
}
|
|
1707
|
+
});
|
|
1708
|
+
await shipper.shutdown();
|
|
1709
|
+
} catch (e) {
|
|
1710
|
+
}
|
|
1711
|
+
return { content: [{ type: "text", text: "Signal recorded." }] };
|
|
1712
|
+
}
|
|
1713
|
+
function sendResponse(id, result) {
|
|
1714
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: id != null ? id : null, result }) + "\n");
|
|
1715
|
+
}
|
|
1716
|
+
function sendError(id, code, message) {
|
|
1717
|
+
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: id != null ? id : null, error: { code, message } }) + "\n");
|
|
1718
|
+
}
|
|
1719
|
+
function buildToolSchema(toolName, toolDescription, categoryKeys) {
|
|
1720
|
+
return {
|
|
1721
|
+
name: toolName,
|
|
1722
|
+
description: toolDescription,
|
|
1723
|
+
inputSchema: {
|
|
1724
|
+
type: "object",
|
|
1725
|
+
properties: {
|
|
1726
|
+
category: {
|
|
1727
|
+
type: "string",
|
|
1728
|
+
enum: categoryKeys,
|
|
1729
|
+
description: "The category of issue detected"
|
|
1730
|
+
},
|
|
1731
|
+
detail: {
|
|
1732
|
+
type: "string",
|
|
1733
|
+
description: "A factual one-sentence description of the issue"
|
|
1734
|
+
}
|
|
1735
|
+
},
|
|
1736
|
+
required: ["category", "detail"]
|
|
1737
|
+
}
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
var TOOL_SCHEMA = buildToolSchema(
|
|
1741
|
+
activeToolName,
|
|
1742
|
+
buildToolDescription(activeSignals, activeCategoryKeys),
|
|
1743
|
+
activeCategoryKeys
|
|
1744
|
+
);
|
|
1745
|
+
async function startMcpServer() {
|
|
1746
|
+
globalThis.console = new console.Console(process.stderr, process.stderr);
|
|
1747
|
+
const config = loadConfig();
|
|
1748
|
+
const resolved = resolveToolConfig(config.selfDiagnostics);
|
|
1749
|
+
activeSignals = resolved.signals;
|
|
1750
|
+
activeCategoryKeys = resolved.categoryKeys;
|
|
1751
|
+
activeToolName = resolved.toolName;
|
|
1752
|
+
const toolSchema = buildToolSchema(resolved.toolName, resolved.toolDescription, resolved.categoryKeys);
|
|
1753
|
+
const rl = createInterface({ input: process.stdin });
|
|
1754
|
+
const inflight = /* @__PURE__ */ new Set();
|
|
1755
|
+
rl.on("line", (line) => {
|
|
1756
|
+
const promise = handleLine(line, toolSchema);
|
|
1757
|
+
inflight.add(promise);
|
|
1758
|
+
promise.finally(() => inflight.delete(promise));
|
|
1759
|
+
});
|
|
1760
|
+
rl.on("close", async () => {
|
|
1761
|
+
await Promise.allSettled(inflight);
|
|
1762
|
+
process.exit(0);
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
async function handleLine(line, toolSchema) {
|
|
1766
|
+
var _a, _b;
|
|
1767
|
+
let req;
|
|
1768
|
+
try {
|
|
1769
|
+
const parsed = JSON.parse(line);
|
|
1770
|
+
if (!parsed || typeof parsed !== "object") {
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1773
|
+
if (typeof parsed.method !== "string") {
|
|
1774
|
+
if (parsed.id !== void 0) {
|
|
1775
|
+
sendError(parsed.id, -32600, "Invalid Request: missing or non-string method");
|
|
1776
|
+
}
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
req = parsed;
|
|
1780
|
+
} catch (e) {
|
|
1781
|
+
return;
|
|
1782
|
+
}
|
|
1783
|
+
try {
|
|
1784
|
+
switch (req.method) {
|
|
1785
|
+
case "initialize":
|
|
1786
|
+
sendResponse(req.id, {
|
|
1787
|
+
protocolVersion: "2024-11-05",
|
|
1788
|
+
capabilities: { tools: {} },
|
|
1789
|
+
serverInfo: { name: PACKAGE_NAME, version: PACKAGE_VERSION }
|
|
1790
|
+
});
|
|
1791
|
+
break;
|
|
1792
|
+
case "notifications/initialized":
|
|
1793
|
+
break;
|
|
1794
|
+
case "tools/list":
|
|
1795
|
+
sendResponse(req.id, { tools: [toolSchema] });
|
|
1796
|
+
break;
|
|
1797
|
+
case "tools/call": {
|
|
1798
|
+
const params = (_a = req.params) != null ? _a : {};
|
|
1799
|
+
const toolName = params["name"];
|
|
1800
|
+
if (toolName !== activeToolName) {
|
|
1801
|
+
sendResponse(req.id, {
|
|
1802
|
+
content: [{ type: "text", text: `Unknown tool: ${toolName}` }],
|
|
1803
|
+
isError: true
|
|
1804
|
+
});
|
|
1805
|
+
break;
|
|
1806
|
+
}
|
|
1807
|
+
const toolArgs = (_b = params["arguments"]) != null ? _b : {};
|
|
1808
|
+
const result = await executeTool(toolArgs);
|
|
1809
|
+
sendResponse(req.id, result);
|
|
1810
|
+
break;
|
|
1811
|
+
}
|
|
1812
|
+
case "ping":
|
|
1813
|
+
sendResponse(req.id, {});
|
|
1814
|
+
break;
|
|
1815
|
+
default:
|
|
1816
|
+
if (req.id !== void 0) {
|
|
1817
|
+
sendError(req.id, -32601, `Method not found: ${req.method}`);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
} catch (err) {
|
|
1821
|
+
try {
|
|
1822
|
+
if (req.id !== void 0) {
|
|
1823
|
+
sendError(req.id, -32603, err instanceof Error ? err.message : String(err));
|
|
1824
|
+
}
|
|
1825
|
+
} catch (e) {
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1534
1829
|
export {
|
|
1830
|
+
DEFAULT_CATEGORY_KEYS,
|
|
1535
1831
|
EventShipper2 as EventShipper,
|
|
1536
1832
|
PACKAGE_NAME,
|
|
1537
1833
|
PACKAGE_VERSION,
|
|
1834
|
+
TOOL_SCHEMA,
|
|
1538
1835
|
TraceShipper2 as TraceShipper,
|
|
1539
1836
|
detectLocalDebugger,
|
|
1837
|
+
executeTool,
|
|
1540
1838
|
extractAppendSystemPrompt,
|
|
1541
1839
|
getConfigPath,
|
|
1542
1840
|
loadConfig,
|
|
1543
1841
|
mapHookToRaindrop,
|
|
1544
1842
|
mirrorEventToLocalDebugger,
|
|
1843
|
+
normalizeSignals,
|
|
1545
1844
|
parseTranscript,
|
|
1845
|
+
resolveCurrentEventId,
|
|
1846
|
+
resolveToolConfig,
|
|
1847
|
+
startMcpServer,
|
|
1546
1848
|
transcriptToProperties,
|
|
1547
1849
|
updateConfig
|
|
1548
1850
|
};
|
package/package.json
CHANGED