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