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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf;
#[derive(Parser)]
pub struct ToolArgs {
#[command(subcommand)]
pub command: ToolCommands,
}
#[derive(Subcommand)]
pub enum ToolCommands {
/// Clean useless comments from source code
#[command(alias = "cuc")]
CleanUselessComments(CleanUselessCommentsArgs),
/// Remove useless directories recursively
RmUselessDirs(RmUselessDirsArgs),
/// Sync ignore rules across OpenCode/Cursor/Claude Code
#[command(alias = "si")]
SyncIgnore(SyncIgnoreArgs),
/// Manage agent init files (AGENTS.md / CLAUDE.md / .cursor/ etc.)
AgentsMd(AgentsMdArgs),
}
#[derive(Parser, Debug, Clone)]
pub struct AgentsMdArgs {
#[command(subcommand)]
pub command: AgentsMdCommands,
}
#[derive(Subcommand, Debug, Clone)]
pub enum AgentsMdCommands {
/// Scan the project for agent init files and list their relative paths
Scan(AgentsMdScanArgs),
/// Delete agent init files recorded in project config
Clean(AgentsMdCleanArgs),
/// Restore agent init files from the default branch
Revert(AgentsMdRevertArgs),
}
#[derive(Parser, Debug, Clone)]
pub struct AgentsMdScanArgs {
/// Write discovered paths into the project `.llman/config.yaml` (create if missing)
#[arg(long, action = clap::ArgAction::SetTrue)]
pub upsert_project_configs: bool,
/// Configuration file path (default: project `.llman/config.yaml`, then global)
#[arg(long, short = 'c')]
pub config: Option<PathBuf>,
/// Verbose output
#[arg(long, short = 'v', action = clap::ArgAction::SetTrue)]
pub verbose: bool,
}
#[derive(Parser, Debug, Clone)]
pub struct AgentsMdCleanArgs {
/// Apply changes (default: dry-run preview)
#[arg(short = 'y', long, action = clap::ArgAction::SetTrue)]
pub yes: bool,
/// `git add` the removed files and commit on the current branch
#[arg(long, action = clap::ArgAction::SetTrue)]
pub commit: bool,
/// Force `--commit` even on the default branch (main/master)
#[arg(long, short = 'f', action = clap::ArgAction::SetTrue)]
pub force: bool,
/// Configuration file path (default: project `.llman/config.yaml`, then global)
#[arg(long, short = 'c')]
pub config: Option<PathBuf>,
/// Verbose output
#[arg(long, short = 'v', action = clap::ArgAction::SetTrue)]
pub verbose: bool,
}
#[derive(Parser, Debug, Clone)]
pub struct AgentsMdRevertArgs {
/// Apply changes (default: dry-run preview)
#[arg(short = 'y', long, action = clap::ArgAction::SetTrue)]
pub yes: bool,
/// Create a branch and commit the restored files
#[arg(long, action = clap::ArgAction::SetTrue)]
pub commit: bool,
/// Configuration file path (default: project `.llman/config.yaml`, then global)
#[arg(long, short = 'c')]
pub config: Option<PathBuf>,
/// Verbose output
#[arg(long, short = 'v', action = clap::ArgAction::SetTrue)]
pub verbose: bool,
}
#[derive(Parser, Debug, Clone)]
pub struct CleanUselessCommentsArgs {
/// Configuration file path (default: .llman/config.yaml)
#[arg(long, short = 'c')]
pub config: Option<PathBuf>,
/// Dry run mode, show changes without applying them
#[arg(long, short = 'd')]
pub dry_run: bool,
/// Apply changes (default: dry run)
#[arg(long, short = 'y')]
pub yes: bool,
/// Interactive mode, confirm changes before applying
#[arg(long, short = 'i')]
pub interactive: bool,
/// Force execution, skip confirmation prompts
#[arg(long, short = 'f')]
pub force: bool,
/// Verbose output
#[arg(long, short = 'v')]
pub verbose: bool,
/// Only process Git tracked files
#[arg(long)]
pub git_only: bool,
/// Files to process (if not specified, use config scope)
pub files: Vec<PathBuf>,
}
#[derive(Parser, Debug, Clone)]
pub struct RmUselessDirsArgs {
/// Configuration file path (default: .llman/config.yaml)
#[arg(long, short = 'c')]
pub config: Option<PathBuf>,
/// Directory to scan (default: current directory)
pub path: Option<PathBuf>,
/// Actually delete empty directories (default: dry run)
#[arg(short = 'y', long)]
pub yes: bool,
/// Path to a .gitignore file to honor (default: repo root .gitignore, else `target`/.gitignore)
#[arg(long)]
pub gitignore: Option<PathBuf>,
/// Treat directories containing only ignored entries as removable (deletes ignored files/dirs)
#[arg(long)]
pub prune_ignored: bool,
/// Verbose output
#[arg(long, short = 'v')]
pub verbose: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum SyncIgnoreTarget {
/// Project root `.ignore` (OpenCode / ripgrep)
Opencode,
/// Project root `.cursorignore`
Cursor,
/// `.claude/settings.json`
ClaudeShared,
/// `.claude/settings.local.json`
ClaudeLocal,
/// All supported targets
All,
}
#[derive(Parser, Debug, Clone)]
#[command(
after_help = "Examples:\n llman tool sync-ignore\n llman tool sync-ignore -y\n llman tool sync-ignore --target cursor --target claude-shared -y\n llman tool sync-ignore --interactive\n"
)]
pub struct SyncIgnoreArgs {
/// Apply changes (default: dry-run preview)
#[arg(short = 'y', long, action = clap::ArgAction::SetTrue)]
pub yes: bool,
/// Interactive mode (MultiSelect targets + preview + confirm)
#[arg(long, action = clap::ArgAction::SetTrue)]
pub interactive: bool,
/// Force execution even when no git repository is found
#[arg(long, action = clap::ArgAction::SetTrue)]
pub force: bool,
/// Verbose output
#[arg(long, short = 'v', action = clap::ArgAction::SetTrue)]
pub verbose: bool,
/// Output target(s) to sync (repeatable, or comma-separated)
#[arg(long, short = 't', value_enum, value_delimiter = ',')]
pub target: Vec<SyncIgnoreTarget>,
/// Additional input file path(s) to include as sources (repeatable)
#[arg(long, short = 'i')]
pub input: Vec<PathBuf>,
}