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: "codewiki",
91                summary: "Generate vault-ready hierarchical code documentation.",
92                daemon_consumed: true,
93                positionals: vec![],
94                flags: vec![
95                    FlagContract::value("--out", "DIR"),
96                    FlagContract::repeatable_value("--scope", "PATH"),
97                    ai_flag(),
98                ],
99                json_output_keys: vec![
100                    "command",
101                    "project_id",
102                    "project_root",
103                    "out_dir",
104                    "generated_pages",
105                    "changed_paths",
106                    "skipped",
107                    "files",
108                    "modules",
109                    "symbols",
110                    "ai_enabled",
111                ],
112            },
113            CommandContract {
114                name: "graph sync-file",
115                summary: "Sync one indexed file into the code-index graph projection.",
116                daemon_consumed: true,
117                positionals: vec![],
118                flags: vec![
119                    FlagContract::value("--file", "FILE"),
120                    FlagContract::switch("--allow-missing-indexed-file"),
121                    format_flag(),
122                ],
123                json_output_keys: vec![
124                    "status",
125                    "project_id",
126                    "file",
127                    "relationships_written",
128                    "skipped",
129                    "summary",
130                ],
131            },
132            CommandContract {
133                name: "graph overview",
134                summary: "Show an overview graph for the current project.",
135                daemon_consumed: true,
136                positionals: vec![],
137                flags: vec![FlagContract::value("--limit", "N"), format_flag()],
138                json_output_keys: graph_payload_keys(),
139            },
140            CommandContract {
141                name: "graph file",
142                summary: "Show graph nodes and links for one indexed file.",
143                daemon_consumed: true,
144                positionals: vec![],
145                flags: vec![FlagContract::value("--file", "FILE"), format_flag()],
146                json_output_keys: graph_payload_keys(),
147            },
148            CommandContract {
149                name: "graph neighbors",
150                summary: "Show graph neighbors for one symbol ID.",
151                daemon_consumed: true,
152                positionals: vec![],
153                flags: vec![
154                    FlagContract::value("--symbol-id", "SYMBOL_ID"),
155                    FlagContract::value("--limit", "N"),
156                    format_flag(),
157                ],
158                json_output_keys: graph_payload_keys(),
159            },
160            CommandContract {
161                name: "graph blast-radius",
162                summary: "Show transitive graph impact for a symbol ID or file path.",
163                daemon_consumed: true,
164                positionals: vec![],
165                flags: vec![
166                    FlagContract::value("--symbol-id", "SYMBOL_ID"),
167                    FlagContract::value("--file", "FILE"),
168                    FlagContract::value("--depth", "N"),
169                    FlagContract::value("--limit", "N"),
170                    format_flag(),
171                ],
172                json_output_keys: graph_payload_keys(),
173            },
174            CommandContract {
175                name: "graph clear",
176                summary: "Clear the current project's code-index graph projection.",
177                daemon_consumed: true,
178                positionals: vec![],
179                flags: vec![
180                    FlagContract::value("--project-id", "PROJECT_ID"),
181                    format_flag(),
182                ],
183                json_output_keys: graph_lifecycle_keys(),
184            },
185            CommandContract {
186                name: "graph rebuild",
187                summary: "Rebuild the current project's code-index graph projection from PostgreSQL facts.",
188                daemon_consumed: true,
189                positionals: vec![],
190                flags: vec![format_flag()],
191                json_output_keys: graph_lifecycle_keys(),
192            },
193        ],
194        error_codes: vec![
195            "invalid_input",
196            "missing_project",
197            "backend_unavailable",
198            "index_unavailable",
199            "contract_violation",
200        ],
201    }
202}
203
204fn format_flag() -> FlagContract {
205    FlagContract::value("--format", "json|text").allowed(vec!["json", "text"])
206}
207
208fn ai_flag() -> FlagContract {
209    FlagContract::value("--ai", "auto|daemon|direct|off")
210        .allowed(vec!["auto", "daemon", "direct", "off"])
211}
212
213fn search_flags() -> Vec<FlagContract> {
214    vec![
215        FlagContract::value("--limit", "N"),
216        FlagContract::value("--offset", "N"),
217        FlagContract::value("--kind", "KIND"),
218        FlagContract::value("--language", "LANG"),
219    ]
220}
221
222fn search_keys() -> Vec<&'static str> {
223    vec![
224        "project_id",
225        "total",
226        "offset",
227        "limit",
228        "results",
229        "id",
230        "name",
231        "qualified_name",
232        "kind",
233        "language",
234        "file_path",
235        "line_start",
236        "line_end",
237        "signature",
238        "score",
239    ]
240}
241
242fn contract_keys() -> Vec<&'static str> {
243    vec![
244        "tool",
245        "contract_version",
246        "summary",
247        "global_flags",
248        "scope",
249        "commands",
250        "error_codes",
251    ]
252}
253
254fn graph_payload_keys() -> Vec<&'static str> {
255    vec!["nodes", "links", "summary"]
256}
257
258fn graph_lifecycle_keys() -> Vec<&'static str> {
259    vec![
260        "status",
261        "action",
262        "project_id",
263        "synced_files",
264        "synced_symbols",
265        "synced_relationships",
266        "deleted_nodes",
267        "deleted_relationships",
268        "summary",
269    ]
270}