@raindrop-ai/claude-code 0.0.5 → 0.0.7

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/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.5";
648
+ var PACKAGE_VERSION = "0.0.7";
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@raindrop-ai/claude-code",
3
- "version": "0.0.5",
4
- "description": "Raindrop observability for Claude Code CLI automatic session, tool call, and prompt tracing via hooks",
3
+ "version": "0.0.7",
4
+ "description": "Raindrop observability for Claude Code CLI \u2014 automatic session, tool call, and prompt tracing via hooks",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "dist/index.cjs",