1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use clap::Parser;
use quelch::ai::AiCommands;
use std::path::PathBuf;
#[derive(Parser)]
#[command(
name = "quelch",
version,
about = "Ingest data directly into Azure AI Search"
)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
/// Config file path
#[arg(short, long, default_value = "quelch.yaml", global = true)]
pub config: PathBuf,
/// Increase verbosity (-v, -vv, -vvv)
#[arg(short, long, action = clap::ArgAction::Count, global = true)]
pub verbose: u8,
/// Suppress TUI, only log errors
#[arg(short, long, global = true)]
pub quiet: bool,
/// Output logs as JSON
#[arg(long, global = true)]
pub json: bool,
/// Disable TUI and fall back to plain structured logs
#[arg(long, global = true)]
pub no_tui: bool,
}
#[derive(clap::Subcommand)]
pub enum Commands {
/// Run a one-shot sync of all configured sources
Sync {
/// Auto-create missing indexes without prompting
#[arg(long)]
create_indexes: bool,
/// Also purge orphaned documents from indexes
#[arg(long)]
purge: bool,
/// Maximum number of documents to sync (useful for debugging)
#[arg(long)]
max_docs: Option<u64>,
},
/// Run continuous sync (polls at configured interval)
Watch {
/// Auto-create missing indexes without prompting
#[arg(long)]
create_indexes: bool,
/// Maximum number of documents to sync per cycle (useful for debugging)
#[arg(long)]
max_docs: Option<u64>,
},
/// Check and create Azure AI Search indexes needed by the config
Setup {
/// Auto-create without prompting
#[arg(short, long)]
yes: bool,
},
/// Show sync status for all sources
Status,
/// Reset sync state (force full re-sync on next run)
Reset {
/// Source name to reset (omit to reset all)
source: Option<String>,
/// Only reset a single subsource (project or space key) within the source
#[arg(long)]
subsource: Option<String>,
},
/// Delete all configured indexes from Azure AI Search and clear sync state
ResetIndexes,
/// Validate config file without running
Validate,
/// Generate a starter quelch.yaml config
Init,
/// Start a local mock Jira and Confluence server for testing
Mock {
/// Port to listen on
#[arg(short, long, default_value = "9999")]
port: u16,
},
/// Run quelch against a fully simulated environment for local testing and CI.
Sim {
/// Run for this long then exit. Default: run until Ctrl-C. Example: 30s, 2m, 1h.
#[arg(long)]
duration: Option<humantime::Duration>,
/// Seed the activity generator for reproducible runs.
#[arg(long)]
seed: Option<u64>,
/// Scale activity rate. 1.0 = default, 2.0 = twice as fast.
#[arg(long, default_value = "1.0")]
rate_multiplier: f64,
/// Probability each Azure request gets a 429 or 503. 0.0 disables.
#[arg(long, default_value = "0.03")]
fault_rate: f64,
/// CI-friendly: fail with exit code 1 if fewer than N docs are indexed.
#[arg(long)]
assert_docs: Option<u64>,
/// Render the TUI to a headless backend and write a multi-frame text
/// dump to this file. Enables deterministic verification of the TUI
/// from CI or an AI agent. Implies --no-tui for stdout.
#[arg(long)]
snapshot_to: Option<PathBuf>,
/// Number of frames to capture when --snapshot-to is set.
#[arg(long, default_value = "10")]
snapshot_frames: u32,
/// Width of the TestBackend when --snapshot-to is set. Default 120.
#[arg(long, default_value = "120")]
snapshot_width: u16,
/// Height of the TestBackend when --snapshot-to is set. Default 40.
#[arg(long, default_value = "40")]
snapshot_height: u16,
},
/// Search indexed data in Azure AI Search
Search {
/// The search query
query: String,
/// Search a specific index (default: search all configured indexes)
#[arg(short, long)]
index: Option<String>,
/// Maximum results per index
#[arg(short, long, default_value = "5")]
top: usize,
/// Output raw JSON instead of formatted results
#[arg(long)]
json: bool,
},
/// Manage AI embedding configuration
Ai {
#[command(subcommand)]
command: Option<AiCommands>,
},
/// Generate Copilot Studio agent topics and instructions
GenerateAgent {
/// Output directory for generated files
#[arg(short, long, default_value = "copilot-studio")]
output: PathBuf,
},
}