aline-ai 0.2.4__py3-none-any.whl → 0.2.5__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.
- {aline_ai-0.2.4.dist-info → aline_ai-0.2.5.dist-info}/METADATA +1 -1
- {aline_ai-0.2.4.dist-info → aline_ai-0.2.5.dist-info}/RECORD +11 -10
- realign/__init__.py +1 -1
- realign/cli.py +24 -1
- realign/commands/__init__.py +2 -2
- realign/commands/push.py +290 -0
- realign/mcp_server.py +133 -0
- {aline_ai-0.2.4.dist-info → aline_ai-0.2.5.dist-info}/WHEEL +0 -0
- {aline_ai-0.2.4.dist-info → aline_ai-0.2.5.dist-info}/entry_points.txt +0 -0
- {aline_ai-0.2.4.dist-info → aline_ai-0.2.5.dist-info}/licenses/LICENSE +0 -0
- {aline_ai-0.2.4.dist-info → aline_ai-0.2.5.dist-info}/top_level.txt +0 -0
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
aline_ai-0.2.
|
|
2
|
-
realign/__init__.py,sha256=
|
|
1
|
+
aline_ai-0.2.5.dist-info/licenses/LICENSE,sha256=H8wTqV5IF1oHw_HbBtS1PSDU8G_q81yblEIL_JfV8Vo,1077
|
|
2
|
+
realign/__init__.py,sha256=zx1q8jbbAtSFt2eDYbUqaqss8LohQnvmI4gRY_HWVWY,68
|
|
3
3
|
realign/claude_detector.py,sha256=NLxI0zJWcqNxNha9jAy9AslTMwHKakCc9yPGdkrbiFE,3028
|
|
4
|
-
realign/cli.py,sha256=
|
|
4
|
+
realign/cli.py,sha256=WSdT9zpHLswBeDPjb5M_U2cvhIm7yrgnXPDyb7vD-jg,2798
|
|
5
5
|
realign/codex_detector.py,sha256=RI3JbZgebrhoqpRfTBMfclYCAISN7hZAHVW3bgftJpU,4428
|
|
6
6
|
realign/config.py,sha256=jarinbr0mA6e5DmgY19b_VpMnxk6SOYTwyvB9luq0ww,7207
|
|
7
7
|
realign/file_lock.py,sha256=-9c3tMdMj_ZxmasK5y6hV9Gfo6KDsSO3Q7PXiTBhsu4,3369
|
|
8
8
|
realign/hooks.py,sha256=GzkEbW35-ifwAilYbVxDpNURZ_7XrF68DTqDSQ8v7fE,50670
|
|
9
9
|
realign/logging_config.py,sha256=KvkKktF-bkUu031y9vgUoHpsbnOw7ud25jhpzliNZwA,4929
|
|
10
|
-
realign/mcp_server.py,sha256=
|
|
10
|
+
realign/mcp_server.py,sha256=Q82nuEm6LF17eKUZfHHt6exQjzOWbbBmGXLYwNIGnoo,21002
|
|
11
11
|
realign/mcp_watcher.py,sha256=ffYOXDLuf9T6Kab3CdGNAOY3DBlAbjZrVrSjM5RdYGU,26828
|
|
12
12
|
realign/redactor.py,sha256=FizaGSdW-QTBAQl4h-gtmMpx2mFrfd2a5DoPEPyLfRg,9989
|
|
13
|
-
realign/commands/__init__.py,sha256=
|
|
13
|
+
realign/commands/__init__.py,sha256=caHulsUeguKyy2ZIIa9hVwzGwNHfIbeHwZIC67C8gnI,213
|
|
14
14
|
realign/commands/auto_commit.py,sha256=jgjAYZHqN34NmQkncZg3Vtwsl3MyAlsvucxEBwUj7ko,7450
|
|
15
15
|
realign/commands/commit.py,sha256=mlwrv5nfTRY17WlcAdiJKKGh5uM7dGvT7sMxhdbsfkw,12605
|
|
16
16
|
realign/commands/config.py,sha256=iiu7usqw00djKZja5bx0iDH8DB0vU2maUPMkXLdgXwI,6609
|
|
17
17
|
realign/commands/hide.py,sha256=i_XLsmsB4duLKNIA-eRAUvniS4N6GdQUyfN92CGndEA,34138
|
|
18
18
|
realign/commands/init.py,sha256=vOXSAveWgP8TWp0rgqZO8zNRKZMPiaxWsg4PeDZ9RVs,13586
|
|
19
|
+
realign/commands/push.py,sha256=77GXeKggzzc-100JN1PSgDFcDhSaB4kv2jN24h5YM6I,10249
|
|
19
20
|
realign/commands/review.py,sha256=3TY6F87RGNEbutxvUr_konr24-gCyj5fmD9usd3n4-U,15570
|
|
20
21
|
realign/commands/search.py,sha256=xTWuX0lpjQPX8cen0ewl-BNF0FeWgjMwN06bdeesED8,18770
|
|
21
22
|
realign/commands/session_utils.py,sha256=L1DwZIGCOBirp6tkAswACJEeDa6i9aAAfsialAs4rRY,864
|
|
22
23
|
realign/commands/show.py,sha256=A9LvhOBcY6_HoI76irPB2rBOSgdftBuX2uZiO8IwNoU,16338
|
|
23
|
-
aline_ai-0.2.
|
|
24
|
-
aline_ai-0.2.
|
|
25
|
-
aline_ai-0.2.
|
|
26
|
-
aline_ai-0.2.
|
|
27
|
-
aline_ai-0.2.
|
|
24
|
+
aline_ai-0.2.5.dist-info/METADATA,sha256=Deqqt3Tjs66ZW_76UA7itCXB_YLnhcO-rGa6vN_6vys,1437
|
|
25
|
+
aline_ai-0.2.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
26
|
+
aline_ai-0.2.5.dist-info/entry_points.txt,sha256=h-NocHDzSueXfsepHTIdRPNQzhNZQPAztJfldd-mQTE,202
|
|
27
|
+
aline_ai-0.2.5.dist-info/top_level.txt,sha256=yIL3s2xv9nf1GwD5n71Aq_JEIV4AfzCIDNKBzewuRm4,8
|
|
28
|
+
aline_ai-0.2.5.dist-info/RECORD,,
|
realign/__init__.py
CHANGED
realign/cli.py
CHANGED
|
@@ -7,7 +7,7 @@ from typing import Optional
|
|
|
7
7
|
from rich.console import Console
|
|
8
8
|
from rich.syntax import Syntax
|
|
9
9
|
|
|
10
|
-
from .commands import init, search, show, config, commit, auto_commit, review, hide
|
|
10
|
+
from .commands import init, search, show, config, commit, auto_commit, review, hide, push
|
|
11
11
|
|
|
12
12
|
app = typer.Typer(
|
|
13
13
|
name="realign",
|
|
@@ -48,6 +48,29 @@ def hide_cli(
|
|
|
48
48
|
raise typer.Exit(code=exit_code)
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
@app.command(name="push")
|
|
52
|
+
def push_cli(
|
|
53
|
+
remote: str = typer.Option("origin", "--remote", "-r", help="Git remote name"),
|
|
54
|
+
branch: Optional[str] = typer.Option(None, "--branch", "-b", help="Branch to push (default: current branch)"),
|
|
55
|
+
force: bool = typer.Option(False, "--force", "-f", help="Skip all confirmation prompts"),
|
|
56
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be pushed without actually pushing"),
|
|
57
|
+
):
|
|
58
|
+
"""Interactive push workflow with review and hide options.
|
|
59
|
+
|
|
60
|
+
This command provides an interactive workflow:
|
|
61
|
+
1. Review unpushed commits
|
|
62
|
+
2. Optionally hide sensitive commits
|
|
63
|
+
3. Final confirmation before pushing
|
|
64
|
+
"""
|
|
65
|
+
exit_code = push.push_command(
|
|
66
|
+
remote=remote,
|
|
67
|
+
branch=branch,
|
|
68
|
+
force=force,
|
|
69
|
+
dry_run=dry_run
|
|
70
|
+
)
|
|
71
|
+
raise typer.Exit(code=exit_code)
|
|
72
|
+
|
|
73
|
+
|
|
51
74
|
@app.command()
|
|
52
75
|
def version():
|
|
53
76
|
"""Show ReAlign version."""
|
realign/commands/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""ReAlign commands module."""
|
|
2
2
|
|
|
3
|
-
from . import init, search, show, config, commit, auto_commit, review, hide
|
|
3
|
+
from . import init, search, show, config, commit, auto_commit, review, hide, push
|
|
4
4
|
|
|
5
|
-
__all__ = ["init", "search", "show", "config", "commit", "auto_commit", "review", "hide"]
|
|
5
|
+
__all__ = ["init", "search", "show", "config", "commit", "auto_commit", "review", "hide", "push"]
|
realign/commands/push.py
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Push command - Interactive workflow for reviewing and pushing commits.
|
|
4
|
+
|
|
5
|
+
This command provides an interactive workflow:
|
|
6
|
+
1. Run aline review to show unpushed commits
|
|
7
|
+
2. Ask user if they want to hide any commits
|
|
8
|
+
3. If yes, allow interactive hiding
|
|
9
|
+
4. Ask for final confirmation before pushing
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
from .review import review_command, get_unpushed_commits
|
|
18
|
+
from .hide import hide_command, parse_commit_indices
|
|
19
|
+
from ..logging_config import setup_logger
|
|
20
|
+
|
|
21
|
+
logger = setup_logger('realign.commands.push', 'push.log')
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def prompt_yes_no(question: str, default: bool = False) -> bool:
|
|
25
|
+
"""
|
|
26
|
+
Prompt user for yes/no confirmation.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
question: Question to ask
|
|
30
|
+
default: Default answer if user just presses Enter
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
True for yes, False for no
|
|
34
|
+
"""
|
|
35
|
+
default_str = "Y/n" if default else "y/N"
|
|
36
|
+
prompt = f"{question} [{default_str}] "
|
|
37
|
+
|
|
38
|
+
while True:
|
|
39
|
+
response = input(prompt).strip().lower()
|
|
40
|
+
|
|
41
|
+
if not response:
|
|
42
|
+
return default
|
|
43
|
+
|
|
44
|
+
if response in ('y', 'yes'):
|
|
45
|
+
return True
|
|
46
|
+
elif response in ('n', 'no'):
|
|
47
|
+
return False
|
|
48
|
+
else:
|
|
49
|
+
print("Please answer 'y' or 'n'")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def push_command(
|
|
53
|
+
repo_root: Optional[Path] = None,
|
|
54
|
+
remote: str = "origin",
|
|
55
|
+
branch: Optional[str] = None,
|
|
56
|
+
force: bool = False,
|
|
57
|
+
dry_run: bool = False
|
|
58
|
+
) -> int:
|
|
59
|
+
"""
|
|
60
|
+
Interactive push workflow with review and hide options.
|
|
61
|
+
|
|
62
|
+
Workflow:
|
|
63
|
+
1. Review unpushed commits
|
|
64
|
+
2. Ask if user wants to hide any commits
|
|
65
|
+
3. Interactive hiding loop (if requested)
|
|
66
|
+
4. Final confirmation
|
|
67
|
+
5. Execute git push
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
repo_root: Path to repository root (auto-detected if None)
|
|
71
|
+
remote: Git remote name (default: origin)
|
|
72
|
+
branch: Branch to push (default: current branch)
|
|
73
|
+
force: Skip all confirmation prompts
|
|
74
|
+
dry_run: Don't actually push, just show what would happen
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
0 on success, 1 on error, 2 if cancelled by user
|
|
78
|
+
"""
|
|
79
|
+
logger.info("======== Push command started ========")
|
|
80
|
+
|
|
81
|
+
# Auto-detect repo root if not provided
|
|
82
|
+
if repo_root is None:
|
|
83
|
+
try:
|
|
84
|
+
result = subprocess.run(
|
|
85
|
+
["git", "rev-parse", "--show-toplevel"],
|
|
86
|
+
capture_output=True,
|
|
87
|
+
text=True,
|
|
88
|
+
check=True
|
|
89
|
+
)
|
|
90
|
+
repo_root = Path(result.stdout.strip())
|
|
91
|
+
logger.debug(f"Detected repo root: {repo_root}")
|
|
92
|
+
except subprocess.CalledProcessError:
|
|
93
|
+
print("Error: Not in a git repository", file=sys.stderr)
|
|
94
|
+
logger.error("Not in a git repository")
|
|
95
|
+
return 1
|
|
96
|
+
|
|
97
|
+
# Detect current branch if not provided
|
|
98
|
+
if branch is None:
|
|
99
|
+
try:
|
|
100
|
+
result = subprocess.run(
|
|
101
|
+
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
102
|
+
cwd=repo_root,
|
|
103
|
+
capture_output=True,
|
|
104
|
+
text=True,
|
|
105
|
+
check=True
|
|
106
|
+
)
|
|
107
|
+
branch = result.stdout.strip()
|
|
108
|
+
logger.debug(f"Detected current branch: {branch}")
|
|
109
|
+
except subprocess.CalledProcessError:
|
|
110
|
+
print("Error: Failed to detect current branch", file=sys.stderr)
|
|
111
|
+
logger.error("Failed to detect current branch")
|
|
112
|
+
return 1
|
|
113
|
+
|
|
114
|
+
# Step 1: Review unpushed commits
|
|
115
|
+
print("\n" + "=" * 60)
|
|
116
|
+
print("STEP 1: Review unpushed commits")
|
|
117
|
+
print("=" * 60 + "\n")
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
commits = get_unpushed_commits(repo_root)
|
|
121
|
+
except Exception as e:
|
|
122
|
+
print(f"Error: Failed to get unpushed commits: {e}", file=sys.stderr)
|
|
123
|
+
logger.error(f"Failed to get unpushed commits: {e}", exc_info=True)
|
|
124
|
+
return 1
|
|
125
|
+
|
|
126
|
+
if not commits:
|
|
127
|
+
print("✓ No unpushed commits found. Nothing to push.\n")
|
|
128
|
+
logger.info("No unpushed commits found")
|
|
129
|
+
return 0
|
|
130
|
+
|
|
131
|
+
# Run review command to display commits
|
|
132
|
+
exit_code = review_command(repo_root=repo_root, verbose=False, detect_secrets=False)
|
|
133
|
+
if exit_code != 0:
|
|
134
|
+
logger.error("Review command failed")
|
|
135
|
+
return 1
|
|
136
|
+
|
|
137
|
+
# Step 2: Ask if user wants to hide any commits
|
|
138
|
+
if not force:
|
|
139
|
+
print("\n" + "=" * 60)
|
|
140
|
+
print("STEP 2: Hide sensitive commits (optional)")
|
|
141
|
+
print("=" * 60 + "\n")
|
|
142
|
+
|
|
143
|
+
want_to_hide = prompt_yes_no(
|
|
144
|
+
"Do you want to hide any commits before pushing?",
|
|
145
|
+
default=False
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
if want_to_hide:
|
|
149
|
+
# Interactive hiding loop
|
|
150
|
+
while True:
|
|
151
|
+
print("\nEnter commit indices to hide (e.g., '1', '1,3,5', '2-4', or 'done'):")
|
|
152
|
+
print(" Type 'done' when finished hiding commits")
|
|
153
|
+
print(" Type 'review' to see the current list of commits")
|
|
154
|
+
print(" Type 'cancel' to abort the push operation")
|
|
155
|
+
|
|
156
|
+
user_input = input("\n> ").strip()
|
|
157
|
+
|
|
158
|
+
if not user_input:
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
if user_input.lower() == 'done':
|
|
162
|
+
break
|
|
163
|
+
elif user_input.lower() == 'cancel':
|
|
164
|
+
print("\n❌ Push cancelled by user.\n")
|
|
165
|
+
logger.info("Push cancelled by user during hide phase")
|
|
166
|
+
return 2
|
|
167
|
+
elif user_input.lower() == 'review':
|
|
168
|
+
print("\n" + "-" * 60)
|
|
169
|
+
review_command(repo_root=repo_root, verbose=False, detect_secrets=False)
|
|
170
|
+
print("-" * 60)
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
# Try to parse and hide the commits
|
|
174
|
+
try:
|
|
175
|
+
# Validate indices first
|
|
176
|
+
indices_list = parse_commit_indices(user_input)
|
|
177
|
+
|
|
178
|
+
# Refresh commit list
|
|
179
|
+
commits = get_unpushed_commits(repo_root)
|
|
180
|
+
if not commits:
|
|
181
|
+
print("\n✓ No more unpushed commits to hide.\n")
|
|
182
|
+
break
|
|
183
|
+
|
|
184
|
+
max_index = len(commits)
|
|
185
|
+
invalid_indices = [i for i in indices_list if i < 1 or i > max_index]
|
|
186
|
+
if invalid_indices:
|
|
187
|
+
print(f"❌ Invalid indices (out of range 1-{max_index}): {invalid_indices}")
|
|
188
|
+
continue
|
|
189
|
+
|
|
190
|
+
# Execute hide command
|
|
191
|
+
print()
|
|
192
|
+
exit_code = hide_command(
|
|
193
|
+
indices=user_input,
|
|
194
|
+
repo_root=repo_root,
|
|
195
|
+
force=False # Always ask for confirmation
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
if exit_code == 0:
|
|
199
|
+
print("\n✅ Commits hidden successfully.\n")
|
|
200
|
+
logger.info(f"Successfully hidden commits: {user_input}")
|
|
201
|
+
|
|
202
|
+
# Refresh and show updated commit list
|
|
203
|
+
commits = get_unpushed_commits(repo_root)
|
|
204
|
+
if not commits:
|
|
205
|
+
print("✓ No more unpushed commits.\n")
|
|
206
|
+
logger.info("No commits left after hiding")
|
|
207
|
+
return 0
|
|
208
|
+
else:
|
|
209
|
+
print("\n📋 Updated commit list:\n")
|
|
210
|
+
review_command(repo_root=repo_root, verbose=False, detect_secrets=False)
|
|
211
|
+
else:
|
|
212
|
+
print(f"\n❌ Failed to hide commits.\n")
|
|
213
|
+
logger.error(f"Hide command failed for indices: {user_input}")
|
|
214
|
+
|
|
215
|
+
# Ask if user wants to continue
|
|
216
|
+
if not prompt_yes_no("Continue with push workflow?", default=True):
|
|
217
|
+
print("\n❌ Push cancelled.\n")
|
|
218
|
+
return 2
|
|
219
|
+
|
|
220
|
+
except ValueError as e:
|
|
221
|
+
print(f"❌ Invalid input: {e}")
|
|
222
|
+
print(" Examples: '1', '1,3,5', '2-4'")
|
|
223
|
+
continue
|
|
224
|
+
except Exception as e:
|
|
225
|
+
print(f"❌ Error during hide: {e}", file=sys.stderr)
|
|
226
|
+
logger.error(f"Error during hide: {e}", exc_info=True)
|
|
227
|
+
|
|
228
|
+
# Ask if user wants to continue
|
|
229
|
+
if not prompt_yes_no("Continue with push workflow?", default=True):
|
|
230
|
+
print("\n❌ Push cancelled.\n")
|
|
231
|
+
return 2
|
|
232
|
+
|
|
233
|
+
# Step 3: Final confirmation
|
|
234
|
+
if not force:
|
|
235
|
+
print("\n" + "=" * 60)
|
|
236
|
+
print("STEP 3: Final confirmation")
|
|
237
|
+
print("=" * 60 + "\n")
|
|
238
|
+
|
|
239
|
+
# Refresh commit list one more time
|
|
240
|
+
try:
|
|
241
|
+
commits = get_unpushed_commits(repo_root)
|
|
242
|
+
except Exception as e:
|
|
243
|
+
print(f"Error: Failed to refresh commits: {e}", file=sys.stderr)
|
|
244
|
+
return 1
|
|
245
|
+
|
|
246
|
+
if not commits:
|
|
247
|
+
print("✓ No unpushed commits. Nothing to push.\n")
|
|
248
|
+
return 0
|
|
249
|
+
|
|
250
|
+
print(f"Ready to push {len(commits)} commit(s) to {remote}/{branch}\n")
|
|
251
|
+
|
|
252
|
+
if not prompt_yes_no("Proceed with git push?", default=True):
|
|
253
|
+
print("\n❌ Push cancelled by user.\n")
|
|
254
|
+
logger.info("Push cancelled by user at final confirmation")
|
|
255
|
+
return 2
|
|
256
|
+
|
|
257
|
+
# Step 4: Execute git push
|
|
258
|
+
print("\n" + "=" * 60)
|
|
259
|
+
print("STEP 4: Pushing to remote")
|
|
260
|
+
print("=" * 60 + "\n")
|
|
261
|
+
|
|
262
|
+
if dry_run:
|
|
263
|
+
print(f"🔍 DRY RUN: Would execute: git push {remote} {branch}\n")
|
|
264
|
+
logger.info(f"Dry run: would push to {remote}/{branch}")
|
|
265
|
+
return 0
|
|
266
|
+
|
|
267
|
+
try:
|
|
268
|
+
print(f"🚀 Pushing to {remote}/{branch}...")
|
|
269
|
+
|
|
270
|
+
result = subprocess.run(
|
|
271
|
+
["git", "push", remote, branch],
|
|
272
|
+
cwd=repo_root,
|
|
273
|
+
capture_output=True,
|
|
274
|
+
text=True
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
if result.returncode == 0:
|
|
278
|
+
print("\n✅ Successfully pushed to remote!\n")
|
|
279
|
+
print(result.stdout)
|
|
280
|
+
logger.info(f"Successfully pushed to {remote}/{branch}")
|
|
281
|
+
return 0
|
|
282
|
+
else:
|
|
283
|
+
print(f"\n❌ Push failed:\n{result.stderr}\n", file=sys.stderr)
|
|
284
|
+
logger.error(f"Git push failed: {result.stderr}")
|
|
285
|
+
return 1
|
|
286
|
+
|
|
287
|
+
except Exception as e:
|
|
288
|
+
print(f"\n❌ Error during push: {e}\n", file=sys.stderr)
|
|
289
|
+
logger.error(f"Error during push: {e}", exc_info=True)
|
|
290
|
+
return 1
|
realign/mcp_server.py
CHANGED
|
@@ -170,6 +170,63 @@ async def list_tools() -> list[Tool]:
|
|
|
170
170
|
"properties": {},
|
|
171
171
|
},
|
|
172
172
|
),
|
|
173
|
+
Tool(
|
|
174
|
+
name="aline_review",
|
|
175
|
+
description=(
|
|
176
|
+
"Review unpushed commits before pushing. "
|
|
177
|
+
"Shows commit messages, LLM summaries, session files modified, and line counts. "
|
|
178
|
+
"Helps identify what content will be made public when you push."
|
|
179
|
+
),
|
|
180
|
+
inputSchema={
|
|
181
|
+
"type": "object",
|
|
182
|
+
"properties": {
|
|
183
|
+
"repo_path": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"description": "Path to repository root (default: current directory)",
|
|
186
|
+
},
|
|
187
|
+
"verbose": {
|
|
188
|
+
"type": "boolean",
|
|
189
|
+
"description": "Show detailed information",
|
|
190
|
+
"default": False,
|
|
191
|
+
},
|
|
192
|
+
"detect_secrets": {
|
|
193
|
+
"type": "boolean",
|
|
194
|
+
"description": "Run sensitive content detection",
|
|
195
|
+
"default": False,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
),
|
|
200
|
+
Tool(
|
|
201
|
+
name="aline_hide",
|
|
202
|
+
description=(
|
|
203
|
+
"Hide (redact) specific unpushed commits by rewriting git history. "
|
|
204
|
+
"This redacts commit messages and session content. "
|
|
205
|
+
"WARNING: This rewrites git history. Use with caution."
|
|
206
|
+
),
|
|
207
|
+
inputSchema={
|
|
208
|
+
"type": "object",
|
|
209
|
+
"properties": {
|
|
210
|
+
"indices": {
|
|
211
|
+
"type": "string",
|
|
212
|
+
"description": (
|
|
213
|
+
"Commit indices to hide (e.g., '1', '1,3,5', '2-4', or '--all'). "
|
|
214
|
+
"Use aline_review first to see the commit indices."
|
|
215
|
+
),
|
|
216
|
+
},
|
|
217
|
+
"repo_path": {
|
|
218
|
+
"type": "string",
|
|
219
|
+
"description": "Path to repository root (default: current directory)",
|
|
220
|
+
},
|
|
221
|
+
"force": {
|
|
222
|
+
"type": "boolean",
|
|
223
|
+
"description": "Skip confirmation prompt",
|
|
224
|
+
"default": False,
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
"required": ["indices"],
|
|
228
|
+
},
|
|
229
|
+
),
|
|
173
230
|
]
|
|
174
231
|
|
|
175
232
|
|
|
@@ -189,6 +246,10 @@ async def call_tool(name: str, arguments: Any) -> list[TextContent]:
|
|
|
189
246
|
result = await handle_get_latest_session(arguments)
|
|
190
247
|
elif name == "aline_version":
|
|
191
248
|
result = await handle_version(arguments)
|
|
249
|
+
elif name == "aline_review":
|
|
250
|
+
result = await handle_review(arguments)
|
|
251
|
+
elif name == "aline_hide":
|
|
252
|
+
result = await handle_hide(arguments)
|
|
192
253
|
else:
|
|
193
254
|
return [TextContent(type="text", text=f"Unknown tool: {name}")]
|
|
194
255
|
|
|
@@ -395,6 +456,78 @@ async def handle_version(args: dict) -> list[TextContent]:
|
|
|
395
456
|
)]
|
|
396
457
|
|
|
397
458
|
|
|
459
|
+
async def handle_review(args: dict) -> list[TextContent]:
|
|
460
|
+
"""Handle aline_review tool."""
|
|
461
|
+
from .commands.review import review_command
|
|
462
|
+
from io import StringIO
|
|
463
|
+
import sys
|
|
464
|
+
|
|
465
|
+
repo_path = args.get("repo_path")
|
|
466
|
+
verbose = args.get("verbose", False)
|
|
467
|
+
detect_secrets = args.get("detect_secrets", False)
|
|
468
|
+
|
|
469
|
+
# Convert repo_path to Path if provided
|
|
470
|
+
repo_root = Path(repo_path) if repo_path else None
|
|
471
|
+
|
|
472
|
+
# Capture stdout
|
|
473
|
+
old_stdout = sys.stdout
|
|
474
|
+
sys.stdout = captured_output = StringIO()
|
|
475
|
+
|
|
476
|
+
try:
|
|
477
|
+
# Call the review command
|
|
478
|
+
exit_code = review_command(
|
|
479
|
+
repo_root=repo_root,
|
|
480
|
+
verbose=verbose,
|
|
481
|
+
detect_secrets=detect_secrets
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
# Get the output
|
|
485
|
+
output = captured_output.getvalue()
|
|
486
|
+
|
|
487
|
+
if exit_code == 0:
|
|
488
|
+
return [TextContent(type="text", text=output or "No unpushed commits found.")]
|
|
489
|
+
else:
|
|
490
|
+
return [TextContent(type="text", text=f"Error: Review failed\n{output}")]
|
|
491
|
+
finally:
|
|
492
|
+
sys.stdout = old_stdout
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
async def handle_hide(args: dict) -> list[TextContent]:
|
|
496
|
+
"""Handle aline_hide tool."""
|
|
497
|
+
from .commands.hide import hide_command
|
|
498
|
+
from io import StringIO
|
|
499
|
+
import sys
|
|
500
|
+
|
|
501
|
+
indices = args["indices"]
|
|
502
|
+
repo_path = args.get("repo_path")
|
|
503
|
+
force = args.get("force", False)
|
|
504
|
+
|
|
505
|
+
# Convert repo_path to Path if provided
|
|
506
|
+
repo_root = Path(repo_path) if repo_path else None
|
|
507
|
+
|
|
508
|
+
# Capture stdout
|
|
509
|
+
old_stdout = sys.stdout
|
|
510
|
+
sys.stdout = captured_output = StringIO()
|
|
511
|
+
|
|
512
|
+
try:
|
|
513
|
+
# Call the hide command
|
|
514
|
+
exit_code = hide_command(
|
|
515
|
+
indices=indices,
|
|
516
|
+
repo_root=repo_root,
|
|
517
|
+
force=force
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
# Get the output
|
|
521
|
+
output = captured_output.getvalue()
|
|
522
|
+
|
|
523
|
+
if exit_code == 0:
|
|
524
|
+
return [TextContent(type="text", text=output or "Hide operation completed.")]
|
|
525
|
+
else:
|
|
526
|
+
return [TextContent(type="text", text=f"Error: Hide failed\n{output}")]
|
|
527
|
+
finally:
|
|
528
|
+
sys.stdout = old_stdout
|
|
529
|
+
|
|
530
|
+
|
|
398
531
|
def _server_log(msg: str):
|
|
399
532
|
"""Log MCP server messages to both stderr and file."""
|
|
400
533
|
from datetime import datetime
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|