chain-insights 0.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/bin/cli.js +10 -0
  4. package/bin/install.cjs +252 -0
  5. package/bin/mcp-proxy.cjs +10 -0
  6. package/dist/active-BSrxLKwn.mjs +50 -0
  7. package/dist/active-BSrxLKwn.mjs.map +1 -0
  8. package/dist/active-Dv7Tu-O4.cjs +68 -0
  9. package/dist/app-BjjuQM0B.mjs +155 -0
  10. package/dist/app-BjjuQM0B.mjs.map +1 -0
  11. package/dist/app-Dq1TdB6p.cjs +161 -0
  12. package/dist/artifact-server-DoxJ7fCx.cjs +47 -0
  13. package/dist/artifact-server-Dxz5YbuQ.mjs +48 -0
  14. package/dist/artifact-server-Dxz5YbuQ.mjs.map +1 -0
  15. package/dist/assets/bg-pattern.png +0 -0
  16. package/dist/assets/logo.png +0 -0
  17. package/dist/call-args-DQA2QcRA.cjs +27 -0
  18. package/dist/call-args-Lk_wOJxd.mjs +29 -0
  19. package/dist/call-args-Lk_wOJxd.mjs.map +1 -0
  20. package/dist/capabilities-CB97WMA5.cjs +83 -0
  21. package/dist/capabilities-DliMBim-.mjs +84 -0
  22. package/dist/capabilities-DliMBim-.mjs.map +1 -0
  23. package/dist/cases-By7INiOa.mjs +6 -0
  24. package/dist/cases-CDcNU91B.cjs +9 -0
  25. package/dist/chunk-CZWwpsFl.cjs +43 -0
  26. package/dist/cli.cjs +752 -0
  27. package/dist/cli.d.cts +1 -0
  28. package/dist/cli.d.mts +1 -0
  29. package/dist/cli.mjs +753 -0
  30. package/dist/cli.mjs.map +1 -0
  31. package/dist/client-D4Bq0rp9.mjs +111 -0
  32. package/dist/client-D4Bq0rp9.mjs.map +1 -0
  33. package/dist/client-D4fZgIaO.cjs +132 -0
  34. package/dist/config-Bmdl5hdk.cjs +67 -0
  35. package/dist/config-BwrBYmiC.mjs +44 -0
  36. package/dist/config-BwrBYmiC.mjs.map +1 -0
  37. package/dist/data-extractor-BNGj7ECT.cjs +347 -0
  38. package/dist/data-extractor-DFzsa5CS.mjs +336 -0
  39. package/dist/data-extractor-DFzsa5CS.mjs.map +1 -0
  40. package/dist/dossier-BsroDgD3.mjs +76 -0
  41. package/dist/dossier-BsroDgD3.mjs.map +1 -0
  42. package/dist/dossier-DtxREpPm.cjs +76 -0
  43. package/dist/evidence-BGcdKxuV.cjs +200 -0
  44. package/dist/evidence-BhvFW-y_.mjs +195 -0
  45. package/dist/evidence-BhvFW-y_.mjs.map +1 -0
  46. package/dist/format-Ce1RObVl.mjs +22 -0
  47. package/dist/format-Ce1RObVl.mjs.map +1 -0
  48. package/dist/format-DOrPvXEr.cjs +20 -0
  49. package/dist/frontmatter-D8wWCeOa.mjs +26 -0
  50. package/dist/frontmatter-D8wWCeOa.mjs.map +1 -0
  51. package/dist/frontmatter-DgAuai7E.cjs +35 -0
  52. package/dist/graph-normalizer-Cv9yK9Pg.mjs +130 -0
  53. package/dist/graph-normalizer-Cv9yK9Pg.mjs.map +1 -0
  54. package/dist/graph-normalizer-DeIj6Ses.cjs +133 -0
  55. package/dist/graph-reports-C4TBjCkM.mjs +63 -0
  56. package/dist/graph-reports-C4TBjCkM.mjs.map +1 -0
  57. package/dist/graph-reports-DU05YCei.cjs +64 -0
  58. package/dist/html-generator-CAv81IWH.cjs +85 -0
  59. package/dist/html-generator-V6Bp0uRb.mjs +68 -0
  60. package/dist/html-generator-V6Bp0uRb.mjs.map +1 -0
  61. package/dist/index.cjs +31 -0
  62. package/dist/index.d.cts +187 -0
  63. package/dist/index.d.cts.map +1 -0
  64. package/dist/index.d.mts +187 -0
  65. package/dist/index.d.mts.map +1 -0
  66. package/dist/index.mjs +9 -0
  67. package/dist/init-BjuFt54X.cjs +232 -0
  68. package/dist/init-CaOsHTIo.mjs +232 -0
  69. package/dist/init-CaOsHTIo.mjs.map +1 -0
  70. package/dist/mcp-proxy.cjs +1257 -0
  71. package/dist/mcp-proxy.d.cts +12 -0
  72. package/dist/mcp-proxy.d.cts.map +1 -0
  73. package/dist/mcp-proxy.d.mts +12 -0
  74. package/dist/mcp-proxy.d.mts.map +1 -0
  75. package/dist/mcp-proxy.mjs +1255 -0
  76. package/dist/mcp-proxy.mjs.map +1 -0
  77. package/dist/output-root-CFYms3ad.cjs +43 -0
  78. package/dist/output-root-CmWM7aV2.mjs +33 -0
  79. package/dist/output-root-CmWM7aV2.mjs.map +1 -0
  80. package/dist/parser-BUIWW1OH.cjs +182 -0
  81. package/dist/parser-DO0_SssG.mjs +182 -0
  82. package/dist/parser-DO0_SssG.mjs.map +1 -0
  83. package/dist/public-tools-D4UI-Zb0.mjs +2554 -0
  84. package/dist/public-tools-D4UI-Zb0.mjs.map +1 -0
  85. package/dist/public-tools-XSpkz2ky.cjs +2556 -0
  86. package/dist/resolver-C2ZS7oC8.mjs +201 -0
  87. package/dist/resolver-C2ZS7oC8.mjs.map +1 -0
  88. package/dist/resolver-zYbu4wDV.cjs +203 -0
  89. package/dist/rolldown-runtime-wcPFST8Q.mjs +13 -0
  90. package/dist/runner-1Eq55OYb.cjs +148 -0
  91. package/dist/runner-BhUHbiHG.mjs +149 -0
  92. package/dist/runner-BhUHbiHG.mjs.map +1 -0
  93. package/dist/schema-4XpzDFQM.cjs +55 -0
  94. package/dist/schema-8d0rVIdZ.mjs +37 -0
  95. package/dist/schema-8d0rVIdZ.mjs.map +1 -0
  96. package/dist/schema-cache-9CksD7tX.mjs +34 -0
  97. package/dist/schema-cache-9CksD7tX.mjs.map +1 -0
  98. package/dist/schema-cache-CgWRCN2N.cjs +36 -0
  99. package/dist/selector-CkFcTXzz.cjs +10 -0
  100. package/dist/selector-xjm6NTHI.mjs +12 -0
  101. package/dist/selector-xjm6NTHI.mjs.map +1 -0
  102. package/dist/server-BkM5xrXb.mjs +45 -0
  103. package/dist/server-BkM5xrXb.mjs.map +1 -0
  104. package/dist/server-DXowbpfi.cjs +54 -0
  105. package/dist/session-BpNylyuJ.cjs +115 -0
  106. package/dist/session-CcTgYxsj.mjs +115 -0
  107. package/dist/session-CcTgYxsj.mjs.map +1 -0
  108. package/dist/setup-DOpKPrlx.cjs +81 -0
  109. package/dist/setup-DyrWHuwQ.mjs +80 -0
  110. package/dist/setup-DyrWHuwQ.mjs.map +1 -0
  111. package/dist/store-BiUhQOIf.cjs +230 -0
  112. package/dist/store-BoWE-Gtl.mjs +225 -0
  113. package/dist/store-BoWE-Gtl.mjs.map +1 -0
  114. package/dist/templates/graph.html +1406 -0
  115. package/dist/tool-visibility-3Z_KvO9Q.mjs +28 -0
  116. package/dist/tool-visibility-3Z_KvO9Q.mjs.map +1 -0
  117. package/dist/tool-visibility-CwgY205r.cjs +36 -0
  118. package/dist/tools-Cp2jAAAb.mjs +100 -0
  119. package/dist/tools-Cp2jAAAb.mjs.map +1 -0
  120. package/dist/tools-f_vJUZAF.cjs +139 -0
  121. package/dist/topup-server-BZuQifvh.cjs +940 -0
  122. package/dist/topup-server-DUjyFftI.mjs +919 -0
  123. package/dist/topup-server-DUjyFftI.mjs.map +1 -0
  124. package/dist/version-1gP19Lhi.mjs +8 -0
  125. package/dist/version-1gP19Lhi.mjs.map +1 -0
  126. package/dist/version-BNGtdpmH.cjs +18 -0
  127. package/dist/viz-BlCJe6Tk.mjs +35 -0
  128. package/dist/viz-BlCJe6Tk.mjs.map +1 -0
  129. package/dist/viz-ClezVXrJ.cjs +44 -0
  130. package/dist/wallet-BMelXBYP.mjs +104 -0
  131. package/dist/wallet-BMelXBYP.mjs.map +1 -0
  132. package/dist/wallet-RnvvSpV2.cjs +146 -0
  133. package/docs/architecture.md +145 -0
  134. package/docs/contributing.md +68 -0
  135. package/docs/debugging.md +68 -0
  136. package/docs/development.md +44 -0
  137. package/docs/graph-tools.md +251 -0
  138. package/docs/images/graph-mcp-iframe.png +0 -0
  139. package/docs/images/graph-visualization.png +0 -0
  140. package/docs/images/topup-page.png +0 -0
  141. package/docs/investigation-workspaces.md +151 -0
  142. package/docs/mcp-proxy.md +180 -0
  143. package/package.json +59 -0
  144. package/skills/chain-insights-developer-experience/SKILL.md +101 -0
  145. package/skills/chain-insights-investigation/SKILL.md +285 -0
  146. package/skills/chain-insights-investigation/agents/openai.yaml +4 -0
  147. package/skills/chain-insights-investigation/scripts/run-target-uat.sh +197 -0
  148. package/skills/chain-insights-trace-funds/SKILL.md +249 -0
  149. package/skills/ci-case/SKILL.md +43 -0
  150. package/skills/ci-status/SKILL.md +45 -0
  151. package/skills/test-chain-insights-graphrag-mcp/SKILL.md +75 -0
  152. package/skills/test-chain-insights-graphrag-mcp/agents/openai.yaml +4 -0
  153. package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +414 -0
@@ -0,0 +1,285 @@
1
+ ---
2
+ name: chain-insights-investigation
3
+ description: Use when operating in a Chain Insights investigation workspace or when the user asks to investigate blockchain activity, trace funds, analyze AML risk, use the cia/chain-insights CLI, work with cases, sessions, evidence, dossiers, graph_query, graph_query_batch, Bittensor, Ethereum, or Base investigation data. This skill is mandatory for Codex-led Chain Insights investigations.
4
+ ---
5
+
6
+ # Chain Insights Investigation
7
+
8
+ Use the Chain Insights framework. Do not improvise an investigation workflow in chat.
9
+
10
+ ## Debug Mode
11
+
12
+ For local development and UAT, enable Graph MCP debug mode before graph calls:
13
+
14
+ ```bash
15
+ cia debug on --token chain-insights-dev-debug --endpoint http://localhost:8012/mcp
16
+ cia debug status
17
+ ```
18
+
19
+ Debug mode disables x402 payments for Graph MCP calls and requires a configured debug token.
20
+
21
+ Turn it off before paid-path testing:
22
+
23
+ ```bash
24
+ cia debug off
25
+ ```
26
+
27
+ ## First Moves
28
+
29
+ 1. Confirm the current directory is an initialized Chain Insights workspace:
30
+ ```bash
31
+ test -f .chain-insights/workspace.json && cat .chain-insights/workspace.json
32
+ ```
33
+ If this fails, stop and tell the user to run:
34
+ ```bash
35
+ cia init .
36
+ ```
37
+ No investigation output belongs under ~/.chain-insights.
38
+ 2. Inspect live network support before choosing tools:
39
+ ```bash
40
+ cia mcp networks
41
+ ```
42
+ Use the advertised `Topology`, `Risk`, `Dataset`, and `Available tools`
43
+ columns as the source of truth for the current GraphRAG endpoint. The
44
+ `Dataset` column is the graph coverage range, usually
45
+ `<first_height>..<last_height> / <first_date>..<last_date>`. If an incident,
46
+ address activity, or requested chain falls outside that range, state that
47
+ limitation before querying. Do not call `address_risk` unless the selected
48
+ network advertises risk support and `address_risk` is available. If only
49
+ topology is available, use `track_funds`, `scam_topology`, or
50
+ `graph_query_batch` with `USE live_topology` as appropriate. Use
51
+ `graph_query_batch` with `USE archive_topology`
52
+ for historical money-flow topology, and `USE facts`
53
+ for graph-language StarRocks facts exposed through `facts_*_view`.
54
+ 3. Read workspace runtime schema notes:
55
+ ```bash
56
+ test -f .chain-insights/runtime-skill/SKILL.md && sed -n '1,220p' .chain-insights/runtime-skill/SKILL.md
57
+ ```
58
+ 4. If `.chain-insights/schema/<network>.graph-schema.json` does not exist, capture schema before case queries:
59
+ ```bash
60
+ mkdir -p .chain-insights/schema
61
+ cia mcp call graph_query_batch network=<network> 'queries=[{"id":"node_labels","query":"USE live_topology MATCH (n:Address) RETURN \"Address\" AS node_label, count(n) AS sample_count LIMIT 1"},{"id":"relationship_types","query":"USE live_topology MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN \"FLOWS_TO\" AS rel_name, count(r) AS sample_count LIMIT 1"},{"id":"address_sample","query":"USE live_topology MATCH (n:Address) RETURN n.address AS address, n.labels AS labels LIMIT 20"},{"id":"flow_sample","query":"USE live_topology MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN r.amount_sum AS amount_sum, r.amount_usd_sum AS amount_usd_sum LIMIT 20"},{"id":"archive_flow_sample","query":"USE archive_topology MATCH (:Address)-[f:FLOWS_TO]->(:Address) RETURN f.period_granularity AS granularity, f.amount_sum AS amount_sum LIMIT 20"}]' > .chain-insights/schema/<network>.graph-schema.raw.json
62
+ ```
63
+ Reduce the raw schema into `.chain-insights/schema/<network>.graph-schema.json` and update `.chain-insights/runtime-skill/SKILL.md` with the observed fields.
64
+ 5. List cases:
65
+ ```bash
66
+ cia case list
67
+ ```
68
+ 6. If a case exists and the user did not choose one, ask which numbered case to use.
69
+ 7. If no case exists but the user gave an address/topic, create one with a descriptive name:
70
+ ```bash
71
+ cia case open "Tracking stolen funds from <full-address>" --tags <network-or-domain>
72
+ ```
73
+ 8. Show selected case before investigating:
74
+ ```bash
75
+ cia case show <case-number>
76
+ ```
77
+ 9. Start or reuse the active session:
78
+ ```bash
79
+ cia case session start <case-number> "short session title"
80
+ ```
81
+
82
+ `cia case show` displays context. It does not start work.
83
+
84
+ ## Hard Rules
85
+
86
+ - Always preserve full blockchain addresses exactly.
87
+ - Python GraphRAG MCP is the golden behavior for `address_risk` and
88
+ `track_funds`. Chain Insights MCP may expose its own high-level tools, but
89
+ their graph semantics must be a faithful port of the Python tools.
90
+ - When the upstream server is Go Graph MCP, high-level Chain Insights tools
91
+ must implement Python-compatible orchestration by calling `graph_query` or
92
+ `graph_query_batch`. Prefer `USE live_topology` for Memgraph RAM topology,
93
+ `USE archive_topology` for StarRocks historical topology, and `USE facts`
94
+ for StarRocks facts. Do not replace Python probe semantics with
95
+ simplified local recipes.
96
+ - For exchange-deposit discovery, preserve Python `BFSOps`/`StolenFundsProbe`
97
+ semantics: forward search to `Address` nodes where `is_exchange IS NOT NULL`,
98
+ stop at exchange nodes, treat `path[-2]` as the deposit candidate, then run
99
+ backward/source and reverse-lead stages. Current MemGQL does not parse
100
+ Memgraph `*BFS` or variable-length relationship syntax, so Go Graph MCP
101
+ deployments reproduce this with generated fixed-depth `FLOWS_TO` query batches.
102
+ - For `address_risk`, Python `GraphRAGQueryEngine.check_address_risk` is the
103
+ golden behavior: bounded neighborhood expansion with exchange-stopped waves,
104
+ risk/scoring fields, lookalikes, forward exchange
105
+ discovery, and backward `BFSOps.bfs_backward` source-exchange discovery.
106
+ Chain Insights may call Go MCP primitives, but it must not reduce
107
+ `address_risk` to only a profile lookup.
108
+ - Never call graph tools without an explicit `network`.
109
+ - Never assume network support. Run `cia mcp networks` first and respect the
110
+ advertised `Available tools` plus the `Dataset` height/date range.
111
+ - Never treat user claims as facts until tool output supports them.
112
+ - Never leave material tool output only in chat. Save it as evidence.
113
+ - Keep evidence compact and use original graph field names. Evidence Markdown should summarize and point to files; large JSON belongs in `reports/tables/`.
114
+ - Store visualization data in `reports/graphs/` and analyst tables in `reports/tables/` under the initialized workspace.
115
+ - Prefer Mermaid/table reports over long prose dossiers.
116
+ - Prefer `cia` commands over direct file edits.
117
+ - Do not use `cia case resume`; use `cia case show`.
118
+ - Use numbered case selectors from `cia case list`.
119
+ - Use `graph_query_batch` for related graph reads.
120
+ - Use read-only Cypher only: no `CREATE`, `MERGE`, `SET`, `DELETE`, `REMOVE`, `DROP`, or `DETACH`.
121
+ - Bound graph reads with `LIMIT`.
122
+
123
+ ## Tool Selection
124
+
125
+ Tool selection is network-dependent:
126
+
127
+ - `Topology: yes` means graph topology tools can run for that network.
128
+ - `Risk: yes` means risk-aware tools such as `address_risk` can be used.
129
+ - `Dataset` defines the graph coverage window. Use it to qualify conclusions;
130
+ do not imply coverage before the first advertised height/date or after the
131
+ last advertised height/date.
132
+ - `Available tools` is authoritative. If a tool is not listed as available,
133
+ fall back to the available lower-level graph tools or tell the user the
134
+ network is not supported for that workflow yet.
135
+
136
+ Use `address_risk` first for ordinary address screening. It measures risk,
137
+ neighborhood context, and exchange behavior together, including exchange inflow
138
+ and outflow paths. Do not use retired separate exchange-flow tools as a primary
139
+ workflow; that behavior belongs in `address_risk`.
140
+
141
+ Use `address_risk` with `compare_address` when the user asks whether two
142
+ addresses are connected or whether a relationship is risky. Use compare mode
143
+ as the unified connection-risk path.
144
+
145
+ Use `track_funds` when the user has victim/source addresses and may also have
146
+ known scammer addresses. It accepts up to five `trusted_addresses` and up to
147
+ five `untrusted_addresses`, preserves those roles, and traces each through the
148
+ local tracing engine.
149
+
150
+ Use `track_funds` for stolen-fund and fund-flow work, including single-address
151
+ fund-flow tracing by passing one address as the only `trusted_addresses` value.
152
+
153
+ Use `scam_topology` when known victim incident ground truth should become
154
+ ML-ready `scam_labels` plus reviewable laundering context. The victim-only
155
+ traversal is outward from victim/source funds, so do not query or promote
156
+ victim inbound transfers as scam infrastructure. The primary traversal is a
157
+ node-relative novelty wave: each new node expands only once, repeated targets
158
+ are retained as non-expanding `convergence_edge` context, and downstream edges
159
+ must have `first_seen_timestamp` or `last_seen_timestamp` greater than or equal
160
+ to the current node's wave-arrival timestamp. The tool can instead use
161
+ `activity_policy=global_incident_only`, where every wave is filtered against
162
+ `incident_timestamp_ms`. Exchange terminal safety is the only hard-coded
163
+ terminal behavior; other labels are generic context hints. The graph report follows the
164
+ same `chain-insights.graph.v1` layout as
165
+ `track_funds`: primary victim-flow edges are `flows`, exchange deposits are
166
+ `deposits`, and deposit-cluster enrichment is `reverse_leads`.
167
+ Victim/source addresses,
168
+ exchange endpoints, and generic labeled context nodes are not automatic scam
169
+ labels; laundering intermediates and exchange deposit candidates are reviewable,
170
+ not automatic writes to `core_address_labels`.
171
+
172
+ ```bash
173
+ cia mcp scam-topology --network bittensor --victim-address 5... --incident-timestamp-ms 1715532228001 --max-hops 16
174
+ cia mcp scam-topology --network bittensor --victim-address 5... --incident-timestamp-ms 1715532228001 --max-hops 16 --activity-policy global_incident_only
175
+ cia mcp scam-topology --network bittensor --victim-address 5... --incident-timestamp-ms 1715532228001 --case 1
176
+ ```
177
+
178
+ If `case_id` or CLI `--case` is provided, `scam_topology` writes a compact
179
+ `chain-insights.evidence_pointer.v1` entry to the case. The pointer references
180
+ workspace-local compact evidence JSON, graph JSON, graph HTML,
181
+ label-candidate CSV, and Markdown report files under `reports/`.
182
+
183
+ Use manual `graph_query_batch` for custom topology or fact questions. Use
184
+ `USE live_topology` for Memgraph RAM topology, `USE archive_topology` for
185
+ StarRocks historical topology, and `USE facts` for StarRocks facts.
186
+ Use `graph_query` or `graph_query_batch` for all graph-language reads.
187
+
188
+ ## Query And Evidence Loop
189
+
190
+ Default to compact evidence. Preserve source schema field names and avoid adding
191
+ derived aliases or unit labels unless the schema or query result explicitly
192
+ supports them. Select only fields needed to support the claim, such as source,
193
+ destination, relationship type, amount fields, tx counts, tx ids, labels, and
194
+ degrees. Avoid storing whole node or relationship property blobs in evidence
195
+ unless the query is explicitly for schema discovery or debugging.
196
+
197
+ For every material graph/tool query:
198
+
199
+ 1. Write output to a temp file with narrow Cypher projections:
200
+ ```bash
201
+ cia mcp call graph_query_batch \
202
+ network=<network> \
203
+ 'queries=[{"id":"<stable_id>","query":"USE live_topology MATCH ... RETURN ... LIMIT 50"}]' \
204
+ > /tmp/<stable_id>.json
205
+ ```
206
+ 2. Inspect the output and reduce it if the tool returned extra fields:
207
+ ```bash
208
+ jq '{schema:"chain-insights.compact_evidence.v1", facts:<selected-fields>}' \
209
+ /tmp/<stable_id>.json > /tmp/<stable_id>.compact.json
210
+ ```
211
+ 3. Save the compact output as evidence. If the JSON is large, Chain Insights stores it under `reports/tables/` and keeps the evidence Markdown as a summary plus pointer:
212
+ ```bash
213
+ cia case evidence add <case-number> \
214
+ --source graph_query_batch_compact \
215
+ --query-params "network=<network> id=<stable_id> ..." \
216
+ --content "$(cat /tmp/<stable_id>.compact.json)"
217
+ ```
218
+ 4. Only after evidence is saved, summarize what the output supports and what remains unknown.
219
+ 5. Write graph/table/report outputs when the query describes fund movement:
220
+ ```bash
221
+ mkdir -p reports/graphs reports/tables
222
+ ```
223
+ Save `chain-insights.graph.v1` JSON to `reports/graphs/` for visualization, tabular extracts to `reports/tables/`, and Mermaid/table analyst reports to `reports/`.
224
+ 6. If an address/entity finding becomes durable, update the dossier with one short pointer, not a full report:
225
+ ```bash
226
+ cia case dossier update <case-number> <full-address> \
227
+ --type unknown \
228
+ --finding "See reports/<report>.md and evidence <filename>."
229
+ ```
230
+
231
+ ## Quoting Pattern
232
+
233
+ Keep MCP JSON arguments on one shell line. Raw newlines inside JSON strings break parsing.
234
+
235
+ Good:
236
+ ```bash
237
+ cia mcp call graph_query_batch network=bittensor 'queries=[{"id":"address_exists","query":"USE live_topology MATCH (n:Address {address: \"5...\"}) RETURN n.address AS address, n.labels AS labels LIMIT 1"},{"id":"address_feature","query":"USE facts MATCH (:Address {address: \"5...\"})-[:HAS_FEATURE]->(feature:AddressFeature) RETURN feature.degree_in AS degree_in, feature.degree_out AS degree_out, feature.tx_total_count AS tx_total_count LIMIT 1"}]'
238
+ ```
239
+
240
+ Bad:
241
+ ```bash
242
+ 'queries=[{"id":"address_exists","query":"MATCH (n {address:
243
+ \"5...\"}) RETURN ..."}]'
244
+ ```
245
+
246
+ ## Session Closeout
247
+
248
+ At the end of a work pass:
249
+
250
+ ```bash
251
+ cia case session end <case-number> \
252
+ --findings "Evidence-backed findings from this session." \
253
+ --next-steps "Concrete next graph queries or checks."
254
+ ```
255
+
256
+ Then show the case:
257
+
258
+ ```bash
259
+ cia case show <case-number>
260
+ ```
261
+
262
+ ## When Asked "What Next?"
263
+
264
+ Give one concrete next command, not a broad menu. Prefer the next topology or fact query, or the next evidence-save step.
265
+
266
+ ## Fresh Workspace UAT
267
+
268
+ When validating the skill or Chain Insights investigation flow, run the bundled script from the Chain Insights repo:
269
+
270
+ ```bash
271
+ skills/chain-insights-investigation/scripts/run-target-uat.sh
272
+ ```
273
+
274
+ The script creates a fresh investigation workspace, enables debug mode, captures runtime graph schema, opens a case for `5GTjfJaLpBNrgybhY24NqhDnKW9r94z72RSYLxeodxJfSkj5`, starts a session, runs `graph_query_batch`, saves compact evidence with original field names, updates a lightweight dossier pointer, ends the session, and verifies the resulting case state.
275
+
276
+ Environment overrides:
277
+
278
+ ```bash
279
+ TARGET_ADDRESS=5GTjfJaLpBNrgybhY24NqhDnKW9r94z72RSYLxeodxJfSkj5 \
280
+ NETWORK=bittensor \
281
+ GRAPH_MCP_ENDPOINT=http://localhost:8012/mcp \
282
+ GRAPH_MCP_DEBUG_TOKEN=chain-insights-dev-debug \
283
+ WORKSPACE_ROOT=/tmp/ci-investigation-uat \
284
+ skills/chain-insights-investigation/scripts/run-target-uat.sh
285
+ ```
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Chain Insights Investigation"
3
+ short_description: "Run evidence-backed blockchain investigations with cia"
4
+ default_prompt: "Use the Chain Insights investigation workflow for this case: select or create a case, start a session, run bounded graph queries, save raw outputs as evidence, update dossiers for durable findings, and close with next steps."
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env bash
2
+ set -Eeuo pipefail
3
+
4
+ TARGET_ADDRESS="${TARGET_ADDRESS:-5GTjfJaLpBNrgybhY24NqhDnKW9r94z72RSYLxeodxJfSkj5}"
5
+ NETWORK="${NETWORK:-bittensor}"
6
+ GRAPH_MCP_ENDPOINT="${GRAPH_MCP_ENDPOINT:-http://localhost:8012/mcp}"
7
+ GRAPH_MCP_DEBUG_TOKEN="${GRAPH_MCP_DEBUG_TOKEN:-chain-insights-dev-debug}"
8
+ WORKSPACE_ROOT="${WORKSPACE_ROOT:-$(mktemp -d /tmp/chain-insights-investigation-uat.XXXXXX)}"
9
+ GLOBAL_REPORTS="${HOME}/.chain-insights/reports"
10
+ GLOBAL_CASES="${HOME}/.chain-insights/cases"
11
+ GLOBAL_SNAPSHOT_BEFORE="$(mktemp /tmp/chain-insights-global-before.XXXXXX)"
12
+ GLOBAL_SNAPSHOT_AFTER="$(mktemp /tmp/chain-insights-global-after.XXXXXX)"
13
+ CONFIG_SNAPSHOT_READY=0
14
+
15
+ log() {
16
+ printf '[chain-insights-investigation-uat] %s\n' "$*"
17
+ }
18
+
19
+ require_cmd() {
20
+ if ! command -v "$1" >/dev/null 2>&1; then
21
+ printf '[chain-insights-investigation-uat] missing command: %s\n' "$1" >&2
22
+ exit 127
23
+ fi
24
+ }
25
+
26
+ snapshot_global_outputs() {
27
+ local output_file="$1"
28
+ : >"${output_file}"
29
+ for dir in "${GLOBAL_REPORTS}" "${GLOBAL_CASES}"; do
30
+ {
31
+ printf '[%s]\n' "${dir}"
32
+ if [[ -d "${dir}" ]]; then
33
+ (
34
+ cd "${dir}"
35
+ find . -mindepth 1 -type d -print | LC_ALL=C sort | sed 's/^/dir /'
36
+ find . -mindepth 1 -type f -print0 \
37
+ | LC_ALL=C sort -z \
38
+ | xargs -0 -r sha256sum \
39
+ | sed 's/^/file /'
40
+ )
41
+ else
42
+ printf '<missing>\n'
43
+ fi
44
+ } >>"${output_file}"
45
+ done
46
+ }
47
+
48
+ assert_no_global_outputs_changed() {
49
+ snapshot_global_outputs "${GLOBAL_SNAPSHOT_AFTER}"
50
+ if ! cmp -s "${GLOBAL_SNAPSHOT_BEFORE}" "${GLOBAL_SNAPSHOT_AFTER}"; then
51
+ log "global investigation output roots changed; reports/cases must stay workspace-local"
52
+ diff -u "${GLOBAL_SNAPSHOT_BEFORE}" "${GLOBAL_SNAPSHOT_AFTER}" >&2 || true
53
+ return 1
54
+ fi
55
+ }
56
+
57
+ restore_mode() {
58
+ if [[ "${CONFIG_SNAPSHOT_READY}" != "1" ]]; then
59
+ return
60
+ fi
61
+ if [[ -n "${OLD_GRAPH_MCP_MODE:-}" ]]; then
62
+ cia config set graphMcpMode "${OLD_GRAPH_MCP_MODE}" >/dev/null || true
63
+ fi
64
+ if [[ -n "${OLD_GRAPH_MCP_ENDPOINT:-}" ]]; then
65
+ cia config set graphMcpEndpoint "${OLD_GRAPH_MCP_ENDPOINT}" >/dev/null || true
66
+ fi
67
+ cia config set graphMcpAuthToken "${OLD_GRAPH_MCP_AUTH_TOKEN:-}" >/dev/null || true
68
+ }
69
+
70
+ finish() {
71
+ local status="$?"
72
+ set +e
73
+ if [[ -f "${GLOBAL_SNAPSHOT_BEFORE}" ]]; then
74
+ assert_no_global_outputs_changed || status=1
75
+ fi
76
+ restore_mode
77
+ rm -f "${GLOBAL_SNAPSHOT_BEFORE}" "${GLOBAL_SNAPSHOT_AFTER}"
78
+ exit "${status}"
79
+ }
80
+ trap finish EXIT
81
+
82
+ require_cmd cia
83
+ require_cmd node
84
+ require_cmd jq
85
+ require_cmd sha256sum
86
+
87
+ snapshot_global_outputs "${GLOBAL_SNAPSHOT_BEFORE}"
88
+
89
+ OLD_GRAPH_MCP_MODE="$(cia config get graphMcpMode || true)"
90
+ OLD_GRAPH_MCP_ENDPOINT="$(cia config get graphMcpEndpoint || true)"
91
+ OLD_GRAPH_MCP_AUTH_TOKEN="$(cia config get graphMcpAuthToken || true)"
92
+ CONFIG_SNAPSHOT_READY=1
93
+
94
+ log "workspace: ${WORKSPACE_ROOT}"
95
+ log "target: ${NETWORK}:${TARGET_ADDRESS}"
96
+ log "enabling Graph MCP debug mode for UAT"
97
+ cia debug on --token "${GRAPH_MCP_DEBUG_TOKEN}" --endpoint "${GRAPH_MCP_ENDPOINT}" >/dev/null
98
+
99
+ cia init "${WORKSPACE_ROOT}" --force >/dev/null
100
+ cd "${WORKSPACE_ROOT}"
101
+
102
+ CASE_NAME="Tracking stolen funds from ${TARGET_ADDRESS}"
103
+ cia case open "${CASE_NAME}" --tags "${NETWORK},uat" --description "Fresh-folder Chain Insights investigation UAT." >/tmp/chain-insights-uat-case-open.txt
104
+ CASE_ID="$(sed -n 's/^Case opened: //p' /tmp/chain-insights-uat-case-open.txt | head -n1)"
105
+ if [[ -z "${CASE_ID}" ]]; then
106
+ log "failed to parse case id"
107
+ cat /tmp/chain-insights-uat-case-open.txt >&2
108
+ exit 1
109
+ fi
110
+
111
+ cia case session start "${CASE_ID}" "UAT graph evidence for ${TARGET_ADDRESS}" >/dev/null
112
+ mkdir -p reports/uat reports/graphs .chain-insights/schema
113
+
114
+ SCHEMA_RAW=".chain-insights/schema/${NETWORK}.graph-schema.raw.json"
115
+ SCHEMA_FILE=".chain-insights/schema/${NETWORK}.graph-schema.json"
116
+ log "capturing ${NETWORK} graph schema"
117
+ cia mcp call graph_query_batch \
118
+ network="${NETWORK}" \
119
+ 'queries=[{"id":"node_labels","query":"USE live_topology MATCH (n:Address) RETURN \"Address\" AS node_label, count(n) AS sample_count LIMIT 1"},{"id":"relationship_types","query":"USE live_topology MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN \"FLOWS_TO\" AS rel_name, count(r) AS sample_count LIMIT 1"},{"id":"address_sample","query":"USE live_topology MATCH (n:Address) RETURN n.address AS address, n.labels AS labels LIMIT 20"},{"id":"flow_sample","query":"USE live_topology MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN r.amount_sum AS amount_sum, r.amount_usd_sum AS amount_usd_sum LIMIT 20"}]' \
120
+ > "${SCHEMA_RAW}"
121
+
122
+ jq --arg network "${NETWORK}" '{
123
+ schema:"chain-insights.runtime_graph_schema.v1",
124
+ network:$network,
125
+ source:"graph_query_batch",
126
+ node_labels:(.facts.queries[]|select(.id=="node_labels")|.results),
127
+ relationship_types:(.facts.queries[]|select(.id=="relationship_types")|.results),
128
+ address_property_keys:(.facts.queries[]|select(.id=="address_property_keys")|.results|map(.property_key)),
129
+ flows_to_property_keys:(.facts.queries[]|select(.id=="flows_to_property_keys")|.results|map(.property_key))
130
+ }' "${SCHEMA_RAW}" > "${SCHEMA_FILE}"
131
+
132
+ if ! jq -e '.flows_to_property_keys | index("amount_sum")' "${SCHEMA_FILE}" >/dev/null; then
133
+ log "schema did not include FLOWS_TO.amount_sum"
134
+ cat "${SCHEMA_FILE}" >&2
135
+ exit 1
136
+ fi
137
+
138
+ RESULT_FILE="reports/uat/address_exists.json"
139
+ COMPACT_FILE="reports/uat/address_exists.compact.json"
140
+
141
+ log "running graph_query_batch address_exists"
142
+ cia mcp call graph_query_batch \
143
+ network="${NETWORK}" \
144
+ "queries=[{\"id\":\"address_exists\",\"query\":\"USE live_topology MATCH (n:Address {address: \\\"${TARGET_ADDRESS}\\\"}) RETURN n.address AS address, n.labels AS labels, n.degree_in AS degree_in, n.degree_out AS degree_out, n.tx_total_count AS tx_total_count, n.total_volume_usd AS total_volume_usd LIMIT 1\"}]" \
145
+ > "${RESULT_FILE}"
146
+
147
+ if ! grep -q "${TARGET_ADDRESS}" "${RESULT_FILE}"; then
148
+ log "graph result did not contain target address"
149
+ cat "${RESULT_FILE}" >&2
150
+ exit 1
151
+ fi
152
+
153
+ jq --arg network "${NETWORK}" '{
154
+ schema:"chain-insights.compact_evidence.v1",
155
+ source:"graph_query_batch",
156
+ network:$network,
157
+ query_ids:["address_exists"],
158
+ addresses:(.facts.queries[]|select(.id=="address_exists")|.results)
159
+ }' "${RESULT_FILE}" > "${COMPACT_FILE}"
160
+
161
+ EVIDENCE_OUT="$(cia case evidence add "${CASE_ID}" \
162
+ --source graph_query_batch_compact \
163
+ --query-params "network=${NETWORK} address=${TARGET_ADDRESS} query=address_exists compact=true schema=${SCHEMA_FILE}" \
164
+ --content "$(cat "${COMPACT_FILE}")")"
165
+ printf '%s\n' "${EVIDENCE_OUT}" > reports/uat/evidence-add.txt
166
+
167
+ SHOW_OUT="$(cia case show "${CASE_ID}")"
168
+ printf '%s\n' "${SHOW_OUT}" > reports/uat/case-show.txt
169
+ if ! printf '%s\n' "${SHOW_OUT}" | grep -q 'Evidence files: 1'; then
170
+ log "case show did not report one evidence file"
171
+ printf '%s\n' "${SHOW_OUT}" >&2
172
+ exit 1
173
+ fi
174
+
175
+ cia case dossier update "${CASE_ID}" "${TARGET_ADDRESS}" \
176
+ --type unknown \
177
+ --finding "UAT confirmed the target address exists in ${NETWORK}; see compact address_exists evidence and ${SCHEMA_FILE}." >/dev/null
178
+
179
+ cia case session end "${CASE_ID}" \
180
+ --findings "UAT confirmed schema capture and compact graph_query_batch evidence for the target address." \
181
+ --next-steps "Run narrow FLOWS_TO projections and save graph JSON under reports/graphs/." >/dev/null
182
+
183
+ FINAL_SHOW="$(cia case show "${CASE_ID}")"
184
+ printf '%s\n' "${FINAL_SHOW}" > reports/uat/final-case-show.txt
185
+ if ! printf '%s\n' "${FINAL_SHOW}" | grep -q 'Dossiers: 1'; then
186
+ log "case show did not report one dossier"
187
+ printf '%s\n' "${FINAL_SHOW}" >&2
188
+ exit 1
189
+ fi
190
+
191
+ log "PASS"
192
+ log "workspace: ${WORKSPACE_ROOT}"
193
+ log "case: ${CASE_ID}"
194
+ log "result: ${WORKSPACE_ROOT}/${RESULT_FILE}"
195
+ log "schema: ${WORKSPACE_ROOT}/${SCHEMA_FILE}"
196
+ log "evidence:"
197
+ find "cases/${CASE_ID}/evidence" -maxdepth 1 -type f -print | sort