Skip to main content

gobby_code/
contract.rs

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}