gitwise-cli 0.24.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gitwise/__init__.py +11 -0
- gitwise/__main__.py +113 -0
- gitwise/_cli_completions.py +88 -0
- gitwise/_cli_dispatch.py +469 -0
- gitwise/_cli_introspection.py +275 -0
- gitwise/_cli_parser.py +345 -0
- gitwise/_cli_setup_agents.py +439 -0
- gitwise/_i18n_data.json +1934 -0
- gitwise/_paths.py +22 -0
- gitwise/_runtime_config.py +246 -0
- gitwise/audit.py +338 -0
- gitwise/branches.py +183 -0
- gitwise/clean.py +197 -0
- gitwise/commit.py +142 -0
- gitwise/conflicts.py +112 -0
- gitwise/context.py +163 -0
- gitwise/design.py +383 -0
- gitwise/diff.py +309 -0
- gitwise/doctor.py +116 -0
- gitwise/git.py +254 -0
- gitwise/health.py +345 -0
- gitwise/i18n.py +99 -0
- gitwise/log.py +329 -0
- gitwise/merge.py +193 -0
- gitwise/optimize.py +212 -0
- gitwise/output.py +652 -0
- gitwise/pick.py +102 -0
- gitwise/pr.py +543 -0
- gitwise/py.typed +0 -0
- gitwise/schema.py +49 -0
- gitwise/setup.py +551 -0
- gitwise/setup_agents/__init__.py +36 -0
- gitwise/setup_agents/adapters/__init__.py +17 -0
- gitwise/setup_agents/adapters/aider.py +5 -0
- gitwise/setup_agents/adapters/base.py +5 -0
- gitwise/setup_agents/adapters/codex.py +5 -0
- gitwise/setup_agents/adapters/continue_adapter.py +5 -0
- gitwise/setup_agents/adapters/cursor.py +5 -0
- gitwise/setup_agents/adapters/opencode.py +5 -0
- gitwise/setup_agents/adapters/pi.py +5 -0
- gitwise/setup_agents/exec.py +449 -0
- gitwise/setup_agents/format.py +164 -0
- gitwise/setup_agents/plan.py +254 -0
- gitwise/setup_agents/plan_gitfiles.py +167 -0
- gitwise/setup_agents/plan_skills.py +256 -0
- gitwise/setup_agents/providers/__init__.py +96 -0
- gitwise/setup_agents/providers/aider.py +11 -0
- gitwise/setup_agents/providers/base.py +79 -0
- gitwise/setup_agents/providers/claude.py +408 -0
- gitwise/setup_agents/providers/codex.py +11 -0
- gitwise/setup_agents/providers/continue_adapter.py +11 -0
- gitwise/setup_agents/providers/cursor.py +11 -0
- gitwise/setup_agents/providers/opencode.py +11 -0
- gitwise/setup_agents/providers/pi.py +11 -0
- gitwise/setup_agents/state.py +141 -0
- gitwise/setup_agents/types.py +48 -0
- gitwise/share/agents/skills/git-audit/SKILL.md +25 -0
- gitwise/share/agents/skills/git-clean/SKILL.md +22 -0
- gitwise/share/agents/skills/git-optimize/SKILL.md +21 -0
- gitwise/share/aider/CONVENTIONS.md.template +8 -0
- gitwise/share/aider/aider.conf.yml.template +4 -0
- gitwise/share/claude/CLAUDE.md.template +9 -0
- gitwise/share/claude/rules/gitwise.md +16 -0
- gitwise/share/claude/settings.json.template +47 -0
- gitwise/share/claude/skills/git-audit/SKILL.md +25 -0
- gitwise/share/claude/skills/git-clean/SKILL.md +22 -0
- gitwise/share/claude/skills/git-optimize/SKILL.md +21 -0
- gitwise/share/codex/agents/gitwise.toml.template +18 -0
- gitwise/share/continue/rules/gitwise.md.template +14 -0
- gitwise/share/cursor/rules/gitwise.mdc.template +16 -0
- gitwise/share/git-config-modern.txt +48 -0
- gitwise/share/hooks/commit-msg +22 -0
- gitwise/share/hooks/pre-commit +19 -0
- gitwise/share/opencode/agents/gitwise.md.template +14 -0
- gitwise/share/pi/skills/gitwise.md.template +14 -0
- gitwise/share/schemas/v1/input/audit.json +40 -0
- gitwise/share/schemas/v1/input/branches.json +51 -0
- gitwise/share/schemas/v1/input/clean.json +52 -0
- gitwise/share/schemas/v1/input/commands.json +36 -0
- gitwise/share/schemas/v1/input/commit.json +63 -0
- gitwise/share/schemas/v1/input/completions.json +51 -0
- gitwise/share/schemas/v1/input/conflicts.json +46 -0
- gitwise/share/schemas/v1/input/context.json +36 -0
- gitwise/share/schemas/v1/input/diff.json +56 -0
- gitwise/share/schemas/v1/input/doctor.json +36 -0
- gitwise/share/schemas/v1/input/health.json +36 -0
- gitwise/share/schemas/v1/input/log.json +71 -0
- gitwise/share/schemas/v1/input/merge.json +63 -0
- gitwise/share/schemas/v1/input/optimize.json +44 -0
- gitwise/share/schemas/v1/input/pick.json +63 -0
- gitwise/share/schemas/v1/input/pr.json +51 -0
- gitwise/share/schemas/v1/input/schema.json +48 -0
- gitwise/share/schemas/v1/input/setup-agents.json +108 -0
- gitwise/share/schemas/v1/input/setup.json +55 -0
- gitwise/share/schemas/v1/input/show.json +46 -0
- gitwise/share/schemas/v1/input/snapshot.json +36 -0
- gitwise/share/schemas/v1/input/stash.json +68 -0
- gitwise/share/schemas/v1/input/status.json +36 -0
- gitwise/share/schemas/v1/input/suggest.json +36 -0
- gitwise/share/schemas/v1/input/summarize.json +44 -0
- gitwise/share/schemas/v1/input/sync.json +55 -0
- gitwise/share/schemas/v1/input/tag.json +73 -0
- gitwise/share/schemas/v1/input/undo.json +60 -0
- gitwise/share/schemas/v1/input/update.json +40 -0
- gitwise/share/schemas/v1/input/worktree.json +50 -0
- gitwise/show.py +118 -0
- gitwise/snapshot.py +110 -0
- gitwise/stash.py +188 -0
- gitwise/status.py +93 -0
- gitwise/suggest.py +148 -0
- gitwise/summarize.py +202 -0
- gitwise/sync.py +257 -0
- gitwise/tag.py +252 -0
- gitwise/undo.py +145 -0
- gitwise/update.py +42 -0
- gitwise/utils/__init__.py +1 -0
- gitwise/utils/git_output.py +51 -0
- gitwise/utils/json_envelope.py +58 -0
- gitwise/utils/parsing.py +34 -0
- gitwise/worktree.py +182 -0
- gitwise_cli-0.24.2.dist-info/METADATA +151 -0
- gitwise_cli-0.24.2.dist-info/RECORD +125 -0
- gitwise_cli-0.24.2.dist-info/WHEEL +4 -0
- gitwise_cli-0.24.2.dist-info/entry_points.txt +2 -0
- gitwise_cli-0.24.2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""CLI introspection: help payload, command metadata, JSON schema generation."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from typing import TypedDict
|
|
5
|
+
|
|
6
|
+
from . import __version__
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _json_safe(value: object) -> object:
|
|
10
|
+
if value is argparse.SUPPRESS:
|
|
11
|
+
return None
|
|
12
|
+
if value is None or isinstance(value, str | int | float | bool):
|
|
13
|
+
return value
|
|
14
|
+
if isinstance(value, list | tuple | set):
|
|
15
|
+
return [_json_safe(item) for item in value]
|
|
16
|
+
if isinstance(value, dict):
|
|
17
|
+
return {str(key): _json_safe(item) for key, item in value.items()}
|
|
18
|
+
return str(value)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _subparsers_action(parser: argparse.ArgumentParser) -> argparse._SubParsersAction | None:
|
|
22
|
+
for action in parser._actions:
|
|
23
|
+
if isinstance(action, argparse._SubParsersAction):
|
|
24
|
+
return action
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _serialize_actions(parser: argparse.ArgumentParser) -> list[dict[str, object]]:
|
|
29
|
+
items: list[dict[str, object]] = []
|
|
30
|
+
for action in parser._actions:
|
|
31
|
+
if action.dest in {"help"}:
|
|
32
|
+
continue
|
|
33
|
+
if isinstance(action, argparse._SubParsersAction):
|
|
34
|
+
continue
|
|
35
|
+
option_strings = list(action.option_strings)
|
|
36
|
+
items.append(
|
|
37
|
+
{
|
|
38
|
+
"kind": "option" if option_strings else "argument",
|
|
39
|
+
"name": action.dest,
|
|
40
|
+
"flags": option_strings,
|
|
41
|
+
"required": bool(getattr(action, "required", False)),
|
|
42
|
+
"nargs": _json_safe(action.nargs),
|
|
43
|
+
"default": _json_safe(action.default),
|
|
44
|
+
"choices": _json_safe(list(action.choices) if action.choices else []),
|
|
45
|
+
"help": "" if action.help is argparse.SUPPRESS else (action.help or ""),
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
return items
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def extract_command_token(argv: list[str]) -> str | None:
|
|
52
|
+
skip_next = False
|
|
53
|
+
for token in argv:
|
|
54
|
+
if skip_next:
|
|
55
|
+
skip_next = False
|
|
56
|
+
continue
|
|
57
|
+
if token in {"--lang", "--theme"}:
|
|
58
|
+
skip_next = True
|
|
59
|
+
continue
|
|
60
|
+
if token.startswith("-"):
|
|
61
|
+
continue
|
|
62
|
+
return token
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def help_payload(parser: argparse.ArgumentParser, command: str | None = None) -> dict[str, object]:
|
|
67
|
+
payload: dict[str, object] = {
|
|
68
|
+
"v": 2,
|
|
69
|
+
"ok": True,
|
|
70
|
+
"kind": "help",
|
|
71
|
+
"schema": "gitwise/help/v1",
|
|
72
|
+
"version": __version__,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
sub_action = _subparsers_action(parser)
|
|
76
|
+
if command is None or sub_action is None or command not in sub_action.choices:
|
|
77
|
+
commands: list[dict[str, object]] = []
|
|
78
|
+
if sub_action is not None:
|
|
79
|
+
help_by_parser_id: dict[int, str] = {}
|
|
80
|
+
for pseudo in sub_action._choices_actions:
|
|
81
|
+
parser_name = str(pseudo.dest)
|
|
82
|
+
parser_obj = sub_action.choices.get(parser_name)
|
|
83
|
+
if parser_obj is None:
|
|
84
|
+
continue
|
|
85
|
+
parser_id = id(parser_obj)
|
|
86
|
+
if parser_id not in help_by_parser_id:
|
|
87
|
+
help_by_parser_id[parser_id] = pseudo.help or ""
|
|
88
|
+
|
|
89
|
+
seen_parsers: set[int] = set()
|
|
90
|
+
for name, choice_parser in sorted(sub_action.choices.items()):
|
|
91
|
+
parser_id = id(choice_parser)
|
|
92
|
+
if parser_id in seen_parsers:
|
|
93
|
+
continue
|
|
94
|
+
seen_parsers.add(parser_id)
|
|
95
|
+
aliases = [
|
|
96
|
+
alias
|
|
97
|
+
for alias, alias_parser in sub_action.choices.items()
|
|
98
|
+
if alias_parser is choice_parser and alias != name
|
|
99
|
+
]
|
|
100
|
+
commands.append(
|
|
101
|
+
{
|
|
102
|
+
"name": name,
|
|
103
|
+
"help": help_by_parser_id.get(parser_id, choice_parser.description or ""),
|
|
104
|
+
"aliases": sorted(aliases),
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
payload.update(
|
|
109
|
+
{
|
|
110
|
+
"scope": "root",
|
|
111
|
+
"usage": parser.format_usage().strip(),
|
|
112
|
+
"description": parser.description or "",
|
|
113
|
+
"options": _serialize_actions(parser),
|
|
114
|
+
"commands": sorted(commands, key=lambda item: str(item["name"])),
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
return payload
|
|
118
|
+
|
|
119
|
+
command_parser = sub_action.choices[command]
|
|
120
|
+
payload.update(
|
|
121
|
+
{
|
|
122
|
+
"scope": "command",
|
|
123
|
+
"command": command,
|
|
124
|
+
"usage": command_parser.format_usage().strip(),
|
|
125
|
+
"description": command_parser.description or "",
|
|
126
|
+
"options": _serialize_actions(command_parser),
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
return payload
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class CommandMetadata(TypedDict):
|
|
133
|
+
name: str
|
|
134
|
+
help: str
|
|
135
|
+
aliases: list[str]
|
|
136
|
+
supports_json: bool
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def canonical_command_name(command_parser: argparse.ArgumentParser) -> str:
|
|
140
|
+
prog = command_parser.prog.strip()
|
|
141
|
+
if not prog:
|
|
142
|
+
return ""
|
|
143
|
+
parts = prog.split()
|
|
144
|
+
return parts[-1] if parts else ""
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def commands_metadata(parser: argparse.ArgumentParser) -> list[CommandMetadata]:
|
|
148
|
+
sub_action = _subparsers_action(parser)
|
|
149
|
+
if sub_action is None:
|
|
150
|
+
return []
|
|
151
|
+
|
|
152
|
+
help_by_parser_id: dict[int, str] = {}
|
|
153
|
+
for pseudo in sub_action._choices_actions:
|
|
154
|
+
parser_name = str(pseudo.dest)
|
|
155
|
+
parser_obj = sub_action.choices.get(parser_name)
|
|
156
|
+
if parser_obj is None:
|
|
157
|
+
continue
|
|
158
|
+
parser_id = id(parser_obj)
|
|
159
|
+
if parser_id not in help_by_parser_id:
|
|
160
|
+
help_by_parser_id[parser_id] = pseudo.help or ""
|
|
161
|
+
|
|
162
|
+
entries: list[CommandMetadata] = []
|
|
163
|
+
seen_parser_ids: set[int] = set()
|
|
164
|
+
for command_parser in sub_action.choices.values():
|
|
165
|
+
parser_id = id(command_parser)
|
|
166
|
+
if parser_id in seen_parser_ids:
|
|
167
|
+
continue
|
|
168
|
+
seen_parser_ids.add(parser_id)
|
|
169
|
+
|
|
170
|
+
name = canonical_command_name(command_parser)
|
|
171
|
+
aliases = sorted(
|
|
172
|
+
[
|
|
173
|
+
n
|
|
174
|
+
for n, candidate in sub_action.choices.items()
|
|
175
|
+
if candidate is command_parser and n != name
|
|
176
|
+
]
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
entries.append(
|
|
180
|
+
{
|
|
181
|
+
"name": name,
|
|
182
|
+
"help": help_by_parser_id.get(parser_id, command_parser.description or ""),
|
|
183
|
+
"aliases": aliases,
|
|
184
|
+
"supports_json": True,
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
return sorted(entries, key=lambda item: str(item["name"]))
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _json_type_for_action(action: argparse.Action) -> str:
|
|
192
|
+
if isinstance(action, argparse._StoreTrueAction | argparse._StoreFalseAction):
|
|
193
|
+
return "boolean"
|
|
194
|
+
|
|
195
|
+
action_type = getattr(action, "type", None)
|
|
196
|
+
if action_type is int:
|
|
197
|
+
return "integer"
|
|
198
|
+
if action_type is float:
|
|
199
|
+
return "number"
|
|
200
|
+
return "string"
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _action_property_schema(action: argparse.Action) -> dict[str, object]:
|
|
204
|
+
value_schema: dict[str, object] = {"type": _json_type_for_action(action)}
|
|
205
|
+
|
|
206
|
+
if action.choices:
|
|
207
|
+
value_schema["enum"] = [_json_safe(choice) for choice in action.choices]
|
|
208
|
+
|
|
209
|
+
description = "" if action.help is argparse.SUPPRESS else (action.help or "")
|
|
210
|
+
if description:
|
|
211
|
+
value_schema["description"] = description
|
|
212
|
+
|
|
213
|
+
if action.default is not argparse.SUPPRESS and action.default is not None:
|
|
214
|
+
value_schema["default"] = _json_safe(action.default)
|
|
215
|
+
|
|
216
|
+
nargs = action.nargs
|
|
217
|
+
if nargs in ("*", "+") or (isinstance(nargs, int) and nargs > 1):
|
|
218
|
+
array_schema: dict[str, object] = {
|
|
219
|
+
"type": "array",
|
|
220
|
+
"items": value_schema,
|
|
221
|
+
}
|
|
222
|
+
if nargs == "+":
|
|
223
|
+
array_schema["minItems"] = 1
|
|
224
|
+
if isinstance(nargs, int) and nargs > 1:
|
|
225
|
+
array_schema["minItems"] = nargs
|
|
226
|
+
array_schema["maxItems"] = nargs
|
|
227
|
+
return array_schema
|
|
228
|
+
|
|
229
|
+
return value_schema
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _action_required(action: argparse.Action) -> bool:
|
|
233
|
+
if action.option_strings:
|
|
234
|
+
return bool(getattr(action, "required", False))
|
|
235
|
+
|
|
236
|
+
nargs = action.nargs
|
|
237
|
+
if nargs in (None, "+"):
|
|
238
|
+
return True
|
|
239
|
+
if isinstance(nargs, int):
|
|
240
|
+
return nargs > 0
|
|
241
|
+
return False
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def command_input_schema(command_parser: argparse.ArgumentParser) -> dict[str, object]:
|
|
245
|
+
properties: dict[str, object] = {}
|
|
246
|
+
required: list[str] = []
|
|
247
|
+
|
|
248
|
+
for action in command_parser._actions:
|
|
249
|
+
if action.dest == "help" or isinstance(action, argparse._SubParsersAction):
|
|
250
|
+
continue
|
|
251
|
+
properties[action.dest] = _action_property_schema(action)
|
|
252
|
+
if _action_required(action):
|
|
253
|
+
required.append(action.dest)
|
|
254
|
+
|
|
255
|
+
schema: dict[str, object] = {
|
|
256
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
257
|
+
"$id": f"https://gitwise.dev/schemas/v1/input/{canonical_command_name(command_parser)}.json",
|
|
258
|
+
"title": f"gitwise {canonical_command_name(command_parser)} cli input",
|
|
259
|
+
"type": "object",
|
|
260
|
+
"additionalProperties": False,
|
|
261
|
+
"properties": properties,
|
|
262
|
+
}
|
|
263
|
+
if required:
|
|
264
|
+
schema["required"] = sorted(set(required))
|
|
265
|
+
|
|
266
|
+
return schema
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def resolve_command_parser(
|
|
270
|
+
*, parser: argparse.ArgumentParser, name: str
|
|
271
|
+
) -> argparse.ArgumentParser | None:
|
|
272
|
+
sub_action = _subparsers_action(parser)
|
|
273
|
+
if sub_action is None:
|
|
274
|
+
return None
|
|
275
|
+
return sub_action.choices.get(name)
|
gitwise/_cli_parser.py
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"""Argparse parser builder for all gitwise subcommands."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from . import __version__
|
|
6
|
+
from .design import GitwiseRichHelpFormatter
|
|
7
|
+
from .i18n import t
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _root_help_epilog() -> str:
|
|
11
|
+
return t("help_root_environment_epilog")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
15
|
+
parent = argparse.ArgumentParser(add_help=False)
|
|
16
|
+
parent.add_argument(
|
|
17
|
+
"--lang",
|
|
18
|
+
choices=["es", "en"],
|
|
19
|
+
default=None,
|
|
20
|
+
help="output language (default: auto-detect from locale)",
|
|
21
|
+
)
|
|
22
|
+
parent.add_argument(
|
|
23
|
+
"--theme",
|
|
24
|
+
choices=["dark", "light", "auto"],
|
|
25
|
+
default=None,
|
|
26
|
+
help="color theme: dark, light, or auto-detect (default: auto)",
|
|
27
|
+
)
|
|
28
|
+
parent.add_argument("--json", action="store_true", help="output JSON")
|
|
29
|
+
parent.add_argument(
|
|
30
|
+
"--json-pretty",
|
|
31
|
+
"--pretty",
|
|
32
|
+
dest="json_pretty",
|
|
33
|
+
action="store_true",
|
|
34
|
+
help="pretty-print JSON output",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
parser = argparse.ArgumentParser(
|
|
38
|
+
prog="gitwise",
|
|
39
|
+
description="CLI for optimizing git workflows and coding agents integration",
|
|
40
|
+
formatter_class=GitwiseRichHelpFormatter,
|
|
41
|
+
epilog=_root_help_epilog(),
|
|
42
|
+
parents=[parent],
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument("--version", action="version", version=f"gitwise {__version__}")
|
|
45
|
+
|
|
46
|
+
sub = parser.add_subparsers(dest="command", metavar="COMMAND")
|
|
47
|
+
|
|
48
|
+
p = sub.add_parser("doctor", help="check requirements and environment", parents=[parent])
|
|
49
|
+
|
|
50
|
+
p = sub.add_parser(
|
|
51
|
+
"setup-agents",
|
|
52
|
+
help="install canonical agents layout + optional providers globally or locally",
|
|
53
|
+
parents=[parent],
|
|
54
|
+
)
|
|
55
|
+
p.add_argument(
|
|
56
|
+
"--local",
|
|
57
|
+
action="store_true",
|
|
58
|
+
help="install into current repo instead of global home",
|
|
59
|
+
)
|
|
60
|
+
p.add_argument(
|
|
61
|
+
"--no-skills",
|
|
62
|
+
action="store_true",
|
|
63
|
+
dest="no_skills",
|
|
64
|
+
help="skip skills installation (global mode only)",
|
|
65
|
+
)
|
|
66
|
+
p.add_argument("--dry-run", action="store_true", help="show actions without executing")
|
|
67
|
+
p.add_argument("--yes", "-y", action="store_true", help="skip confirmation")
|
|
68
|
+
p.add_argument(
|
|
69
|
+
"--no-symlinks",
|
|
70
|
+
action="store_true",
|
|
71
|
+
help="force @AGENTS.md import fallback (no symlinks) — --local only",
|
|
72
|
+
)
|
|
73
|
+
p.add_argument(
|
|
74
|
+
"--strict",
|
|
75
|
+
action="store_true",
|
|
76
|
+
help="treat warnings as errors (CI) — --local only",
|
|
77
|
+
)
|
|
78
|
+
p.add_argument(
|
|
79
|
+
"--replace-claude-with-symlink",
|
|
80
|
+
action="store_true",
|
|
81
|
+
dest="replace_claude_with_symlink",
|
|
82
|
+
help="bucket 4: replace CLAUDE.md with symlink to AGENTS.md — --local only",
|
|
83
|
+
)
|
|
84
|
+
p.add_argument(
|
|
85
|
+
"--migrate-legacy-claude",
|
|
86
|
+
action="store_true",
|
|
87
|
+
dest="migrate_legacy_claude",
|
|
88
|
+
help="migrate legacy Claude-only layout to canonical AGENTS/.agents — --local only",
|
|
89
|
+
)
|
|
90
|
+
p.add_argument(
|
|
91
|
+
"--frozen-time",
|
|
92
|
+
action="store_true",
|
|
93
|
+
dest="frozen_time",
|
|
94
|
+
help="freeze snapshot timestamp — --local only",
|
|
95
|
+
)
|
|
96
|
+
p.add_argument(
|
|
97
|
+
"--no-git-files",
|
|
98
|
+
action="store_true",
|
|
99
|
+
dest="no_git_files",
|
|
100
|
+
help="don't touch .gitignore or .gitattributes — --local only",
|
|
101
|
+
)
|
|
102
|
+
p.add_argument(
|
|
103
|
+
"--providers",
|
|
104
|
+
nargs="*",
|
|
105
|
+
default=None,
|
|
106
|
+
dest="providers",
|
|
107
|
+
help="install config for coding providers (comma-separated: claude,cursor or multiple: --providers claude cursor)",
|
|
108
|
+
)
|
|
109
|
+
p.add_argument(
|
|
110
|
+
"--adapters",
|
|
111
|
+
nargs="*",
|
|
112
|
+
default=None,
|
|
113
|
+
dest="adapters",
|
|
114
|
+
help=argparse.SUPPRESS,
|
|
115
|
+
)
|
|
116
|
+
p.add_argument(
|
|
117
|
+
"--list-providers",
|
|
118
|
+
action="store_true",
|
|
119
|
+
dest="list_providers",
|
|
120
|
+
help="list available providers and exit",
|
|
121
|
+
)
|
|
122
|
+
p.add_argument(
|
|
123
|
+
"--list-adapters",
|
|
124
|
+
action="store_true",
|
|
125
|
+
dest="list_adapters",
|
|
126
|
+
help=argparse.SUPPRESS,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
p = sub.add_parser("setup", help="apply modern git defaults", parents=[parent])
|
|
130
|
+
p.add_argument("--dry-run", action="store_true")
|
|
131
|
+
p.add_argument("--yes", "-y", action="store_true")
|
|
132
|
+
p.add_argument(
|
|
133
|
+
"--hooks-mode",
|
|
134
|
+
choices=["preserve", "native", "legacy", "skip"],
|
|
135
|
+
default="preserve",
|
|
136
|
+
help="hooks strategy: preserve (default), native, legacy, or skip",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
p = sub.add_parser("audit", help="repository diagnostics", parents=[parent])
|
|
140
|
+
p.add_argument("--quick", action="store_true")
|
|
141
|
+
|
|
142
|
+
p = sub.add_parser("summarize", help="compact status + log", parents=[parent])
|
|
143
|
+
p.add_argument("--diff", action="store_true")
|
|
144
|
+
p.add_argument("--max-commits", type=int, default=10, dest="max_commits")
|
|
145
|
+
|
|
146
|
+
p = sub.add_parser("snapshot", help="generate .claude/git-snapshot.md", parents=[parent])
|
|
147
|
+
|
|
148
|
+
p = sub.add_parser(
|
|
149
|
+
"clean",
|
|
150
|
+
help="clean up stale branches and refs",
|
|
151
|
+
aliases=["branch-clean"],
|
|
152
|
+
parents=[parent],
|
|
153
|
+
)
|
|
154
|
+
p.add_argument("--branches", action="store_true")
|
|
155
|
+
p.add_argument("--refs", action="store_true")
|
|
156
|
+
p.add_argument("--dry-run", action="store_true")
|
|
157
|
+
p.add_argument("--yes", "-y", action="store_true")
|
|
158
|
+
|
|
159
|
+
p = sub.add_parser("optimize", help="optimize the repository", parents=[parent])
|
|
160
|
+
p.add_argument("--dry-run", action="store_true")
|
|
161
|
+
p.add_argument("--yes", "-y", action="store_true")
|
|
162
|
+
|
|
163
|
+
p = sub.add_parser("worktree", help="worktree helpers for Claude agents", parents=[parent])
|
|
164
|
+
p.add_argument("action", choices=["new", "clean"], nargs="?", metavar="new|clean")
|
|
165
|
+
p.add_argument("branch", nargs="?")
|
|
166
|
+
p.add_argument("--dry-run", action="store_true")
|
|
167
|
+
|
|
168
|
+
p = sub.add_parser(
|
|
169
|
+
"diff",
|
|
170
|
+
help="changed files with diffstat (default), or patch view",
|
|
171
|
+
parents=[parent],
|
|
172
|
+
)
|
|
173
|
+
diff_group = p.add_mutually_exclusive_group()
|
|
174
|
+
diff_group.add_argument("--staged", action="store_true", help="show staged changes only")
|
|
175
|
+
diff_group.add_argument("--name-only", action="store_true", help="show only file names")
|
|
176
|
+
diff_group.add_argument(
|
|
177
|
+
"--full", "--patch", action="store_true", help="show full patch with delta integration"
|
|
178
|
+
)
|
|
179
|
+
p.add_argument("--stat", action="store_true", help="show diffstat (default behavior)")
|
|
180
|
+
|
|
181
|
+
p = sub.add_parser("log", help="pretty git log with filters", parents=[parent])
|
|
182
|
+
p.add_argument("--oneline", action="store_true", help="one line per commit")
|
|
183
|
+
p.add_argument("--graph", action="store_true", help="show branch topology graph")
|
|
184
|
+
p.add_argument("--author", type=str, default=None, help="filter by author")
|
|
185
|
+
p.add_argument("--grep", type=str, default=None, help="filter by message pattern")
|
|
186
|
+
p.add_argument("--since", type=str, default=None, help="show commits since date")
|
|
187
|
+
p.add_argument("--until", type=str, default=None, help="show commits until date")
|
|
188
|
+
p.add_argument("--file", type=str, default=None, help="show commits for file")
|
|
189
|
+
p.add_argument("--max-count", type=int, default=20, dest="max_count", help="max commits")
|
|
190
|
+
|
|
191
|
+
p = sub.add_parser("show", help="commit inspector", parents=[parent])
|
|
192
|
+
p.add_argument("ref", nargs="?", default="HEAD", help="commit ref (default: HEAD)")
|
|
193
|
+
p.add_argument("--stat", action="store_true", help="show diffstat")
|
|
194
|
+
|
|
195
|
+
p = sub.add_parser("commit", help="smart conventional commit", parents=[parent])
|
|
196
|
+
p.add_argument("-m", "--message", type=str, default=None, help="commit message")
|
|
197
|
+
p.add_argument("--type", type=str, default=None, help="commit type (feat/fix/etc)")
|
|
198
|
+
p.add_argument("--scope", type=str, default=None, help="commit scope")
|
|
199
|
+
p.add_argument("--breaking", action="store_true", help="breaking change (!)")
|
|
200
|
+
p.add_argument("--amend", action="store_true", help="amend last commit")
|
|
201
|
+
p.add_argument("--dry-run", action="store_true", help="show without committing")
|
|
202
|
+
|
|
203
|
+
p = sub.add_parser("branches", help="branch intelligence dashboard", parents=[parent])
|
|
204
|
+
p.add_argument("--stale", action="store_true", help="show stale [gone] branches only")
|
|
205
|
+
p.add_argument("--remote", action="store_true", help="show remote branches")
|
|
206
|
+
p.add_argument(
|
|
207
|
+
"--sort",
|
|
208
|
+
type=str,
|
|
209
|
+
default="refname",
|
|
210
|
+
help="sort field: refname, committerdate, -committerdate",
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
p = sub.add_parser("sync", help="remote fetch, safe pull/push", parents=[parent])
|
|
214
|
+
p.add_argument("--pull", action="store_true", help="pull --ff-only after fetch")
|
|
215
|
+
p.add_argument("--push", action="store_true", help="push unpushed commits")
|
|
216
|
+
p.add_argument("--remote", type=str, default=None, help="specific remote (default: all)")
|
|
217
|
+
p.add_argument("--dry-run", action="store_true", help="show planned actions")
|
|
218
|
+
|
|
219
|
+
p = sub.add_parser("pr", help="GitHub PR wrapper (requires gh)", parents=[parent])
|
|
220
|
+
p.add_argument(
|
|
221
|
+
"action",
|
|
222
|
+
nargs="?",
|
|
223
|
+
default="list",
|
|
224
|
+
choices=["list", "checks", "view", "comments"],
|
|
225
|
+
help="pr action",
|
|
226
|
+
)
|
|
227
|
+
p.add_argument(
|
|
228
|
+
"selector",
|
|
229
|
+
nargs="?",
|
|
230
|
+
default=None,
|
|
231
|
+
help="PR number/url/branch (default: current branch PR)",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
p = sub.add_parser("undo", help="reflog-based undo", parents=[parent])
|
|
235
|
+
p.add_argument("ref", nargs="?", default=None, help="target ref (default: HEAD~1)")
|
|
236
|
+
p.add_argument("--soft", action="store_true", help="soft reset (keep working tree)")
|
|
237
|
+
p.add_argument("--steps", type=int, default=1, help="number of steps back")
|
|
238
|
+
p.add_argument("--dry-run", action="store_true", help="show without resetting")
|
|
239
|
+
p.add_argument("--yes", "-y", action="store_true", help="skip confirmation for --hard")
|
|
240
|
+
|
|
241
|
+
p = sub.add_parser("context", help="enriched repo snapshot for LLMs", parents=[parent])
|
|
242
|
+
|
|
243
|
+
p = sub.add_parser("health", help="repo health score (0-100)", parents=[parent])
|
|
244
|
+
|
|
245
|
+
p = sub.add_parser(
|
|
246
|
+
"stash",
|
|
247
|
+
help="manage stashes (list/show/pop/drop/clear)",
|
|
248
|
+
parents=[parent],
|
|
249
|
+
)
|
|
250
|
+
p.add_argument(
|
|
251
|
+
"action",
|
|
252
|
+
nargs="?",
|
|
253
|
+
default="list",
|
|
254
|
+
choices=["list", "show", "pop", "drop", "clear", "clean"],
|
|
255
|
+
)
|
|
256
|
+
p.add_argument("--index", type=int, default=0, help="stash index (default: 0)")
|
|
257
|
+
p.add_argument("--dry-run", action="store_true", help="dry run (clear only)")
|
|
258
|
+
p.add_argument("--yes", "-y", action="store_true", help="skip confirmation")
|
|
259
|
+
p.add_argument("--patch", action="store_true", help="show full patch (show only)")
|
|
260
|
+
|
|
261
|
+
p = sub.add_parser("tag", help="semver-aware tag management", parents=[parent])
|
|
262
|
+
p.add_argument(
|
|
263
|
+
"action", nargs="?", default="list", choices=["list", "latest", "create", "delete"]
|
|
264
|
+
)
|
|
265
|
+
p.add_argument("name", nargs="?", help="tag name (for create/delete)")
|
|
266
|
+
p.add_argument("--bump", choices=["major", "minor", "patch"], help="bump semver part")
|
|
267
|
+
p.add_argument("-m", "--message", type=str, help="annotated tag message")
|
|
268
|
+
p.add_argument("--dry-run", action="store_true", help="show without executing")
|
|
269
|
+
p.add_argument("--yes", "-y", action="store_true", help="skip confirmation")
|
|
270
|
+
|
|
271
|
+
p = sub.add_parser("merge", help="merge/rebase with pre-flight checks", parents=[parent])
|
|
272
|
+
p.add_argument("branch", help="branch to merge/rebase from")
|
|
273
|
+
p.add_argument("--rebase", action="store_true", help="rebase instead of merge")
|
|
274
|
+
p.add_argument("--no-ff", action="store_true", help="force no-fast-forward")
|
|
275
|
+
p.add_argument("--dry-run", action="store_true", help="show checks without merging")
|
|
276
|
+
p.add_argument("--yes", "-y", action="store_true", help="skip confirmation")
|
|
277
|
+
|
|
278
|
+
p = sub.add_parser(
|
|
279
|
+
"conflicts", help="conflict detection and resolution helper", parents=[parent]
|
|
280
|
+
)
|
|
281
|
+
p.add_argument("--ours", action="store_true", help="resolve all conflicts using ours")
|
|
282
|
+
p.add_argument("--theirs", action="store_true", help="resolve all conflicts using theirs")
|
|
283
|
+
|
|
284
|
+
p = sub.add_parser(
|
|
285
|
+
"suggest",
|
|
286
|
+
help="suggest commit message from staged diff",
|
|
287
|
+
aliases=["commit-suggest"],
|
|
288
|
+
parents=[parent],
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
p = sub.add_parser(
|
|
292
|
+
"pick", help="cherry-pick or revert commits", aliases=["cherry-pick"], parents=[parent]
|
|
293
|
+
)
|
|
294
|
+
p.add_argument("refs", nargs="*", help="commit refs to pick/revert")
|
|
295
|
+
p.add_argument("--revert", action="store_true", help="revert instead of cherry-pick")
|
|
296
|
+
p.add_argument(
|
|
297
|
+
"--continue", dest="continue_", action="store_true", help="continue after conflict"
|
|
298
|
+
)
|
|
299
|
+
p.add_argument("--abort", action="store_true", help="abort in-progress pick/revert")
|
|
300
|
+
p.add_argument("--dry-run", action="store_true", help="show without executing")
|
|
301
|
+
|
|
302
|
+
p = sub.add_parser(
|
|
303
|
+
"update", help="update gitwise (git pull in install directory)", parents=[parent]
|
|
304
|
+
)
|
|
305
|
+
p.add_argument("--dry-run", action="store_true")
|
|
306
|
+
|
|
307
|
+
p = sub.add_parser("status", help="enhanced git status for humans and AI", parents=[parent])
|
|
308
|
+
|
|
309
|
+
p = sub.add_parser(
|
|
310
|
+
"commands",
|
|
311
|
+
help="list available subcommands and metadata",
|
|
312
|
+
parents=[parent],
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
p = sub.add_parser(
|
|
316
|
+
"schema",
|
|
317
|
+
help="print JSON schema for a command",
|
|
318
|
+
parents=[parent],
|
|
319
|
+
)
|
|
320
|
+
p.add_argument("name", help="command name to inspect")
|
|
321
|
+
p.add_argument(
|
|
322
|
+
"--version",
|
|
323
|
+
default="v1",
|
|
324
|
+
help="schema catalog version (default: v1)",
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
p = sub.add_parser(
|
|
328
|
+
"completions",
|
|
329
|
+
help="print shell completion script (bash/zsh/fish)",
|
|
330
|
+
parents=[parent],
|
|
331
|
+
)
|
|
332
|
+
p.add_argument(
|
|
333
|
+
"shell",
|
|
334
|
+
choices=["bash", "zsh", "fish"],
|
|
335
|
+
default="bash",
|
|
336
|
+
nargs="?",
|
|
337
|
+
help="target shell for completion script",
|
|
338
|
+
)
|
|
339
|
+
p.add_argument(
|
|
340
|
+
"--prog",
|
|
341
|
+
default="gitwise",
|
|
342
|
+
help="command name used inside completion script (default: gitwise)",
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
return parser
|