gitwise-cli 0.26.1__tar.gz → 0.28.0__tar.gz
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_cli-0.26.1 → gitwise_cli-0.28.0}/CHANGELOG.md +28 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/PKG-INFO +12 -4
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/README.md +11 -3
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/__init__.py +1 -1
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/__main__.py +33 -1
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/_cli_setup_agents.py +31 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/_i18n_data.json +92 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/branches.py +46 -27
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/clean.py +3 -1
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/conflicts.py +12 -2
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/context.py +7 -6
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/diff.py +15 -3
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/doctor.py +10 -8
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/git.py +1 -1
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/health.py +3 -2
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/log.py +4 -3
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/output.py +10 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/pr.py +10 -8
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/show.py +5 -3
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/snapshot.py +3 -2
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/stash.py +5 -2
- gitwise_cli-0.28.0/gitwise/status.py +123 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/suggest.py +4 -4
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/summarize.py +16 -14
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/tag.py +11 -9
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/update.py +14 -2
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/utils/git_output.py +24 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/worktree.py +8 -4
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/install.sh +6 -2
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/pyproject.toml +2 -1
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/conftest.py +6 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_adapters.py +13 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_audit.py +5 -1
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_i18n.py +28 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_log.py +16 -2
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_snapshot.py +3 -1
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_tag.py +20 -0
- gitwise_cli-0.26.1/gitwise/status.py +0 -93
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/.gitignore +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/LICENSE +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/bin/gitwise +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/_cli_completions.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/_cli_dispatch.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/_cli_introspection.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/_cli_parser.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/_paths.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/_runtime_config.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/audit.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/commit.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/design.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/i18n.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/merge.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/optimize.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/pick.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/py.typed +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/schema.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/__init__.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/adapters/__init__.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/adapters/aider.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/adapters/base.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/adapters/codex.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/adapters/continue_adapter.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/adapters/cursor.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/adapters/opencode.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/adapters/pi.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/exec.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/format.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/plan.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/plan_gitfiles.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/plan_skills.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/providers/__init__.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/providers/aider.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/providers/base.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/providers/claude.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/providers/codex.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/providers/continue_adapter.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/providers/cursor.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/providers/opencode.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/providers/pi.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/state.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/setup_agents/types.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/sync.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/undo.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/utils/__init__.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/utils/json_envelope.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/gitwise/utils/parsing.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/agents/skills/git-audit/SKILL.md +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/agents/skills/git-clean/SKILL.md +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/agents/skills/git-optimize/SKILL.md +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/aider/CONVENTIONS.md.template +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/aider/aider.conf.yml.template +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/claude/CLAUDE.md.template +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/claude/rules/gitwise.md +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/claude/settings.json.template +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/claude/skills/git-audit/SKILL.md +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/claude/skills/git-clean/SKILL.md +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/claude/skills/git-optimize/SKILL.md +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/codex/agents/gitwise.toml.template +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/continue/rules/gitwise.md.template +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/cursor/rules/gitwise.mdc.template +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/git-config-modern.txt +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/hooks/commit-msg +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/hooks/pre-commit +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/opencode/agents/gitwise.md.template +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/pi/skills/gitwise.md.template +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/audit.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/branches.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/clean.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/commands.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/commit.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/completions.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/conflicts.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/context.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/diff.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/doctor.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/health.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/log.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/merge.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/optimize.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/pick.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/pr.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/schema.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/setup-agents.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/setup.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/show.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/snapshot.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/stash.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/status.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/suggest.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/summarize.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/sync.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/tag.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/undo.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/update.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/share/schemas/v1/input/worktree.json +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/__init__.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_branches.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_clean.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_commit.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_conflicts.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_context.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_design.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_diff.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_doctor.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_edge_cases.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_git.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_git_output_utils.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_health.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_json_envelope_utils.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_main.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_md_links_script.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_merge.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_optimize.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_output.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_parsing_utils.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_pick.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_pr.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_roadmap_baseline_script.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_sa_plan.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_sa_unit.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_schema_catalog.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_setup.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_setup_agents.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_show.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_stash.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_status.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_suggest.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_summarize.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_sync.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_sync_changelog_es_script.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_undo.py +0 -0
- {gitwise_cli-0.26.1 → gitwise_cli-0.28.0}/tests/test_worktree.py +0 -0
|
@@ -3,6 +3,34 @@
|
|
|
3
3
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
4
4
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
5
|
|
|
6
|
+
## v0.28.0 (2026-06-19)
|
|
7
|
+
|
|
8
|
+
### BREAKING CHANGE
|
|
9
|
+
|
|
10
|
+
- three JSON contract changes for agents. (1) branches --json: current/in_worktree are now bool (were strings); ahead/behind are now int|null (were strings); upstream/tracking are now string|null. (2) log --json: date field changes from --date=iso to --date=iso-strict (e.g. "2026-06-18T14:30:00+00:00"). (3) tag list --json: date field changes from %(creatordate:iso) to %(creatordate:iso-strict), same shift as log. Agents and scripts parsing these fields must update their deserializers. With major_version_zero=true this rotates the minor (0.27.x -> 0.28.0); the auto-release workflow will bump pyproject.toml and regenerate CHANGELOG.md on push to main, surfacing all three breaks under the Breaking Changes section.
|
|
11
|
+
- `branches --json` changes types in 5 fields. `current`
|
|
12
|
+
and `in_worktree` are now bool (were "true"/"false" strings); `ahead` and
|
|
13
|
+
`behind` are now int|null (were strings); `upstream` and `tracking` are
|
|
14
|
+
now string|null (were always strings). Agents and scripts parsing these
|
|
15
|
+
fields must update their deserializers. With major_version_zero=true this
|
|
16
|
+
rotates the minor (0.27.x -> 0.28.0); the auto-release workflow will bump
|
|
17
|
+
pyproject.toml and regenerate CHANGELOG.md on push to main.
|
|
18
|
+
|
|
19
|
+
### Feat
|
|
20
|
+
|
|
21
|
+
- unified loading feedback + structured JSON (BREAKING branches/log/tag) (#64)
|
|
22
|
+
- unified loading feedback + structured JSON for status/branches
|
|
23
|
+
|
|
24
|
+
### Fix
|
|
25
|
+
|
|
26
|
+
- address CI failures and CodeRabbit review findings
|
|
27
|
+
|
|
28
|
+
## v0.27.0 (2026-06-19)
|
|
29
|
+
|
|
30
|
+
### Feat
|
|
31
|
+
|
|
32
|
+
- **install**: Windows installer + extend audit to detect unpinned uses: (#63)
|
|
33
|
+
|
|
6
34
|
## v0.26.1 (2026-06-18)
|
|
7
35
|
|
|
8
36
|
### Fix
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitwise-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.28.0
|
|
4
4
|
Summary: Python CLI for optimizing git workflows and Claude Code integration
|
|
5
5
|
Project-URL: Homepage, https://github.com/drzioner/gitwise
|
|
6
6
|
Project-URL: Repository, https://github.com/drzioner/gitwise
|
|
@@ -89,11 +89,19 @@ uv sync
|
|
|
89
89
|
uv run python -m gitwise doctor
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
**Windows** (PowerShell 5.1+, auto-installs `uv` if missing):
|
|
93
|
+
|
|
94
|
+
```powershell
|
|
95
|
+
irm https://raw.githubusercontent.com/drzioner/gitwise/main/install.ps1 | iex
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
For a version-pinned install (e.g. for reproducible setups), see `Get-Help .\install.ps1 -Detailed` after download.
|
|
99
|
+
|
|
92
100
|
Update an existing installation:
|
|
93
101
|
|
|
94
102
|
```bash
|
|
95
|
-
brew upgrade gitwise # if installed via Homebrew
|
|
96
|
-
uv tool upgrade gitwise-cli # if installed via uv
|
|
103
|
+
brew upgrade gitwise # if installed via Homebrew (macOS/Linux)
|
|
104
|
+
uv tool upgrade gitwise-cli # if installed via uv (any OS)
|
|
97
105
|
# or re-run the curl | bash installer, which always pulls latest
|
|
98
106
|
```
|
|
99
107
|
|
|
@@ -101,7 +109,7 @@ Uninstall:
|
|
|
101
109
|
|
|
102
110
|
```bash
|
|
103
111
|
brew uninstall gitwise # if installed via Homebrew
|
|
104
|
-
uv tool uninstall gitwise-cli # if installed via uv
|
|
112
|
+
uv tool uninstall gitwise-cli # if installed via uv (any OS)
|
|
105
113
|
```
|
|
106
114
|
|
|
107
115
|
## Quick Start
|
|
@@ -56,11 +56,19 @@ uv sync
|
|
|
56
56
|
uv run python -m gitwise doctor
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
**Windows** (PowerShell 5.1+, auto-installs `uv` if missing):
|
|
60
|
+
|
|
61
|
+
```powershell
|
|
62
|
+
irm https://raw.githubusercontent.com/drzioner/gitwise/main/install.ps1 | iex
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For a version-pinned install (e.g. for reproducible setups), see `Get-Help .\install.ps1 -Detailed` after download.
|
|
66
|
+
|
|
59
67
|
Update an existing installation:
|
|
60
68
|
|
|
61
69
|
```bash
|
|
62
|
-
brew upgrade gitwise # if installed via Homebrew
|
|
63
|
-
uv tool upgrade gitwise-cli # if installed via uv
|
|
70
|
+
brew upgrade gitwise # if installed via Homebrew (macOS/Linux)
|
|
71
|
+
uv tool upgrade gitwise-cli # if installed via uv (any OS)
|
|
64
72
|
# or re-run the curl | bash installer, which always pulls latest
|
|
65
73
|
```
|
|
66
74
|
|
|
@@ -68,7 +76,7 @@ Uninstall:
|
|
|
68
76
|
|
|
69
77
|
```bash
|
|
70
78
|
brew uninstall gitwise # if installed via Homebrew
|
|
71
|
-
uv tool uninstall gitwise-cli # if installed via uv
|
|
79
|
+
uv tool uninstall gitwise-cli # if installed via uv (any OS)
|
|
72
80
|
```
|
|
73
81
|
|
|
74
82
|
## Quick Start
|
|
@@ -7,7 +7,7 @@ from ._cli_dispatch import DISPATCH
|
|
|
7
7
|
from ._cli_introspection import extract_command_token, help_payload
|
|
8
8
|
from ._cli_parser import build_parser
|
|
9
9
|
from .i18n import t
|
|
10
|
-
from .output import print_dim, print_json, set_json_pretty
|
|
10
|
+
from .output import print_dim, print_json, set_json_mode, set_json_pretty
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _is_log_json_enabled() -> bool:
|
|
@@ -32,12 +32,36 @@ def _install_rich_traceback() -> None:
|
|
|
32
32
|
return
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
def _ensure_utf8_stdio() -> None:
|
|
36
|
+
"""Force stdout/stderr to UTF-8.
|
|
37
|
+
|
|
38
|
+
Windows defaults to a system codepage (often cp1252) for the Python
|
|
39
|
+
embedded in console apps. That codepage cannot encode characters like
|
|
40
|
+
U+2713 (✓) used throughout gitwise's status output, causing
|
|
41
|
+
UnicodeEncodeError when the user is not running through a wrapper that
|
|
42
|
+
sets PYTHONIOENCODING. macOS/Linux are already UTF-8 by default, so
|
|
43
|
+
this reconfigure is a no-op there.
|
|
44
|
+
"""
|
|
45
|
+
for stream_name in ("stdout", "stderr"):
|
|
46
|
+
stream = getattr(sys, stream_name, None)
|
|
47
|
+
# TextIOWrapper.reconfigure exists on Python 3.7+; guard just in case
|
|
48
|
+
# the stream has been replaced with something non-standard.
|
|
49
|
+
reconfigure = getattr(stream, "reconfigure", None)
|
|
50
|
+
if callable(reconfigure):
|
|
51
|
+
try:
|
|
52
|
+
reconfigure(encoding="utf-8", errors="replace")
|
|
53
|
+
except (TypeError, ValueError):
|
|
54
|
+
# Stream does not accept these kwargs or is closed; ignore.
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
35
58
|
def main() -> int:
|
|
36
59
|
import os
|
|
37
60
|
|
|
38
61
|
from ._runtime_config import reset_runtime_config
|
|
39
62
|
from .i18n import set_locale
|
|
40
63
|
|
|
64
|
+
_ensure_utf8_stdio()
|
|
41
65
|
_install_rich_traceback()
|
|
42
66
|
|
|
43
67
|
parser = build_parser()
|
|
@@ -76,6 +100,7 @@ def main() -> int:
|
|
|
76
100
|
reset_runtime_config()
|
|
77
101
|
|
|
78
102
|
set_json_pretty(args.json_pretty)
|
|
103
|
+
set_json_mode(args.json)
|
|
79
104
|
|
|
80
105
|
if args.lang:
|
|
81
106
|
set_locale(args.lang)
|
|
@@ -93,6 +118,13 @@ def main() -> int:
|
|
|
93
118
|
except Exception:
|
|
94
119
|
if _should_show_rich_traceback():
|
|
95
120
|
raise
|
|
121
|
+
# No Rich traceback available (CI / non-tty / LOG_JSON mode).
|
|
122
|
+
# Still emit the raw traceback to stderr so CI logs are
|
|
123
|
+
# diagnostic. Otherwise the user only sees "unexpected error"
|
|
124
|
+
# and has no way to identify the root cause.
|
|
125
|
+
import traceback as _traceback
|
|
126
|
+
|
|
127
|
+
_traceback.print_exc()
|
|
96
128
|
from .output import error as _error
|
|
97
129
|
|
|
98
130
|
_error(t("unexpected_error"))
|
|
@@ -385,6 +385,37 @@ def run_setup_agents(
|
|
|
385
385
|
adapters_legacy_used: bool = False,
|
|
386
386
|
) -> int:
|
|
387
387
|
"""Dispatcher: global mode (default) or per-repo mode (--local)."""
|
|
388
|
+
import platform
|
|
389
|
+
|
|
390
|
+
if platform.system() == "Windows":
|
|
391
|
+
if not local:
|
|
392
|
+
# Global mode creates symlinks in ~/.claude/ (skills, CLAUDE.md
|
|
393
|
+
# pointer, etc.). Windows does not support POSIX-style symlinks
|
|
394
|
+
# without Developer Mode, and even with it the semantics around
|
|
395
|
+
# relative targets differ enough that the existing setup_agents
|
|
396
|
+
# code produces broken links. Fail fast with a clear workaround
|
|
397
|
+
# instead of letting the user hit a confusing traceback.
|
|
398
|
+
if as_json:
|
|
399
|
+
print_json(
|
|
400
|
+
{
|
|
401
|
+
"ok": False,
|
|
402
|
+
"error": "windows_global_unsupported",
|
|
403
|
+
"message": t("setup_agents_windows_global_unsupported"),
|
|
404
|
+
"workaround": "gitwise setup-agents --local --no-symlinks",
|
|
405
|
+
}
|
|
406
|
+
)
|
|
407
|
+
else:
|
|
408
|
+
error(t("setup_agents_windows_global_unsupported"))
|
|
409
|
+
info(t("setup_agents_windows_workaround_local"))
|
|
410
|
+
return 1
|
|
411
|
+
if not no_symlinks:
|
|
412
|
+
# Local mode auto-enables --no-symlinks on Windows. Same root
|
|
413
|
+
# cause: os.symlink() either errors out (no Developer Mode) or
|
|
414
|
+
# produces links that do not behave like POSIX symlinks for our
|
|
415
|
+
# sandbox check (os.path.realpath over a not-yet-existing target).
|
|
416
|
+
warn(t("setup_agents_windows_auto_no_symlinks"))
|
|
417
|
+
no_symlinks = True
|
|
418
|
+
|
|
388
419
|
if local:
|
|
389
420
|
return _run_setup_local(
|
|
390
421
|
target,
|
|
@@ -519,6 +519,18 @@
|
|
|
519
519
|
"es": "fsmonitor integrado no está soportado en Linux (solo macOS y Windows)",
|
|
520
520
|
"en": "built-in fsmonitor not supported on Linux (macOS and Windows only)"
|
|
521
521
|
},
|
|
522
|
+
"setup_agents_windows_global_unsupported": {
|
|
523
|
+
"es": "setup-agents en modo global no está soportado en Windows (requiere symlinks POSIX en ~/.claude/).",
|
|
524
|
+
"en": "setup-agents global mode is not supported on Windows (requires POSIX symlinks in ~/.claude/)."
|
|
525
|
+
},
|
|
526
|
+
"setup_agents_windows_workaround_local": {
|
|
527
|
+
"es": "Workaround: usá 'gitwise setup-agents --local --no-symlinks' dentro del repo donde quieras configurar los agentes.",
|
|
528
|
+
"en": "Workaround: use 'gitwise setup-agents --local --no-symlinks' inside the repo where you want to set up agents."
|
|
529
|
+
},
|
|
530
|
+
"setup_agents_windows_auto_no_symlinks": {
|
|
531
|
+
"es": "Windows detectado: activando --no-symlinks automáticamente (los symlinks POSIX no son confiables en Windows).",
|
|
532
|
+
"en": "Windows detected: auto-enabling --no-symlinks (POSIX symlinks are not reliable on Windows)."
|
|
533
|
+
},
|
|
522
534
|
"gc_already_running": {
|
|
523
535
|
"es": "git gc/maintenance ya está en ejecución — esperá a que termine",
|
|
524
536
|
"en": "git gc/maintenance already running — wait for it to finish"
|
|
@@ -1559,6 +1571,78 @@
|
|
|
1559
1571
|
"es": "Auditando: ejecutando git-sizer...",
|
|
1560
1572
|
"en": "Auditing: running git-sizer..."
|
|
1561
1573
|
},
|
|
1574
|
+
"status_worktree_add": {
|
|
1575
|
+
"es": "Creando worktree para {branch}...",
|
|
1576
|
+
"en": "Creating worktree for {branch}..."
|
|
1577
|
+
},
|
|
1578
|
+
"status_health_scan": {
|
|
1579
|
+
"es": "Calculando salud del repositorio...",
|
|
1580
|
+
"en": "Computing repository health..."
|
|
1581
|
+
},
|
|
1582
|
+
"status_context_scan": {
|
|
1583
|
+
"es": "Recolectando contexto del repositorio...",
|
|
1584
|
+
"en": "Gathering repository context..."
|
|
1585
|
+
},
|
|
1586
|
+
"status_snapshot_gen": {
|
|
1587
|
+
"es": "Generando snapshot...",
|
|
1588
|
+
"en": "Generating snapshot..."
|
|
1589
|
+
},
|
|
1590
|
+
"status_reading_status": {
|
|
1591
|
+
"es": "Leyendo estado del repositorio...",
|
|
1592
|
+
"en": "Reading repository status..."
|
|
1593
|
+
},
|
|
1594
|
+
"status_summarizing": {
|
|
1595
|
+
"es": "Resumiendo repositorio...",
|
|
1596
|
+
"en": "Summarizing repository..."
|
|
1597
|
+
},
|
|
1598
|
+
"status_reading_diff": {
|
|
1599
|
+
"es": "Calculando cambios...",
|
|
1600
|
+
"en": "Computing changes..."
|
|
1601
|
+
},
|
|
1602
|
+
"status_reading_log": {
|
|
1603
|
+
"es": "Leyendo historial de commits...",
|
|
1604
|
+
"en": "Reading commit history..."
|
|
1605
|
+
},
|
|
1606
|
+
"status_loading_commit": {
|
|
1607
|
+
"es": "Cargando commit...",
|
|
1608
|
+
"en": "Loading commit..."
|
|
1609
|
+
},
|
|
1610
|
+
"status_analyzing_branches": {
|
|
1611
|
+
"es": "Analizando ramas...",
|
|
1612
|
+
"en": "Analyzing branches..."
|
|
1613
|
+
},
|
|
1614
|
+
"status_analyzing_staged": {
|
|
1615
|
+
"es": "Analizando cambios preparados...",
|
|
1616
|
+
"en": "Analyzing staged changes..."
|
|
1617
|
+
},
|
|
1618
|
+
"status_detecting_conflicts": {
|
|
1619
|
+
"es": "Detectando conflictos...",
|
|
1620
|
+
"en": "Detecting conflicts..."
|
|
1621
|
+
},
|
|
1622
|
+
"status_reading_stashes": {
|
|
1623
|
+
"es": "Leyendo stashes...",
|
|
1624
|
+
"en": "Reading stashes..."
|
|
1625
|
+
},
|
|
1626
|
+
"status_reading_tags": {
|
|
1627
|
+
"es": "Leyendo tags...",
|
|
1628
|
+
"en": "Reading tags..."
|
|
1629
|
+
},
|
|
1630
|
+
"status_querying_github": {
|
|
1631
|
+
"es": "Consultando GitHub...",
|
|
1632
|
+
"en": "Querying GitHub..."
|
|
1633
|
+
},
|
|
1634
|
+
"status_checking_env": {
|
|
1635
|
+
"es": "Verificando entorno...",
|
|
1636
|
+
"en": "Checking environment..."
|
|
1637
|
+
},
|
|
1638
|
+
"status_scanning_stale": {
|
|
1639
|
+
"es": "Buscando ramas y refs obsoletos...",
|
|
1640
|
+
"en": "Scanning stale branches and refs..."
|
|
1641
|
+
},
|
|
1642
|
+
"status_updating": {
|
|
1643
|
+
"es": "Actualizando gitwise...",
|
|
1644
|
+
"en": "Updating gitwise..."
|
|
1645
|
+
},
|
|
1562
1646
|
"step_commit_graph": {
|
|
1563
1647
|
"es": "git commit-graph write --reachable --changed-paths",
|
|
1564
1648
|
"en": "git commit-graph write --reachable --changed-paths"
|
|
@@ -1915,6 +1999,14 @@
|
|
|
1915
1999
|
"es": "update requiere una instalación vía git clone (no se encontró .git/)",
|
|
1916
2000
|
"en": "update requires a git clone installation (.git/ not found)"
|
|
1917
2001
|
},
|
|
2002
|
+
"update_no_upstream": {
|
|
2003
|
+
"es": "la rama actual '{branch}' no tiene rama de seguimiento; update solo puede actualizar una rama con upstream",
|
|
2004
|
+
"en": "current branch '{branch}' has no upstream; update can only fast-forward a tracking branch"
|
|
2005
|
+
},
|
|
2006
|
+
"update_no_upstream_hint": {
|
|
2007
|
+
"es": "cambia a tu rama principal (p.ej. 'git switch main') o configura el upstream con 'git branch --set-upstream-to=origin/{branch}'",
|
|
2008
|
+
"en": "switch to your main branch (e.g. 'git switch main') or set an upstream with 'git branch --set-upstream-to=origin/{branch}'"
|
|
2009
|
+
},
|
|
1918
2010
|
"invalid_grep_pattern": {
|
|
1919
2011
|
"es": "patrón de búsqueda inválido o peligroso: {pattern}",
|
|
1920
2012
|
"en": "invalid or unsafe grep pattern: {pattern}"
|
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
"""gitwise branches — intelligence dashboard with ahead/behind, merged, stale, worktree info."""
|
|
2
2
|
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TypedDict
|
|
5
|
+
|
|
3
6
|
from .git import require_root, stale_branches, worktree_branches
|
|
4
7
|
from .git import run as git_run
|
|
5
8
|
from .i18n import t
|
|
6
|
-
from .output import error, info, print_dim, print_json, print_table
|
|
9
|
+
from .output import error, info, print_dim, print_json, print_table, status
|
|
7
10
|
from .utils.json_envelope import ok_envelope
|
|
8
11
|
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
class BranchEntry(TypedDict):
|
|
14
|
+
name: str
|
|
15
|
+
current: bool
|
|
16
|
+
sha: str
|
|
17
|
+
subject: str
|
|
18
|
+
age: str
|
|
19
|
+
upstream: str | None
|
|
20
|
+
ahead: int | None
|
|
21
|
+
behind: int | None
|
|
22
|
+
tracking: str | None
|
|
23
|
+
in_worktree: bool
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _parse_track_count(tracking: str, marker: str) -> int | None:
|
|
27
|
+
if marker not in tracking:
|
|
28
|
+
return None
|
|
29
|
+
raw = tracking.split(marker)[1].split(",")[0].strip().rstrip("]")
|
|
30
|
+
return int(raw) if raw.isdigit() else None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _parse_branches(raw: str, wt_branches: set[str]) -> list[BranchEntry]:
|
|
34
|
+
branches: list[BranchEntry] = []
|
|
12
35
|
for line in raw.splitlines():
|
|
13
36
|
if not line.strip():
|
|
14
37
|
continue
|
|
@@ -23,24 +46,18 @@ def _parse_branches(raw: str, wt_branches: set[str]) -> list[dict[str, str]]:
|
|
|
23
46
|
tracking = parts[5] if len(parts) > 5 else ""
|
|
24
47
|
upstream = parts[6] if len(parts) > 6 else ""
|
|
25
48
|
|
|
26
|
-
ahead = behind = ""
|
|
27
|
-
if "[ahead" in tracking:
|
|
28
|
-
ahead = tracking.split("ahead")[1].split(",")[0].strip().rstrip("]")
|
|
29
|
-
if "behind" in tracking:
|
|
30
|
-
behind = tracking.split("behind")[1].strip().rstrip("]")
|
|
31
|
-
|
|
32
49
|
branches.append(
|
|
33
50
|
{
|
|
34
51
|
"name": name,
|
|
35
|
-
"current":
|
|
52
|
+
"current": is_current,
|
|
36
53
|
"sha": sha,
|
|
37
54
|
"subject": subject,
|
|
38
55
|
"age": age,
|
|
39
|
-
"upstream": upstream,
|
|
40
|
-
"ahead": ahead,
|
|
41
|
-
"behind": behind,
|
|
42
|
-
"tracking": tracking,
|
|
43
|
-
"in_worktree":
|
|
56
|
+
"upstream": upstream or None,
|
|
57
|
+
"ahead": _parse_track_count(tracking, "ahead"),
|
|
58
|
+
"behind": _parse_track_count(tracking, "behind"),
|
|
59
|
+
"tracking": tracking or None,
|
|
60
|
+
"in_worktree": name in wt_branches,
|
|
44
61
|
}
|
|
45
62
|
)
|
|
46
63
|
return branches
|
|
@@ -72,7 +89,7 @@ def _print_stale_branches(*, names: list[str], as_json: bool) -> int:
|
|
|
72
89
|
return 0
|
|
73
90
|
|
|
74
91
|
|
|
75
|
-
def _fetch_branch_rows(*, root, remote: bool, sort: str) -> list[
|
|
92
|
+
def _fetch_branch_rows(*, root: Path, remote: bool, sort: str) -> list[BranchEntry] | None:
|
|
76
93
|
wt_branches = worktree_branches(cwd=root)
|
|
77
94
|
ref_pattern = "refs/remotes/" if remote else "refs/heads/"
|
|
78
95
|
fmt = "%(HEAD)\t%(refname:short)\t%(objectname:short)\t%(subject)\t%(committerdate:relative)\t%(upstream:track)\t%(upstream:short)"
|
|
@@ -94,7 +111,7 @@ def _fetch_branch_rows(*, root, remote: bool, sort: str) -> list[dict[str, str]]
|
|
|
94
111
|
|
|
95
112
|
|
|
96
113
|
def _build_branch_rows(
|
|
97
|
-
branches: list[
|
|
114
|
+
branches: list[BranchEntry],
|
|
98
115
|
) -> tuple[list[list[str]], set[int], int | None]:
|
|
99
116
|
rows: list[list[str]] = []
|
|
100
117
|
highlight_rows: set[int] = set()
|
|
@@ -102,28 +119,28 @@ def _build_branch_rows(
|
|
|
102
119
|
for idx, branch_item in enumerate(branches):
|
|
103
120
|
sha = branch_item["sha"][:8]
|
|
104
121
|
subject = branch_item["subject"][:40]
|
|
105
|
-
age = branch_item
|
|
122
|
+
age = branch_item["age"]
|
|
106
123
|
flags: list[str] = []
|
|
107
|
-
if branch_item
|
|
124
|
+
if branch_item["ahead"]:
|
|
108
125
|
flags.append(f"↑{branch_item['ahead']}")
|
|
109
|
-
if branch_item
|
|
126
|
+
if branch_item["behind"]:
|
|
110
127
|
flags.append(f"↓{branch_item['behind']}")
|
|
111
|
-
if branch_item
|
|
128
|
+
if branch_item["in_worktree"]:
|
|
112
129
|
flags.append("wt")
|
|
113
|
-
if branch_item
|
|
130
|
+
if branch_item["upstream"]:
|
|
114
131
|
flags.append(f"→{branch_item['upstream']}")
|
|
115
132
|
status = " ".join(flags) if flags else ""
|
|
116
133
|
name_display = (
|
|
117
|
-
f"* {branch_item['name']}" if branch_item["current"]
|
|
134
|
+
f"* {branch_item['name']}" if branch_item["current"] else branch_item["name"]
|
|
118
135
|
)
|
|
119
136
|
rows.append([name_display, sha, subject, age, status])
|
|
120
|
-
if branch_item["current"]
|
|
137
|
+
if branch_item["current"]:
|
|
121
138
|
current_idx = idx
|
|
122
139
|
highlight_rows.add(idx)
|
|
123
140
|
return rows, highlight_rows, current_idx
|
|
124
141
|
|
|
125
142
|
|
|
126
|
-
def _print_branch_table(branches: list[
|
|
143
|
+
def _print_branch_table(branches: list[BranchEntry]) -> None:
|
|
127
144
|
columns = [
|
|
128
145
|
(t("col_branch"), "name"),
|
|
129
146
|
(t("col_sha"), "sha"),
|
|
@@ -163,13 +180,15 @@ def run_branches(
|
|
|
163
180
|
return 1
|
|
164
181
|
|
|
165
182
|
if stale:
|
|
166
|
-
|
|
183
|
+
with status(t("status_analyzing_branches")):
|
|
184
|
+
names = stale_branches(cwd=root)
|
|
167
185
|
return _print_stale_branches(names=names, as_json=as_json)
|
|
168
186
|
|
|
169
187
|
if not _validate_sort_field(sort):
|
|
170
188
|
return 1
|
|
171
189
|
|
|
172
|
-
|
|
190
|
+
with status(t("status_analyzing_branches")):
|
|
191
|
+
branches = _fetch_branch_rows(root=root, remote=remote, sort=sort)
|
|
173
192
|
if branches is None:
|
|
174
193
|
return 1
|
|
175
194
|
if not branches:
|
|
@@ -22,6 +22,7 @@ from .output import (
|
|
|
22
22
|
print_header,
|
|
23
23
|
print_json,
|
|
24
24
|
print_success,
|
|
25
|
+
status,
|
|
25
26
|
warn,
|
|
26
27
|
)
|
|
27
28
|
from .utils.json_envelope import error_envelope, ok_envelope
|
|
@@ -78,7 +79,8 @@ def run_clean(
|
|
|
78
79
|
return 1
|
|
79
80
|
cwd = root
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
with status(t("status_scanning_stale")):
|
|
83
|
+
deletable, skipped = _categorize(cwd)
|
|
82
84
|
|
|
83
85
|
if dry_run:
|
|
84
86
|
if as_json:
|
|
@@ -5,7 +5,16 @@ from pathlib import Path
|
|
|
5
5
|
from .git import require_root
|
|
6
6
|
from .git import run as git_run
|
|
7
7
|
from .i18n import t
|
|
8
|
-
from .output import
|
|
8
|
+
from .output import (
|
|
9
|
+
error,
|
|
10
|
+
ok,
|
|
11
|
+
print_accent,
|
|
12
|
+
print_blank,
|
|
13
|
+
print_dim,
|
|
14
|
+
print_header,
|
|
15
|
+
print_json,
|
|
16
|
+
status,
|
|
17
|
+
)
|
|
9
18
|
from .utils.json_envelope import error_envelope, ok_envelope
|
|
10
19
|
from .utils.parsing import stripped_non_empty_lines, to_int
|
|
11
20
|
|
|
@@ -90,7 +99,8 @@ def run_conflicts(
|
|
|
90
99
|
if root is None:
|
|
91
100
|
return 1
|
|
92
101
|
|
|
93
|
-
|
|
102
|
+
with status(t("status_detecting_conflicts")):
|
|
103
|
+
conflicts = _find_conflict_files(root)
|
|
94
104
|
|
|
95
105
|
if not conflicts:
|
|
96
106
|
return _report_no_conflicts(as_json=as_json)
|
|
@@ -5,7 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from .git import require_root
|
|
6
6
|
from .git import run as git_run
|
|
7
7
|
from .i18n import t
|
|
8
|
-
from .output import info, print_blank, print_bracket, print_dim, print_header, print_json
|
|
8
|
+
from .output import info, print_blank, print_bracket, print_dim, print_header, print_json, status
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def _directory_tree(root: Path, max_depth: int = 3) -> list[str]:
|
|
@@ -108,11 +108,12 @@ def run_context(*, as_json: bool = False) -> int:
|
|
|
108
108
|
if root is None:
|
|
109
109
|
return 1
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
with status(t("status_context_scan")):
|
|
112
|
+
tree = _directory_tree(root)
|
|
113
|
+
contributors = _top_contributors(root)
|
|
114
|
+
file_types = _file_type_breakdown(root)
|
|
115
|
+
todo_fixme = _todo_fixme_counts(root)
|
|
116
|
+
topology = _branch_topology(root)
|
|
116
117
|
|
|
117
118
|
if as_json:
|
|
118
119
|
from .health import compute_health
|
|
@@ -15,8 +15,9 @@ from .output import (
|
|
|
15
15
|
print_file_status,
|
|
16
16
|
print_header,
|
|
17
17
|
print_json,
|
|
18
|
+
status,
|
|
18
19
|
)
|
|
19
|
-
from .utils.git_output import parse_name_status_entries
|
|
20
|
+
from .utils.git_output import parse_name_status_entries, status_label
|
|
20
21
|
from .utils.json_envelope import ok_envelope
|
|
21
22
|
|
|
22
23
|
DiffValue = str | int | bool
|
|
@@ -205,6 +206,9 @@ def _render_stat_output(
|
|
|
205
206
|
numstat_details=numstat_details,
|
|
206
207
|
)
|
|
207
208
|
if as_json:
|
|
209
|
+
for entry in merged_files:
|
|
210
|
+
raw_code = str(entry.get("code", entry.get("status", "")))
|
|
211
|
+
entry["status_label"] = status_label(raw_code)
|
|
208
212
|
print_json(
|
|
209
213
|
ok_envelope(
|
|
210
214
|
files=merged_files,
|
|
@@ -251,7 +255,14 @@ def _render_non_stat_output(
|
|
|
251
255
|
for line in lines:
|
|
252
256
|
parts = line.split("\t", 1)
|
|
253
257
|
if len(parts) == 2:
|
|
254
|
-
|
|
258
|
+
code = parts[0].strip()
|
|
259
|
+
files.append(
|
|
260
|
+
{
|
|
261
|
+
"status": code,
|
|
262
|
+
"status_label": status_label(code),
|
|
263
|
+
"path": parts[1].strip(),
|
|
264
|
+
}
|
|
265
|
+
)
|
|
255
266
|
|
|
256
267
|
if as_json:
|
|
257
268
|
print_json(ok_envelope(files=files, count=len(files)))
|
|
@@ -287,7 +298,8 @@ def run_diff(
|
|
|
287
298
|
|
|
288
299
|
use_stat = stat or (not staged and not name_only and not full)
|
|
289
300
|
cmd = _diff_cmd(use_stat=use_stat, staged=staged, name_only=name_only, full=full)
|
|
290
|
-
|
|
301
|
+
with status(t("status_reading_diff")):
|
|
302
|
+
result = git_run(cmd, cwd=cwd, check=False)
|
|
291
303
|
if result.returncode != 0:
|
|
292
304
|
error(t("git_diff_failed", error=result.stderr.strip()))
|
|
293
305
|
return 1
|
|
@@ -15,6 +15,7 @@ from .output import (
|
|
|
15
15
|
print_json,
|
|
16
16
|
print_kv,
|
|
17
17
|
print_status_line,
|
|
18
|
+
status,
|
|
18
19
|
warn,
|
|
19
20
|
)
|
|
20
21
|
|
|
@@ -33,17 +34,18 @@ _TOOL_INFO: dict[str, tuple[str, str]] = {
|
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
def run_doctor(*, as_json: bool = False) -> int:
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
with status(t("status_checking_env")):
|
|
38
|
+
git_ver = git_version()
|
|
39
|
+
git_ok = git_ver >= MIN_GIT
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
python_ver = sys.version_info[:3]
|
|
42
|
+
python_ok = python_ver >= (3, 9)
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
platform_name = platform.system()
|
|
45
|
+
fsmonitor_supported = platform_name in ("Darwin", "Windows")
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
optional = {tool: bool(shutil.which(tool)) for tool in _OPTIONAL_TOOLS}
|
|
48
|
+
gpg = gpg_status()
|
|
47
49
|
|
|
48
50
|
result = {
|
|
49
51
|
"v": 2,
|
|
@@ -61,7 +61,7 @@ def run(
|
|
|
61
61
|
) -> subprocess.CompletedProcess[str]:
|
|
62
62
|
from .output import debug
|
|
63
63
|
|
|
64
|
-
cmd_name = args
|
|
64
|
+
cmd_name = next((arg for arg in args if not arg.startswith("-")), None)
|
|
65
65
|
actual_timeout = timeout if timeout is not None else _get_timeout(cmd_name)
|
|
66
66
|
debug(f"git {' '.join(args)}")
|
|
67
67
|
try:
|
|
@@ -12,7 +12,7 @@ from .git import (
|
|
|
12
12
|
)
|
|
13
13
|
from .git import run as git_run
|
|
14
14
|
from .i18n import t
|
|
15
|
-
from .output import print_header, print_json, print_status_line
|
|
15
|
+
from .output import print_header, print_json, print_status_line, status
|
|
16
16
|
from .utils.json_envelope import ok_envelope
|
|
17
17
|
from .utils.parsing import non_empty_lines, to_int
|
|
18
18
|
|
|
@@ -330,7 +330,8 @@ def run_health(*, as_json: bool = False) -> int:
|
|
|
330
330
|
if root is None:
|
|
331
331
|
return 1
|
|
332
332
|
|
|
333
|
-
|
|
333
|
+
with status(t("status_health_scan")):
|
|
334
|
+
h = compute_health(root)
|
|
334
335
|
|
|
335
336
|
if as_json:
|
|
336
337
|
print_json(ok_envelope(payload=h))
|