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}