1use gobby_core::cli_contract::{
2 CliContract, CommandContract, FlagContract, PositionalContract, ScopeContract,
3};
4
5pub fn contract() -> CliContract {
6 CliContract {
7 tool: "gcode",
8 contract_version: 1,
9 summary: "Fast code index CLI for Gobby.",
10 global_flags: vec![
11 FlagContract::value("--project", "ROOT"),
12 format_flag(),
13 FlagContract::switch("--quiet"),
14 FlagContract::switch("--verbose"),
15 FlagContract::switch("--no-freshness"),
16 ],
17 scope: Some(ScopeContract {
18 flags: vec![FlagContract::value("--project", "ROOT")],
19 default: "detect project from current working directory",
20 identity_keys: vec!["project_id", "project_root"],
21 }),
22 commands: vec![
23 CommandContract {
24 name: "contract",
25 summary: "Emit this CLI contract.",
26 daemon_consumed: true,
27 positionals: vec![],
28 flags: vec![format_flag()],
29 json_output_keys: contract_keys(),
30 },
31 CommandContract {
32 name: "index",
33 summary: "Index a directory or specific files into the code index.",
34 daemon_consumed: true,
35 positionals: vec![PositionalContract {
36 name: "PATH",
37 required: false,
38 repeatable: false,
39 }],
40 flags: vec![
41 FlagContract::repeatable_value("--files", "FILE"),
42 FlagContract::switch("--full"),
43 FlagContract::switch("--require-cpp-semantics"),
44 FlagContract::switch("--sync-projections"),
45 ],
46 json_output_keys: vec![
47 "project_id",
48 "root",
49 "indexed_files",
50 "indexed_symbols",
51 "skipped_files",
52 "errors",
53 ],
54 },
55 CommandContract {
56 name: "search",
57 summary: "Hybrid symbol and content search over the code index.",
58 daemon_consumed: true,
59 positionals: vec![
60 PositionalContract::required("QUERY"),
61 PositionalContract {
62 name: "PATH",
63 required: false,
64 repeatable: true,
65 },
66 ],
67 flags: search_flags(),
68 json_output_keys: search_keys(),
69 },
70 CommandContract {
71 name: "search-symbol",
72 summary: "Exact-first symbol/name search with deterministic ranking.",
73 daemon_consumed: true,
74 positionals: vec![
75 PositionalContract::required("QUERY"),
76 PositionalContract {
77 name: "PATH",
78 required: false,
79 repeatable: true,
80 },
81 ],
82 flags: {
83 let mut flags = search_flags();
84 flags.push(FlagContract::switch("--with-graph"));
85 flags
86 },
87 json_output_keys: search_keys(),
88 },
89 CommandContract {
90 name: "grep",
91 summary: "Indexed exact pattern search over code content chunks.",
92 daemon_consumed: true,
93 positionals: vec![
94 PositionalContract::required("PATTERN"),
95 PositionalContract {
96 name: "PATH",
97 required: false,
98 repeatable: true,
99 },
100 ],
101 flags: grep_flags(),
102 json_output_keys: grep_keys(),
103 },
104 CommandContract {
105 name: "callers",
106 summary: "Find callers of a symbol UUID or name.",
107 daemon_consumed: true,
108 positionals: vec![PositionalContract::required("SYMBOL")],
109 flags: graph_read_flags(),
110 json_output_keys: graph_read_keys(),
111 },
112 CommandContract {
113 name: "usages",
114 summary: "Find incoming call usages of a symbol UUID or name.",
115 daemon_consumed: true,
116 positionals: vec![PositionalContract::required("SYMBOL")],
117 flags: graph_read_flags(),
118 json_output_keys: graph_read_keys(),
119 },
120 CommandContract {
121 name: "codewiki",
122 summary: "Generate vault-ready hierarchical code documentation.",
123 daemon_consumed: true,
124 positionals: vec![],
125 flags: vec![
126 FlagContract::value("--out", "DIR"),
127 FlagContract::repeatable_value("--scope", "PATH"),
128 ai_flag(),
129 ],
130 json_output_keys: vec![
131 "command",
132 "project_id",
133 "project_root",
134 "out_dir",
135 "generated_pages",
136 "changed_paths",
137 "skipped",
138 "files",
139 "modules",
140 "symbols",
141 "ai_enabled",
142 ],
143 },
144 CommandContract {
145 name: "graph sync-file",
146 summary: "Sync one indexed file into the code-index graph projection.",
147 daemon_consumed: true,
148 positionals: vec![],
149 flags: vec![
150 FlagContract::value("--file", "FILE"),
151 FlagContract::switch("--allow-missing-indexed-file"),
152 format_flag(),
153 ],
154 json_output_keys: vec![
155 "status",
156 "project_id",
157 "file",
158 "relationships_written",
159 "skipped",
160 "summary",
161 ],
162 },
163 CommandContract {
164 name: "graph overview",
165 summary: "Show an overview graph for the current project.",
166 daemon_consumed: true,
167 positionals: vec![],
168 flags: vec![FlagContract::value("--limit", "N"), format_flag()],
169 json_output_keys: graph_payload_keys(),
170 },
171 CommandContract {
172 name: "graph file",
173 summary: "Show graph nodes and links for one indexed file.",
174 daemon_consumed: true,
175 positionals: vec![],
176 flags: vec![FlagContract::value("--file", "FILE"), format_flag()],
177 json_output_keys: graph_payload_keys(),
178 },
179 CommandContract {
180 name: "graph neighbors",
181 summary: "Show graph neighbors for one symbol ID.",
182 daemon_consumed: true,
183 positionals: vec![],
184 flags: vec![
185 FlagContract::value("--symbol-id", "SYMBOL_ID"),
186 FlagContract::value("--limit", "N"),
187 format_flag(),
188 ],
189 json_output_keys: graph_payload_keys(),
190 },
191 CommandContract {
192 name: "graph blast-radius",
193 summary: "Show transitive graph impact for a symbol ID or file path.",
194 daemon_consumed: true,
195 positionals: vec![],
196 flags: vec![
197 FlagContract::value("--symbol-id", "SYMBOL_ID"),
198 FlagContract::value("--file", "FILE"),
199 FlagContract::value("--depth", "N"),
200 FlagContract::value("--limit", "N"),
201 format_flag(),
202 ],
203 json_output_keys: graph_payload_keys(),
204 },
205 CommandContract {
206 name: "graph clear",
207 summary: "Clear the current project's code-index graph projection.",
208 daemon_consumed: true,
209 positionals: vec![],
210 flags: vec![
211 FlagContract::value("--project-id", "PROJECT_ID"),
212 format_flag(),
213 ],
214 json_output_keys: graph_lifecycle_keys(),
215 },
216 CommandContract {
217 name: "graph rebuild",
218 summary: "Rebuild the current project's code-index graph projection from PostgreSQL facts.",
219 daemon_consumed: true,
220 positionals: vec![],
221 flags: vec![format_flag()],
222 json_output_keys: graph_lifecycle_keys(),
223 },
224 ],
225 error_codes: vec![
226 "invalid_input",
227 "missing_project",
228 "backend_unavailable",
229 "index_unavailable",
230 "contract_violation",
231 ],
232 }
233}
234
235fn format_flag() -> FlagContract {
236 FlagContract::value("--format", "json|text").allowed(vec!["json", "text"])
237}
238
239fn ai_flag() -> FlagContract {
240 FlagContract::value("--ai", "auto|daemon|direct|off")
241 .allowed(vec!["auto", "daemon", "direct", "off"])
242}
243
244fn search_flags() -> Vec<FlagContract> {
245 vec![
246 FlagContract::value("--limit", "N"),
247 FlagContract::value("--offset", "N"),
248 FlagContract::value("--kind", "KIND"),
249 FlagContract::value("--language", "LANG"),
250 ]
251}
252
253fn grep_flags() -> Vec<FlagContract> {
254 vec![
255 FlagContract::switch("--fixed-strings"),
256 FlagContract::switch("--ignore-case"),
257 FlagContract::switch("--word"),
258 FlagContract::value("--before-context", "N"),
259 FlagContract::value("--after-context", "N"),
260 FlagContract::value("--context", "N"),
261 FlagContract::repeatable_value("--glob", "GLOB"),
262 FlagContract::value("--max-count", "N"),
263 format_flag(),
264 ]
265}
266
267fn graph_read_flags() -> Vec<FlagContract> {
268 vec![
269 FlagContract::value("--limit", "N"),
270 FlagContract::value("--offset", "N"),
271 format_flag(),
272 ]
273}
274
275fn search_keys() -> Vec<&'static str> {
276 vec![
277 "project_id",
278 "total",
279 "offset",
280 "limit",
281 "results",
282 "id",
283 "name",
284 "qualified_name",
285 "kind",
286 "language",
287 "file_path",
288 "line_start",
289 "line_end",
290 "signature",
291 "score",
292 ]
293}
294
295fn grep_keys() -> Vec<&'static str> {
296 vec![
297 "project_id",
298 "pattern",
299 "fixed_strings",
300 "ignore_case",
301 "word",
302 "paths",
303 "globs",
304 "max_count",
305 "matched_lines",
306 "truncated",
307 "scanned_chunks",
308 "matches",
309 "path",
310 "line",
311 "text",
312 "spans",
313 "start",
314 "end",
315 "before",
316 "after",
317 ]
318}
319
320fn graph_read_keys() -> Vec<&'static str> {
321 vec![
322 "project_id",
323 "total",
324 "offset",
325 "limit",
326 "results",
327 "id",
328 "name",
329 "file_path",
330 "line",
331 "relation",
332 "distance",
333 "metadata",
334 "hint",
335 ]
336}
337
338fn contract_keys() -> Vec<&'static str> {
339 vec![
340 "tool",
341 "contract_version",
342 "summary",
343 "global_flags",
344 "scope",
345 "commands",
346 "error_codes",
347 ]
348}
349
350fn graph_payload_keys() -> Vec<&'static str> {
351 vec!["nodes", "links", "summary"]
352}
353
354fn graph_lifecycle_keys() -> Vec<&'static str> {
355 vec![
356 "status",
357 "action",
358 "project_id",
359 "synced_files",
360 "synced_symbols",
361 "synced_relationships",
362 "deleted_nodes",
363 "deleted_relationships",
364 "summary",
365 ]
366}