1use std::path::PathBuf;
32
33use serde::{Deserialize, Serialize};
34
35pub const SCHEMA_VERSION: u32 = 2;
37
38#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
40pub struct Totals {
41 pub code: usize,
42 pub lines: usize,
43 pub files: usize,
44 pub bytes: usize,
45 pub tokens: usize,
46 pub avg_lines: usize,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
50pub struct LangRow {
51 pub lang: String,
52 pub code: usize,
53 pub lines: usize,
54 pub files: usize,
55 pub bytes: usize,
56 pub tokens: usize,
57 pub avg_lines: usize,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct LangReport {
62 pub rows: Vec<LangRow>,
63 pub total: Totals,
64 pub with_files: bool,
65 pub children: ChildrenMode,
66 pub top: usize,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
70pub struct ModuleRow {
71 pub module: String,
72 pub code: usize,
73 pub lines: usize,
74 pub files: usize,
75 pub bytes: usize,
76 pub tokens: usize,
77 pub avg_lines: usize,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct ModuleReport {
82 pub rows: Vec<ModuleRow>,
83 pub total: Totals,
84 pub module_roots: Vec<String>,
85 pub module_depth: usize,
86 pub children: ChildIncludeMode,
87 pub top: usize,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
91#[serde(rename_all = "snake_case")]
92pub enum FileKind {
93 Parent,
94 Child,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
98pub struct FileRow {
99 pub path: String,
100 pub module: String,
101 pub lang: String,
102 pub kind: FileKind,
103 pub code: usize,
104 pub comments: usize,
105 pub blanks: usize,
106 pub lines: usize,
107 pub bytes: usize,
108 pub tokens: usize,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct ExportData {
113 pub rows: Vec<FileRow>,
114 pub module_roots: Vec<String>,
115 pub module_depth: usize,
116 pub children: ChildIncludeMode,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct RunReceipt {
121 pub schema_version: u32,
122 pub generated_at_ms: u128,
123 pub lang_file: String,
124 pub module_file: String,
125 pub export_file: String,
126 }
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
130#[serde(rename_all = "snake_case")]
131pub enum ScanStatus {
132 Complete,
133 Partial,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize, Default)]
137pub struct ToolInfo {
138 pub name: String,
139 pub version: String,
140}
141
142impl ToolInfo {
143 pub fn current() -> Self {
144 Self {
145 name: "tokmd".to_string(),
146 version: env!("CARGO_PKG_VERSION").to_string(),
147 }
148 }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct ScanArgs {
153 pub paths: Vec<String>,
154 pub excluded: Vec<String>,
155 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
157 pub excluded_redacted: bool,
158 pub config: ConfigMode,
159 pub hidden: bool,
160 pub no_ignore: bool,
161 pub no_ignore_parent: bool,
162 pub no_ignore_dot: bool,
163 pub no_ignore_vcs: bool,
164 pub treat_doc_strings_as_comments: bool,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct LangArgsMeta {
169 pub format: String,
170 pub top: usize,
171 pub with_files: bool,
172 pub children: ChildrenMode,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct LangReceipt {
177 pub schema_version: u32,
178 pub generated_at_ms: u128,
179 pub tool: ToolInfo,
180 pub mode: String, pub status: ScanStatus,
182 pub warnings: Vec<String>,
183 pub scan: ScanArgs,
184 pub args: LangArgsMeta,
185 #[serde(flatten)]
186 pub report: LangReport,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct ModuleArgsMeta {
191 pub format: String,
192 pub module_roots: Vec<String>,
193 pub module_depth: usize,
194 pub children: ChildIncludeMode,
195 pub top: usize,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct ModuleReceipt {
200 pub schema_version: u32,
201 pub generated_at_ms: u128,
202 pub tool: ToolInfo,
203 pub mode: String, pub status: ScanStatus,
205 pub warnings: Vec<String>,
206 pub scan: ScanArgs,
207 pub args: ModuleArgsMeta,
208 #[serde(flatten)]
209 pub report: ModuleReport,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct ExportArgsMeta {
214 pub format: ExportFormat,
215 pub module_roots: Vec<String>,
216 pub module_depth: usize,
217 pub children: ChildIncludeMode,
218 pub min_code: usize,
219 pub max_rows: usize,
220 pub redact: RedactMode,
221 pub strip_prefix: Option<String>,
222 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
224 pub strip_prefix_redacted: bool,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct ExportReceipt {
229 pub schema_version: u32,
230 pub generated_at_ms: u128,
231 pub tool: ToolInfo,
232 pub mode: String, pub status: ScanStatus,
234 pub warnings: Vec<String>,
235 pub scan: ScanArgs,
236 pub args: ExportArgsMeta,
237 #[serde(flatten)]
238 pub data: ExportData,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct LangArgs {
243 pub paths: Vec<PathBuf>,
244 pub format: TableFormat,
245 pub top: usize,
246 pub files: bool,
247 pub children: ChildrenMode,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct ModuleArgs {
252 pub paths: Vec<PathBuf>,
253 pub format: TableFormat,
254 pub top: usize,
255 pub module_roots: Vec<String>,
256 pub module_depth: usize,
257 pub children: ChildIncludeMode,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct ExportArgs {
262 pub paths: Vec<PathBuf>,
263 pub format: ExportFormat,
264 pub output: Option<PathBuf>,
265 pub module_roots: Vec<String>,
266 pub module_depth: usize,
267 pub children: ChildIncludeMode,
268 pub min_code: usize,
269 pub max_rows: usize,
270 pub redact: RedactMode,
271 pub meta: bool,
272 pub strip_prefix: Option<PathBuf>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct ContextReceipt {
277 pub schema_version: u32,
278 pub generated_at_ms: u128,
279 pub tool: ToolInfo,
280 pub mode: String,
281 pub budget_tokens: usize,
282 pub used_tokens: usize,
283 pub utilization_pct: f64,
284 pub strategy: String,
285 pub rank_by: String,
286 pub file_count: usize,
287 pub files: Vec<ContextFileRow>,
288 #[serde(default, skip_serializing_if = "Option::is_none")]
290 pub rank_by_effective: Option<String>,
291 #[serde(default, skip_serializing_if = "Option::is_none")]
293 pub fallback_reason: Option<String>,
294 #[serde(default, skip_serializing_if = "Vec::is_empty")]
296 pub excluded_by_policy: Vec<PolicyExcludedFile>,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct ContextFileRow {
301 pub path: String,
302 pub module: String,
303 pub lang: String,
304 pub tokens: usize,
305 pub code: usize,
306 pub lines: usize,
307 pub bytes: usize,
308 pub value: usize,
309 #[serde(default, skip_serializing_if = "String::is_empty")]
310 pub rank_reason: String,
311 #[serde(default, skip_serializing_if = "is_default_policy")]
313 pub policy: InclusionPolicy,
314 #[serde(default, skip_serializing_if = "Option::is_none")]
316 pub effective_tokens: Option<usize>,
317 #[serde(default, skip_serializing_if = "Option::is_none")]
319 pub policy_reason: Option<String>,
320 #[serde(default, skip_serializing_if = "Vec::is_empty")]
322 pub classifications: Vec<FileClassification>,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
331pub struct DiffRow {
332 pub lang: String,
333 pub old_code: usize,
334 pub new_code: usize,
335 pub delta_code: i64,
336 pub old_lines: usize,
337 pub new_lines: usize,
338 pub delta_lines: i64,
339 pub old_files: usize,
340 pub new_files: usize,
341 pub delta_files: i64,
342 pub old_bytes: usize,
343 pub new_bytes: usize,
344 pub delta_bytes: i64,
345 pub old_tokens: usize,
346 pub new_tokens: usize,
347 pub delta_tokens: i64,
348}
349
350#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
352pub struct DiffTotals {
353 pub old_code: usize,
354 pub new_code: usize,
355 pub delta_code: i64,
356 pub old_lines: usize,
357 pub new_lines: usize,
358 pub delta_lines: i64,
359 pub old_files: usize,
360 pub new_files: usize,
361 pub delta_files: i64,
362 pub old_bytes: usize,
363 pub new_bytes: usize,
364 pub delta_bytes: i64,
365 pub old_tokens: usize,
366 pub new_tokens: usize,
367 pub delta_tokens: i64,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
372pub struct DiffReceipt {
373 pub schema_version: u32,
374 pub generated_at_ms: u128,
375 pub tool: ToolInfo,
376 pub mode: String,
377 pub from_source: String,
378 pub to_source: String,
379 pub diff_rows: Vec<DiffRow>,
380 pub totals: DiffTotals,
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
388#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
389#[serde(rename_all = "kebab-case")]
390pub enum TableFormat {
391 Md,
393 Tsv,
395 Json,
397}
398
399#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
400#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
401#[serde(rename_all = "kebab-case")]
402pub enum ExportFormat {
403 Csv,
405 Jsonl,
407 Json,
409 Cyclonedx,
411}
412
413#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
414#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
415#[serde(rename_all = "kebab-case")]
416pub enum ConfigMode {
417 #[default]
419 Auto,
420 None,
422}
423
424#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
425#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
426#[serde(rename_all = "kebab-case")]
427pub enum ChildrenMode {
428 Collapse,
430 Separate,
432}
433
434#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
435#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
436#[serde(rename_all = "kebab-case")]
437pub enum ChildIncludeMode {
438 Separate,
440 ParentsOnly,
442}
443
444#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
445#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
446#[serde(rename_all = "kebab-case")]
447pub enum RedactMode {
448 None,
450 Paths,
452 All,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
459pub struct ContextLogRecord {
460 pub schema_version: u32,
461 pub generated_at_ms: u128,
462 pub tool: ToolInfo,
463 pub budget_tokens: usize,
464 pub used_tokens: usize,
465 pub utilization_pct: f64,
466 pub strategy: String,
467 pub rank_by: String,
468 pub file_count: usize,
469 pub total_bytes: usize,
470 pub output_destination: String,
471}
472
473pub const HANDOFF_SCHEMA_VERSION: u32 = 4;
479
480pub const CONTEXT_BUNDLE_SCHEMA_VERSION: u32 = 2;
482
483pub const CONTEXT_SCHEMA_VERSION: u32 = 3;
485
486#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
492#[serde(rename_all = "snake_case")]
493pub enum FileClassification {
494 Generated,
496 Fixture,
498 Vendored,
500 Lockfile,
502 Minified,
504 DataBlob,
506 Sourcemap,
508}
509
510#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
512#[serde(rename_all = "snake_case")]
513pub enum InclusionPolicy {
514 #[default]
516 Full,
517 HeadTail,
519 Summary,
521 Skip,
523}
524
525fn is_default_policy(policy: &InclusionPolicy) -> bool {
527 *policy == InclusionPolicy::Full
528}
529
530#[derive(Debug, Clone, Serialize, Deserialize)]
532pub struct PolicyExcludedFile {
533 pub path: String,
534 pub original_tokens: usize,
535 pub policy: InclusionPolicy,
536 pub reason: String,
537 pub classifications: Vec<FileClassification>,
538}
539
540#[derive(Debug, Clone, Serialize, Deserialize)]
542pub struct HandoffManifest {
543 pub schema_version: u32,
544 pub generated_at_ms: u128,
545 pub tool: ToolInfo,
546 pub mode: String,
547 pub inputs: Vec<String>,
548 pub output_dir: String,
549 pub budget_tokens: usize,
550 pub used_tokens: usize,
551 pub utilization_pct: f64,
552 pub strategy: String,
553 pub rank_by: String,
554 pub capabilities: Vec<CapabilityStatus>,
555 pub artifacts: Vec<ArtifactEntry>,
556 pub included_files: Vec<ContextFileRow>,
557 pub excluded_paths: Vec<HandoffExcludedPath>,
558 pub excluded_patterns: Vec<String>,
559 pub smart_excluded_files: Vec<SmartExcludedFile>,
560 pub total_files: usize,
561 pub bundled_files: usize,
562 pub intelligence_preset: String,
563 #[serde(default, skip_serializing_if = "Option::is_none")]
565 pub rank_by_effective: Option<String>,
566 #[serde(default, skip_serializing_if = "Option::is_none")]
568 pub fallback_reason: Option<String>,
569 #[serde(default, skip_serializing_if = "Vec::is_empty")]
571 pub excluded_by_policy: Vec<PolicyExcludedFile>,
572}
573
574#[derive(Debug, Clone, Serialize, Deserialize)]
576pub struct SmartExcludedFile {
577 pub path: String,
578 pub reason: String,
579 pub tokens: usize,
580}
581
582#[derive(Debug, Clone, Serialize, Deserialize)]
584pub struct ContextBundleManifest {
585 pub schema_version: u32,
586 pub generated_at_ms: u128,
587 pub tool: ToolInfo,
588 pub mode: String,
589 pub budget_tokens: usize,
590 pub used_tokens: usize,
591 pub utilization_pct: f64,
592 pub strategy: String,
593 pub rank_by: String,
594 pub file_count: usize,
595 pub bundle_bytes: usize,
596 pub artifacts: Vec<ArtifactEntry>,
597 pub included_files: Vec<ContextFileRow>,
598 pub excluded_paths: Vec<ContextExcludedPath>,
599 pub excluded_patterns: Vec<String>,
600 #[serde(default, skip_serializing_if = "Option::is_none")]
602 pub rank_by_effective: Option<String>,
603 #[serde(default, skip_serializing_if = "Option::is_none")]
605 pub fallback_reason: Option<String>,
606 #[serde(default, skip_serializing_if = "Vec::is_empty")]
608 pub excluded_by_policy: Vec<PolicyExcludedFile>,
609}
610
611#[derive(Debug, Clone, Serialize, Deserialize)]
613pub struct ContextExcludedPath {
614 pub path: String,
615 pub reason: String,
616}
617
618#[derive(Debug, Clone, Serialize, Deserialize)]
620pub struct HandoffIntelligence {
621 pub tree: Option<String>,
622 pub tree_depth: Option<usize>,
623 pub hotspots: Option<Vec<HandoffHotspot>>,
624 pub complexity: Option<HandoffComplexity>,
625 pub derived: Option<HandoffDerived>,
626 pub warnings: Vec<String>,
627}
628
629#[derive(Debug, Clone, Serialize, Deserialize)]
631pub struct HandoffExcludedPath {
632 pub path: String,
633 pub reason: String,
634}
635
636#[derive(Debug, Clone, Serialize, Deserialize)]
638pub struct HandoffHotspot {
639 pub path: String,
640 pub commits: usize,
641 pub lines: usize,
642 pub score: usize,
643}
644
645#[derive(Debug, Clone, Serialize, Deserialize)]
647pub struct HandoffComplexity {
648 pub total_functions: usize,
649 pub avg_function_length: f64,
650 pub max_function_length: usize,
651 pub avg_cyclomatic: f64,
652 pub max_cyclomatic: usize,
653 pub high_risk_files: usize,
654}
655
656#[derive(Debug, Clone, Serialize, Deserialize)]
658pub struct HandoffDerived {
659 pub total_files: usize,
660 pub total_code: usize,
661 pub total_lines: usize,
662 pub total_tokens: usize,
663 pub lang_count: usize,
664 pub dominant_lang: String,
665 pub dominant_pct: f64,
666}
667
668#[derive(Debug, Clone, Serialize, Deserialize)]
670pub struct CapabilityStatus {
671 pub name: String,
672 pub status: CapabilityState,
673 #[serde(skip_serializing_if = "Option::is_none")]
674 pub reason: Option<String>,
675}
676
677#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
679#[serde(rename_all = "snake_case")]
680pub enum CapabilityState {
681 Available,
683 Skipped,
685 Unavailable,
687}
688
689#[derive(Debug, Clone, Serialize, Deserialize)]
691pub struct ArtifactEntry {
692 pub name: String,
693 pub path: String,
694 pub description: String,
695 pub bytes: u64,
696 #[serde(skip_serializing_if = "Option::is_none")]
697 pub hash: Option<ArtifactHash>,
698}
699
700#[derive(Debug, Clone, Serialize, Deserialize)]
702pub struct ArtifactHash {
703 pub algo: String,
704 pub hash: String,
705}