Skip to main content

comindly_api/
browse.rs

1//! Browsing the index: overview, org map, symbols, search, packages, flows and source lines.
2
3use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6
7pub use crate::doctor::Check;
8pub use crate::graph::{ContractRole, ContractView, FlowStep, Node, Zoom};
9pub use crate::view::View;
10
11use super::Entry;
12
13/// `GET /api/v1/overview`: the index at a glance.
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct Overview {
16    pub repos: Vec<RepoSymbols>,
17    pub symbols: usize,
18    pub edges: usize,
19    pub cross_repo_edges: usize,
20    pub contracts: usize,
21    pub linked_contracts: usize,
22    /// Ledger entries per status.
23    pub ledger: BTreeMap<String, usize>,
24    /// Records waiting in Needs review.
25    #[serde(default)]
26    pub review: usize,
27    pub skew: Vec<Skew>,
28    /// Whether this caller may write to the ledger and conventions.
29    pub writable: bool,
30    pub root: Option<String>,
31    pub problem: Option<String>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct RepoSymbols {
36    pub repo: String,
37    pub symbols: usize,
38}
39
40/// A consumer locked to another version of a package than its publisher declares.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct Skew {
43    pub package: String,
44    pub publisher: String,
45    pub published: Option<String>,
46    pub consumer: String,
47    pub locked: Option<String>,
48}
49
50/// `GET /api/v1/orgmap?at=`: `at` indexes `/api/v1/versions` (oldest first); absent, the latest.
51#[derive(Debug, Clone, Default, Serialize, Deserialize)]
52pub struct AtQuery {
53    pub at: Option<usize>,
54}
55
56/// Repositories as nodes, linked by cross-repository code and contracts.
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct OrgMap {
59    pub nodes: Vec<OrgNode>,
60    pub edges: Vec<OrgEdge>,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct OrgNode {
65    pub id: String,
66    pub name: String,
67    pub repo: String,
68    pub location: String,
69}
70
71/// `src` uses `dst`; `via` names the contracts a contract link goes through.
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct OrgEdge {
74    pub src: String,
75    pub dst: String,
76    /// `code`, `contract` (routes, queues, topics) or `data` (tables and buckets both sides use).
77    pub kind: String,
78    pub weight: usize,
79    pub via: Vec<String>,
80}
81
82/// `GET /api/v1/find?q=`: symbols by name.
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct FindQuery {
85    pub q: String,
86}
87
88/// `GET /api/v1/node?focus=` and `/api/v1/context?focus=&budget=`.
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct FocusQuery {
91    pub focus: String,
92    #[serde(default, skip_serializing_if = "Option::is_none")]
93    pub budget: Option<usize>,
94}
95
96/// One symbol: its neighbours, what a change ripples to, and what the team recorded about it.
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct NodeDetail {
99    pub zoom: Zoom,
100    pub graph: Neighbourhood,
101    pub ripple: Ripple,
102    pub ledger: Vec<Entry>,
103    pub summary: Option<String>,
104    pub flow: Option<Narration>,
105}
106
107/// Nodes and labelled edges around the focus, for the canvas graph.
108#[derive(Debug, Clone, Default, Serialize, Deserialize)]
109pub struct Neighbourhood {
110    pub nodes: Vec<Node>,
111    pub edges: Vec<GraphEdge>,
112    #[serde(default, skip_serializing_if = "Option::is_none")]
113    pub focus: Option<String>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct GraphEdge {
118    pub src: String,
119    pub dst: String,
120    pub kind: String,
121}
122
123/// How many symbols a change reaches, per repository.
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct Ripple {
126    pub total: usize,
127    pub repos: BTreeMap<String, usize>,
128}
129
130/// A flow's walkthrough and the questions it answers.
131#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct Narration {
133    pub narration: String,
134    pub questions: Vec<String>,
135}
136
137/// `GET /api/v1/search?q=&repo=`.
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct SearchQuery {
140    pub q: String,
141    #[serde(default, skip_serializing_if = "Option::is_none")]
142    pub repo: Option<String>,
143}
144
145/// A hybrid search hit.
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct SearchHit {
148    pub id: String,
149    pub name: String,
150    pub kind: String,
151    pub repo: String,
152    pub location: String,
153    pub signature: Option<String>,
154    pub summary: Option<String>,
155    pub dependents: usize,
156}
157
158/// `GET /api/v1/packages`: internal packages and who uses which version.
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct Package {
161    pub ecosystem: String,
162    pub name: String,
163    pub publisher: String,
164    pub published: Option<String>,
165    pub consumer: String,
166    pub requirement: Option<String>,
167    pub locked: Option<String>,
168    pub skewed: bool,
169}
170
171/// `GET /api/v1/agent?tool=&args=`: what a read-only MCP tool answers an agent.
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct AgentQuery {
174    pub tool: String,
175    /// The tool's arguments as JSON.
176    #[serde(default)]
177    pub args: Option<String>,
178}
179
180/// `GET /api/v1/setup`: the index, its checks and how to connect agents.
181#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct Setup {
183    pub index: String,
184    pub remote: bool,
185    pub version: String,
186    pub checks: Vec<Check>,
187    pub tools: Vec<Tool>,
188    pub agents: Vec<String>,
189    /// When to use comindly's tools: a section for AGENTS.md, and the same as a Claude Code skill.
190    pub agents_md: String,
191    pub skill: String,
192}
193
194/// An MCP tool.
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct Tool {
197    pub name: String,
198    pub description: String,
199}
200
201/// `GET /api/v1/flows`: an entry point and how far its flow reaches.
202#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct FlowRow {
204    pub entry: Node,
205    pub steps: usize,
206    /// In the order the flow first reaches them.
207    pub repos: Vec<String>,
208    pub contracts: Vec<String>,
209    pub summary: Option<String>,
210    pub narrated: bool,
211}
212
213/// `GET /api/v1/flow?id=`.
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct FlowQuery {
216    pub id: String,
217}
218
219/// One flow: its walkthrough when the index has one, and every step.
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct FlowDetail {
222    pub entry: Node,
223    pub narration: Option<String>,
224    pub questions: Option<Vec<String>>,
225    pub steps: Vec<FlowStep>,
226}
227
228fn around() -> usize {
229    6
230}
231
232/// `GET /api/v1/source?repo=&at=path:line&around=`.
233#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct SourceQuery {
235    pub repo: String,
236    /// `path:line`, relative to the repository.
237    pub at: String,
238    #[serde(default = "around")]
239    pub around: usize,
240}
241
242/// The lines around `path:line`.
243#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct SourceLines {
245    pub path: String,
246    pub line: usize,
247    pub lines: Vec<SourceLine>,
248    /// The indexed commit the lines come from.
249    pub commit: Option<String>,
250    /// The lines on the repository's host, when it is GitHub or GitLab.
251    pub link: Option<String>,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct SourceLine {
256    pub n: usize,
257    pub text: String,
258}