agentic-devtools 0.2.413__py3-none-any.whl → 0.2.415__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.
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.2.413'
22
- __version_tuple__ = version_tuple = (0, 2, 413)
21
+ __version__ = version = '0.2.415'
22
+ __version_tuple__ = version_tuple = (0, 2, 415)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -140,11 +140,20 @@ _MARKDOWN_LINK_PATTERN = re.compile(r"\[[^\]]*\]\(([^()]*(?:\([^()]*\)[^()]*)*)\
140
140
  _REFERENCE_LINK_PATTERN = re.compile(r"\[([^\]]+)\]\[([^\]]*)\]")
141
141
  _SHORTCUT_REFERENCE_PATTERN = re.compile(r"(?<![!\]])\[([^\]]+)\](?!\[)(?!\()(?!:)")
142
142
  _SHORTCUT_IMAGE_PATTERN = re.compile(r"!\[([^\]]+)\](?!\[)(?!\()")
143
- _REFERENCE_DEFINITION_PATTERN = re.compile(r"^[ \t]{0,3}\[([^\]]+)\]:\s*(.+)$", re.MULTILINE)
144
- _INLINE_CODE_PATTERN = re.compile(r"`([^`\n]+)`")
143
+ _REFERENCE_DEFINITION_PATTERN = re.compile(
144
+ r"^[ \t]{0,3}\[([^\]]+)\]:[ \t]*(?:\r?\n[ \t]{0,3})?(.+)$",
145
+ re.MULTILINE,
146
+ )
145
147
  _PATH_CANDIDATE_PATTERN = re.compile(r"^[A-Za-z0-9_.\-]+(?:/[A-Za-z0-9_.\-]+)+/?$")
146
148
  _URI_SCHEME_PATTERN = re.compile(r"^[A-Za-z][A-Za-z0-9+.\-]*:")
147
149
  _FENCE_OPEN_PATTERN = re.compile(r"^[ \t]{0,3}(?P<fence>`{3,}|~{3,}).*$")
150
+ _ATX_HEADING_BOUNDARY_PATTERN = re.compile(r"^[ \t]{0,3}#{1,6}(?:[ \t]+|$)")
151
+ _BLOCK_QUOTE_BOUNDARY_PATTERN = re.compile(r"^[ \t]{0,3}>")
152
+ _UNORDERED_LIST_BOUNDARY_PATTERN = re.compile(r"^[ \t]{0,3}[-+*][ \t]+")
153
+ _ORDERED_LIST_BOUNDARY_PATTERN = re.compile(r"^[ \t]{0,3}1[.)][ \t]+")
154
+ _ORDERED_LIST_ITEM_PATTERN = re.compile(r"^[ \t]{0,3}\d+[.)][ \t]+")
155
+ _SETEXT_HEADING_BOUNDARY_PATTERN = re.compile(r"^[ \t]{0,3}(?:=+|-+)[ \t]*$")
156
+ _THEMATIC_BREAK_BOUNDARY_PATTERN = re.compile(r"^[ \t]{0,3}(?:(?:\*[ \t]*){3,}|(?:-[ \t]*){3,}|(?:_[ \t]*){3,})$")
148
157
 
149
158
  _WORD_PATTERN = re.compile(r"[a-z0-9]+")
150
159
  _STOPWORDS: frozenset[str] = frozenset(
@@ -697,8 +706,9 @@ def check_path_references(unit: CustomizationUnit, repo_root: Path | str) -> lis
697
706
  entry_dir = Path(unit.path).parent
698
707
  violations: list[Violation] = []
699
708
  visible_body = _strip_fenced_code(unit.body)
709
+ link_scan_body = _strip_inline_code(visible_body)
700
710
 
701
- for target in _iter_markdown_targets(visible_body):
711
+ for target in _iter_markdown_targets(link_scan_body):
702
712
  link = _link_target(target)
703
713
  if not link or link.startswith("#") or _URI_SCHEME_PATTERN.match(link):
704
714
  continue
@@ -711,7 +721,7 @@ def check_path_references(unit: CustomizationUnit, repo_root: Path | str) -> lis
711
721
  Violation(RULE_PATHS, unit.path, f"linked resource is more than one level from its entry file: {link}")
712
722
  )
713
723
 
714
- for span in _INLINE_CODE_PATTERN.findall(visible_body):
724
+ for _, _, span in _iter_inline_code_spans(visible_body):
715
725
  candidate = span.strip()
716
726
  if not _PATH_CANDIDATE_PATTERN.match(candidate):
717
727
  continue
@@ -769,14 +779,180 @@ def _strip_fenced_code(body: str) -> str:
769
779
  inside = True
770
780
  fence = match.group("fence")[0]
771
781
  fence_len = len(match.group("fence"))
782
+ kept.append(_line_break_only(raw_line))
772
783
  continue
773
784
  kept.append(raw_line)
774
785
  continue
786
+ kept.append(_line_break_only(raw_line))
775
787
  if _is_fence_close(line, fence, fence_len):
776
788
  inside = False
777
789
  return "".join(kept)
778
790
 
779
791
 
792
+ def _strip_inline_code(body: str) -> str:
793
+ """Return *body* with inline code spans replaced by spaces.
794
+
795
+ Inline code contents must not be parsed for Markdown link syntax because
796
+ they are literal text. The replacement preserves offsets and line
797
+ structure so that reference-definition lines immediately before or after a
798
+ span are still found at their original positions.
799
+ """
800
+ parts: list[str] = []
801
+ last = 0
802
+ for start, end, _ in _iter_inline_code_spans(body):
803
+ parts.append(body[last:start])
804
+ span = body[start:end]
805
+ parts.append("".join(char if char in "\r\n`" else " " for char in span))
806
+ last = end
807
+ parts.append(body[last:])
808
+ return "".join(parts)
809
+
810
+
811
+ def _line_break_only(line: str) -> str:
812
+ """Return only the line break from *line* (if present)."""
813
+ if line.endswith("\r\n"):
814
+ return "\r\n"
815
+ if line.endswith("\n"):
816
+ return "\n"
817
+ return ""
818
+
819
+
820
+ def _iter_inline_code_spans(body: str) -> list[tuple[int, int, str]]:
821
+ """Return Markdown inline-code spans as ``(start, end, content)`` tuples.
822
+
823
+ Matching is done per non-blank block so a code span may cross a line break
824
+ inside one paragraph, but an unmatched backtick run cannot consume later
825
+ paragraphs. Opening and closing delimiters must use the same backtick-run
826
+ length.
827
+ """
828
+ spans: list[tuple[int, int, str]] = []
829
+ for block_start, block_end in _nonblank_block_ranges(body):
830
+ cursor = block_start
831
+ while cursor < block_end:
832
+ if body[cursor] != "`" or _is_escaped_backtick_run(body, cursor):
833
+ cursor += 1
834
+ continue
835
+ open_start = cursor
836
+ while cursor < block_end and body[cursor] == "`":
837
+ cursor += 1
838
+ delimiter_len = cursor - open_start
839
+ close = _find_inline_code_close(body, cursor, block_end, delimiter_len)
840
+ if close is None:
841
+ continue
842
+ spans.append((open_start, close + delimiter_len, body[cursor:close]))
843
+ cursor = close + delimiter_len
844
+ return spans
845
+
846
+
847
+ def _nonblank_block_ranges(body: str) -> list[tuple[int, int]]:
848
+ """Return ``[start, end)`` ranges for non-blank line blocks in *body*.
849
+
850
+ Consecutive block-quote lines (``> …``) at the same nesting depth are kept
851
+ in the same range so that a multiline code span within one block-quoted
852
+ paragraph is not split across separate ranges. A change in quote depth
853
+ starts a new range so that a code span cannot cross a nesting boundary.
854
+ A quote-only line (``>`` / ``> ``) is treated as blank and ends the current
855
+ paragraph inside the block quote.
856
+
857
+ Ordered list items (``2.``, ``3.``, …) that continue a list started by
858
+ any digit are treated as block boundaries so that an unterminated backtick
859
+ in one item cannot pair with a backtick in a later item.
860
+ """
861
+ ranges: list[tuple[int, int]] = []
862
+ block_start: int | None = None
863
+ offset = 0
864
+ prev_quote_depth = 0
865
+ in_ordered_list = False
866
+ for line in body.splitlines(keepends=True):
867
+ # Count block-quote depth and strip all ``>`` markers so that nested
868
+ # quotes (``>>``, ``>>>``) do not leave a residual ``>`` that would look
869
+ # like a new block boundary to ``_starts_markdown_block_boundary``.
870
+ block_line = line
871
+ quote_depth = 0
872
+ while _BLOCK_QUOTE_BOUNDARY_PATTERN.match(block_line):
873
+ block_line = _BLOCK_QUOTE_BOUNDARY_PATTERN.sub("", block_line, count=1)
874
+ quote_depth += 1
875
+ is_block_quote = quote_depth > 0
876
+ if block_line.strip():
877
+ if block_start is None:
878
+ block_start = offset
879
+ if _ORDERED_LIST_ITEM_PATTERN.match(block_line):
880
+ in_ordered_list = True
881
+ elif (
882
+ _starts_markdown_block_boundary(block_line)
883
+ or (is_block_quote and prev_quote_depth == 0)
884
+ or (is_block_quote and prev_quote_depth > 0 and quote_depth != prev_quote_depth)
885
+ or (in_ordered_list and _ORDERED_LIST_ITEM_PATTERN.match(block_line))
886
+ ):
887
+ ranges.append((block_start, offset))
888
+ block_start = offset
889
+ if _ORDERED_LIST_BOUNDARY_PATTERN.match(block_line):
890
+ in_ordered_list = True
891
+ elif not _ORDERED_LIST_ITEM_PATTERN.match(block_line):
892
+ in_ordered_list = False
893
+ prev_quote_depth = quote_depth
894
+ else:
895
+ if block_start is not None:
896
+ ranges.append((block_start, offset))
897
+ block_start = None
898
+ prev_quote_depth = 0
899
+ in_ordered_list = False
900
+ offset += len(line)
901
+ # ATX headings are self-terminating single-line blocks. Close the
902
+ # range immediately so that an unmatched backtick inside the heading
903
+ # cannot pair with a backtick in the following paragraph.
904
+ if block_start is not None and _ATX_HEADING_BOUNDARY_PATTERN.match(block_line):
905
+ ranges.append((block_start, offset))
906
+ block_start = None
907
+ prev_quote_depth = 0
908
+ in_ordered_list = False
909
+ if block_start is not None:
910
+ ranges.append((block_start, len(body)))
911
+ return ranges
912
+
913
+
914
+ def _starts_markdown_block_boundary(line: str) -> bool:
915
+ """Return whether *line* starts a Markdown block that interrupts a paragraph."""
916
+ return bool(
917
+ _ATX_HEADING_BOUNDARY_PATTERN.match(line)
918
+ or _BLOCK_QUOTE_BOUNDARY_PATTERN.match(line)
919
+ or _UNORDERED_LIST_BOUNDARY_PATTERN.match(line)
920
+ or _ORDERED_LIST_BOUNDARY_PATTERN.match(line)
921
+ or _SETEXT_HEADING_BOUNDARY_PATTERN.match(line)
922
+ or _THEMATIC_BREAK_BOUNDARY_PATTERN.match(line)
923
+ )
924
+
925
+
926
+ def _find_inline_code_close(body: str, start: int, end: int, delimiter_len: int) -> int | None:
927
+ """Return the start offset of the matching inline-code closer, if any.
928
+
929
+ Backslash escaping does not apply inside a code span (CommonMark §6.1), so
930
+ the ``_is_escaped_backtick_run`` check is intentionally absent here. It is
931
+ only used by the opener scanner in ``_iter_inline_code_spans``.
932
+ """
933
+ cursor = start
934
+ while cursor < end:
935
+ if body[cursor] != "`":
936
+ cursor += 1
937
+ continue
938
+ run_start = cursor
939
+ while cursor < end and body[cursor] == "`":
940
+ cursor += 1
941
+ if cursor - run_start == delimiter_len:
942
+ return run_start
943
+ return None
944
+
945
+
946
+ def _is_escaped_backtick_run(body: str, start: int) -> bool:
947
+ """Return whether the backtick at *start* is escaped by an odd backslash run."""
948
+ backslashes = 0
949
+ cursor = start - 1
950
+ while cursor >= 0 and body[cursor] == "\\":
951
+ backslashes += 1
952
+ cursor -= 1
953
+ return backslashes % 2 == 1
954
+
955
+
780
956
  def _normalize_reference_label(label: str) -> str:
781
957
  """Return a Markdown reference label in its normalized lookup form."""
782
958
  return " ".join(label.strip().split()).lower()
@@ -127,6 +127,7 @@ class ResolveThreadsAction:
127
127
  suppressed = 0
128
128
  skipped_reviews: list[str] = []
129
129
  finalization_errors: list[str] = []
130
+ hard_failures: list[tuple[int, str]] = []
130
131
 
131
132
  for prior_review in prior_reviews:
132
133
  try:
@@ -138,13 +139,11 @@ class ResolveThreadsAction:
138
139
  review_id=prior_review.id,
139
140
  )
140
141
  except Exception as exc:
141
- logger.error("PR #%d: Thread resolution failed: %s", snapshot.pr_number, exc)
142
- return ActionResult(
143
- name=self.name,
144
- decision=ActionDecision.FAILED,
145
- error=str(exc),
146
- details="finalize_post_repair raised an exception",
142
+ logger.error(
143
+ "PR #%d: Thread resolution failed for review %s: %s", snapshot.pr_number, prior_review.id, exc
147
144
  )
145
+ hard_failures.append((prior_review.id, str(exc)))
146
+ continue
148
147
 
149
148
  finalization_errors.extend(result.errors)
150
149
 
@@ -156,6 +155,15 @@ class ResolveThreadsAction:
156
155
  unresolved += result.unresolved_count
157
156
  suppressed += result.suppressed_count
158
157
 
158
+ if len(hard_failures) == len(prior_reviews):
159
+ failure_msgs = [f"#{rid}: {err}" for rid, err in hard_failures]
160
+ return ActionResult(
161
+ name=self.name,
162
+ decision=ActionDecision.FAILED,
163
+ error="; ".join(failure_msgs),
164
+ details=f"All {len(prior_reviews)} prior reviews failed finalization: {', '.join(failure_msgs)}",
165
+ )
166
+
159
167
  # Update derived state so downstream actions (approve, merge) see the
160
168
  # post-resolution count within the same pipeline run.
161
169
  #
@@ -192,6 +200,9 @@ class ResolveThreadsAction:
192
200
  details = f"{details}; {suppressed} suppressed comment(s) not counted"
193
201
  if skipped_reviews:
194
202
  details = f"{details}; skipped {len(skipped_reviews)} prior review(s): {', '.join(skipped_reviews)}"
203
+ if hard_failures:
204
+ failure_msgs = [f"#{rid}: {err}" for rid, err in hard_failures]
205
+ details = f"{details}; hard failures: {', '.join(failure_msgs)}"
195
206
  if finalization_errors:
196
207
  details = f"{details}; finalization errors: {', '.join(finalization_errors)}"
197
208
 
@@ -249,6 +249,8 @@ def run_setup_with_pr_workflow(
249
249
  pr_created = False
250
250
  message = "No file changes detected."
251
251
  emergency_stash_created = False
252
+ temporary_branch_name: str | None = None
253
+ should_delete_temporary_branch = False
252
254
  branch_restored = False
253
255
 
254
256
  try:
@@ -318,6 +320,7 @@ def run_setup_with_pr_workflow(
318
320
  )
319
321
  message = f"Failed to create branch '{branch_name}'."
320
322
  else:
323
+ temporary_branch_name = branch_name
321
324
  commit_msg = f"chore: agdt-setup v{version}"
322
325
  add_result = run_git("add", ".", check=False)
323
326
  if add_result.returncode != 0:
@@ -329,11 +332,31 @@ def run_setup_with_pr_workflow(
329
332
  else:
330
333
  commit_result = run_git("commit", "-m", commit_msg, check=False)
331
334
  if commit_result.returncode != 0:
332
- print(
333
- f"Error: 'git commit' failed: {commit_result.stderr.strip()}",
334
- file=sys.stderr,
335
- )
336
- message = "Failed to commit changes."
335
+ diff_after = run_git("diff", "--cached", "--quiet", check=False)
336
+ if diff_after.returncode == 0:
337
+ status_after = run_git("status", "--porcelain", check=False)
338
+ if status_after.returncode == 0 and not status_after.stdout.strip():
339
+ print(
340
+ " ℹ Commit aborted by hooks or empty diff; treating as idempotent no-op.",
341
+ file=sys.stderr,
342
+ )
343
+ message = (
344
+ "No new changes — setup output matches origin/main "
345
+ "(already merged or formatted away)."
346
+ )
347
+ should_delete_temporary_branch = True
348
+ else:
349
+ print(
350
+ f"Error: 'git commit' failed: {commit_result.stderr.strip()}",
351
+ file=sys.stderr,
352
+ )
353
+ message = "Failed to commit changes."
354
+ else:
355
+ print(
356
+ f"Error: 'git commit' failed: {commit_result.stderr.strip()}",
357
+ file=sys.stderr,
358
+ )
359
+ message = "Failed to commit changes."
337
360
  else:
338
361
  branch_created = branch_name
339
362
 
@@ -461,6 +484,13 @@ def run_setup_with_pr_workflow(
461
484
  f"Run 'git checkout {original_branch}' manually.",
462
485
  file=sys.stderr,
463
486
  )
487
+ if branch_restored and should_delete_temporary_branch and temporary_branch_name:
488
+ delete_branch = run_git("branch", "-D", temporary_branch_name, check=False)
489
+ if delete_branch.returncode != 0:
490
+ print(
491
+ f"Warning: Could not delete temporary branch '{temporary_branch_name}'.",
492
+ file=sys.stderr,
493
+ )
464
494
 
465
495
  # Step 13 — pop the user's original auto-stash.
466
496
  # Only pop when the original branch was successfully restored;
@@ -7,6 +7,7 @@ latest PyPI release of ``agentic-devtools``.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ import os
10
11
  import re
11
12
  import site
12
13
  import subprocess
@@ -29,9 +30,13 @@ SELF_UPGRADE_LOCK_MESSAGE = (
29
30
  )
30
31
 
31
32
  #: Actionable remediation printed alongside :data:`SELF_UPGRADE_LOCK_MESSAGE`.
32
- SELF_UPGRADE_LOCK_REMEDY = (
33
+ #: The ``<sys.executable>`` placeholder stands for the Python interpreter path;
34
+ #: the generated script substitutes the real path via an f-string at runtime,
35
+ #: formats it for PowerShell with ``&`` and surrounds the executable with
36
+ #: double quotes so paths with spaces remain runnable.
37
+ SELF_UPGRADE_LOCK_REMEDY_TEMPLATE = (
33
38
  " Continuing with the installed version. To upgrade, close every running "
34
- "agdt-* process and run: python -m pip install --upgrade agentic-devtools"
39
+ 'agdt-* process and run: & "<sys.executable>" -m pip install --upgrade agentic-devtools'
35
40
  )
36
41
 
37
42
  _SELF_UPGRADE_LOCK_EXECUTABLE = "agdt-setup.exe"
@@ -61,28 +66,30 @@ def is_self_upgrade_lock(output: str) -> bool:
61
66
 
62
67
  def _is_agentic_devtools_tilde_backup(name: str) -> bool:
63
68
  """Return ``True`` for pip's ``~``-mangled agentic-devtools backups."""
69
+ name_lower = name.lower()
64
70
  for candidate in ("agentic-devtools", "agentic_devtools"):
65
71
  prefix = f"~{candidate[1:]}"
66
- if name == prefix:
72
+ if name_lower == prefix:
67
73
  return True
68
- if not name.startswith(prefix):
74
+ if not name_lower.startswith(prefix):
69
75
  continue
70
- remainder = name[len(prefix) :]
76
+ remainder = name_lower[len(prefix) :]
71
77
  return _has_distribution_suffix(remainder)
72
78
  return False
73
79
 
74
80
 
75
81
  def _is_agentic_devtools_distribution_name(name: str) -> bool:
76
82
  """Return ``True`` for agentic-devtools distribution names and pip backups."""
77
- if _is_agentic_devtools_tilde_backup(name):
83
+ name_lower = name.lower()
84
+ if _is_agentic_devtools_tilde_backup(name_lower):
78
85
  return True
79
86
 
80
87
  for candidate in ("agentic-devtools", "agentic_devtools"):
81
- if name == candidate:
88
+ if name_lower == candidate:
82
89
  return True
83
- if not name.startswith(candidate):
90
+ if not name_lower.startswith(candidate):
84
91
  continue
85
- remainder = name[len(candidate) :]
92
+ remainder = name_lower[len(candidate) :]
86
93
  if _has_distribution_suffix(remainder):
87
94
  return True
88
95
  return False
@@ -178,8 +185,23 @@ def install_package() -> tuple[bool, str]:
178
185
  """Install/upgrade ``agentic-devtools`` from PyPI.
179
186
 
180
187
  Returns ``(success, output)`` where *output* is the combined
181
- stdout + stderr from pip.
188
+ stdout + stderr from pip, or a synthetic lock message when the
189
+ Windows autorun short-circuit fires (see below).
190
+
191
+ On Windows when spawned by ``agdt-setup.exe`` (``AGDT_SETUP_AUTORUN``
192
+ is set), pip is **not** invoked — the function returns
193
+ ``(False, <lock message>)`` immediately to prevent WinError 32 from
194
+ corrupting package metadata. Callers should treat that pair the same
195
+ as a real pip WinError 32 failure.
182
196
  """
197
+ if sys.platform == "win32" and os.environ.get("AGDT_SETUP_AUTORUN"):
198
+ # Proactively skip pip install when spawned by agdt-setup.exe to avoid WinError 32
199
+ return (
200
+ False,
201
+ "[WinError 32] The process cannot access the file because "
202
+ "it is being used by another process: agdt-setup.exe",
203
+ )
204
+
183
205
  stdout_chunks: list[str] = []
184
206
  stderr_chunks: list[str] = []
185
207
  process = subprocess.Popen(
@@ -374,7 +396,7 @@ _SELF_UPGRADE_LOCK_MESSAGE = (
374
396
 
375
397
  _SELF_UPGRADE_LOCK_REMEDY = (
376
398
  " Continuing with the installed version. To upgrade, close every running "
377
- "agdt-* process and run: python -m pip install --upgrade agentic-devtools"
399
+ f'agdt-* process and run: & "{sys.executable}" -m pip install --upgrade agentic-devtools'
378
400
  )
379
401
 
380
402
  _SELF_UPGRADE_LOCK_EXECUTABLE = "agdt-setup.exe"
@@ -397,28 +419,30 @@ def _site_packages_dirs():
397
419
 
398
420
  def _is_agentic_devtools_tilde_backup(name):
399
421
  """Return True for pip's ~-mangled agentic-devtools backups."""
422
+ name_lower = name.lower()
400
423
  for candidate in ("agentic-devtools", "agentic_devtools"):
401
424
  prefix = f"~{candidate[1:]}"
402
- if name == prefix:
425
+ if name_lower == prefix:
403
426
  return True
404
- if not name.startswith(prefix):
427
+ if not name_lower.startswith(prefix):
405
428
  continue
406
- remainder = name[len(prefix) :]
429
+ remainder = name_lower[len(prefix) :]
407
430
  return _has_distribution_suffix(remainder)
408
431
  return False
409
432
 
410
433
 
411
434
  def _is_agentic_devtools_distribution_name(name):
412
435
  """Return True for agentic-devtools distribution names and pip backups."""
413
- if _is_agentic_devtools_tilde_backup(name):
436
+ name_lower = name.lower()
437
+ if _is_agentic_devtools_tilde_backup(name_lower):
414
438
  return True
415
439
 
416
440
  for candidate in ("agentic-devtools", "agentic_devtools"):
417
- if name == candidate:
441
+ if name_lower == candidate:
418
442
  return True
419
- if not name.startswith(candidate):
443
+ if not name_lower.startswith(candidate):
420
444
  continue
421
- remainder = name[len(candidate) :]
445
+ remainder = name_lower[len(candidate) :]
422
446
  if _has_distribution_suffix(remainder):
423
447
  return True
424
448
  return False
@@ -482,9 +506,24 @@ def _install_package():
482
506
  """Install/upgrade agentic-devtools from PyPI.
483
507
 
484
508
  Returns ``(success, self_upgrade_lock)``. ``self_upgrade_lock`` is True
485
- when the failure was Windows refusing to replace the console script of the
486
- running installation, which is recoverable.
509
+ in two cases:
510
+ 1. **Proactive skip** — running on Windows as the ``agdt-setup.exe``
511
+ autorun executable (``AGDT_SETUP_AUTORUN`` is set). pip is not
512
+ invoked; the return is immediate to prevent WinError 32 from
513
+ corrupting package metadata.
514
+ 2. **Reactive detection** — pip was invoked but failed with WinError 32
515
+ (Windows refused to replace the locked console-script executable).
516
+ In both cases ``success`` is False. Only the proactive skip guarantees
517
+ the installed version remains intact; the reactive path runs after pip
518
+ has already attempted the uninstall.
487
519
  """
520
+ import os
521
+ import sys
522
+ if sys.platform == "win32" and os.environ.get("AGDT_SETUP_AUTORUN"):
523
+ # Proactively skip pip install when spawned by agdt-setup.exe to avoid WinError 32
524
+ # which corrupts metadata before failing.
525
+ return False, True
526
+
488
527
  stdout_chunks = []
489
528
  stderr_chunks = []
490
529
  process = subprocess.Popen(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentic-devtools
3
- Version: 0.2.413
3
+ Version: 0.2.415
4
4
  Summary: Agentic devtools integrate Jira, DevOps & more
5
5
  Author: ayaiayorg
6
6
  License-Expression: MIT
@@ -1,5 +1,5 @@
1
1
  agentic_devtools/__init__.py,sha256=J_Zw_vWKghk-cLmqI83hXQmSiS8zMhGIHM5WPLDkZuo,242
2
- agentic_devtools/_version.py,sha256=rvjWAdv1XJQdgz4L2djb0ya1yGUUgYGGW5VkxjGa2_g,524
2
+ agentic_devtools/_version.py,sha256=yIi8ocqIsuh2cqu1EnYhgjeImc0-nP0OgjnFW9ZTHzw,524
3
3
  agentic_devtools/agdt_gitignore.py,sha256=aBPBQe7M0GLH8NIp1NsyN9ZiO80fNGOi46IcT5A4SK4,1569
4
4
  agentic_devtools/background_tasks.py,sha256=IVC1XJKQzPBP8wCRNCZX_iZCg9FJY_GBUzddSJj7xqw,17473
5
5
  agentic_devtools/config.py,sha256=DEVxTVZhsQbVQwO9qdB_1h19aBpjMzF6xJY_ePYLGLs,15629
@@ -127,7 +127,7 @@ agentic_devtools/cli/checks/__init__.py,sha256=TOccSqgwCTfvGAc39phAOJaAvRLn6AUUB
127
127
  agentic_devtools/cli/checks/__main__.py,sha256=J5ilhApBdIVr2hZIB7gki42P0AOxLDVQJPoGi_-vTEo,201
128
128
  agentic_devtools/cli/checks/changed_files.py,sha256=b3XjEBGVYcKQdXQtbYMBmEkT9uhKZGTXswESMBMjUzU,7255
129
129
  agentic_devtools/cli/checks/commands.py,sha256=oLVwaOs_baFuzaj6ApIZEbQQwBOdeBYwxr-iF-BwabM,23421
130
- agentic_devtools/cli/checks/customization_quality.py,sha256=pX2OJQyKwk-emlWDdDvX9BeyGp4JVweLZpew45N-Qvo,40135
130
+ agentic_devtools/cli/checks/customization_quality.py,sha256=33kztmISilTk9w2PEj8YH9FTfVip20r0ZZh8K0WNshE,47638
131
131
  agentic_devtools/cli/checks/lint.py,sha256=HP5lYUNzEX15QI_s7-q-C-s142CwySg4k4PoPtC97t4,7932
132
132
  agentic_devtools/cli/checks/setup_drift.py,sha256=cGJBccYBiAEs6r6ER274fBwoN1FpL-EDwLsdoCcmU7o,7665
133
133
  agentic_devtools/cli/checks/setup_drift_changed_files.py,sha256=EmWvKdOSATzz7f6cKMwxiEblYvJy_2lTy7ypYza8SoE,3721
@@ -187,7 +187,7 @@ agentic_devtools/cli/ci/pipeline/actions/merge.py,sha256=MPQxTMaDWpeZBzge2tx8htC
187
187
  agentic_devtools/cli/ci/pipeline/actions/publish.py,sha256=A00Lk-vZKnhOCN2O5mkks0tayr7NA5eZfUA95CUP7gs,3990
188
188
  agentic_devtools/cli/ci/pipeline/actions/rebase.py,sha256=FGykjl5xi9Bg8nq2AE6IB7skdmKJ7Z4iOT4bd2K3B2U,5256
189
189
  agentic_devtools/cli/ci/pipeline/actions/request_review.py,sha256=-BInEFbX7rHY_wKna-ksa5cQV_Arxi7w1Jg1kNdBltE,9173
190
- agentic_devtools/cli/ci/pipeline/actions/resolve_threads.py,sha256=EU04VIjS8A71khgYCUwCX7cxr6m3CIJqTis-a6yWl6Q,9131
190
+ agentic_devtools/cli/ci/pipeline/actions/resolve_threads.py,sha256=orY7RsqrMsvOmGDa7R-Z96itSFj9N5DICertZdekwOo,9687
191
191
  agentic_devtools/cli/ci/pipeline/actions/squash.py,sha256=woloutgBoaEkZMO6HcLoChCV3S-yeD7T22ygePciRx0,10894
192
192
  agentic_devtools/cli/ci/pipeline/actions/takeover.py,sha256=ms0coVGuT7peZ3XKbYzqzwrAwqwyyvfPzCsPXLPXKws,6394
193
193
  agentic_devtools/cli/ci/pipeline/discovery/__init__.py,sha256=QNdOYvl_wK6qcWOvNJR-wm8EtWD_DPn4barKF5RSmfU,752
@@ -340,7 +340,7 @@ agentic_devtools/cli/setup/phase_markers.py,sha256=cFiQoa8Ydbsb60RVu1chtW1FazOh3
340
340
  agentic_devtools/cli/setup/phases.py,sha256=h_Czv3QH6rttoguVOFo5dnq1j1E7OfpvoNi0nvyEDnI,571
341
341
  agentic_devtools/cli/setup/platform_detection.py,sha256=uEw6z44J1y85F1XLTWtnMIRRdZ-vWkI9iwWU_BzGA_4,15587
342
342
  agentic_devtools/cli/setup/post_autorun_version_check.py,sha256=oqjp5Q5SnUfmrqubsYYnPD9gasviaoW7pry44Fpyxx0,3529
343
- agentic_devtools/cli/setup/pr_workflow.py,sha256=Y3U8kS1J2ILaTizujCqQ3kHwZx3NfwzAg6ly9Gaxplo,22202
343
+ agentic_devtools/cli/setup/pr_workflow.py,sha256=PdgqhMMQu9YTGa0a1fWIsMffxbeUYrElafJP4vhaLkY,24083
344
344
  agentic_devtools/cli/setup/property_change_detection.py,sha256=hjmgjy6OsX0z-1QPhNfQQ18PsW3ghAMgEzKu9ciPjnI,10588
345
345
  agentic_devtools/cli/setup/refresh_outcome.py,sha256=FhMI-vmRl2LbEtbyKu8DtN98ix8IyA3eu8r1PXz-uPU,3436
346
346
  agentic_devtools/cli/setup/registry.py,sha256=aSNcx4CtVEOl4qT8c7wu7iPG9YxrIatjFETK8JaUY-A,16692
@@ -360,7 +360,7 @@ agentic_devtools/cli/setup/script_generators/constants.py,sha256=BkpLmENaPkfYJUK
360
360
  agentic_devtools/cli/setup/script_generators/gitignore_updater.py,sha256=RbpWdUCK7lAMfazVm6ytgVIZalbkMre6dzStQ7uEIoQ,4185
361
361
  agentic_devtools/cli/setup/script_generators/legacy_migration.py,sha256=eFVKlkgQGVwYl8uXIJhxBB59sLQVeUAZi6Xsl8PNXY8,3075
362
362
  agentic_devtools/cli/setup/script_generators/repo_specific.py,sha256=_xaZTOYNWKInValGMwJBqGvlRthip5R7oZBPsyWIU1I,1498
363
- agentic_devtools/cli/setup/script_generators/required_setup.py,sha256=8CSBcwygYPKEIRx7yHsRNDLXvfNU2h0H-J3ggz03i7s,22490
363
+ agentic_devtools/cli/setup/script_generators/required_setup.py,sha256=0sdiCxEKSnEC2K1HYTcqI2SgIWFc87G7ya1hfa6fCGI,24546
364
364
  agentic_devtools/cli/setup/script_generators/root_entry_point.py,sha256=UzW1N_ln_EvM28EmxOiBkxxis5QU2CiputAEZm2NwIE,4311
365
365
  agentic_devtools/cli/setup/templates/README.md,sha256=Y_Kw_0CeHzUM6xcckVXT9UcNAybr9XM_BI1WkyW9WF4,869
366
366
  agentic_devtools/cli/setup/templates/review-pr.py,sha256=XXZjDfJViByAy0RQP5WB1fUIK8qxNRDo_9ZlssKk10A,3456
@@ -949,8 +949,8 @@ agentic_devtools/_bundled_skills/prompts/speckit.plan.prompt.md,sha256=IJja5r2Sd
949
949
  agentic_devtools/_bundled_skills/prompts/speckit.specify.prompt.md,sha256=eyzE3GRi2hyW30a6xPYOU7q6MJf0skrD-baEGURYqpg,31
950
950
  agentic_devtools/_bundled_skills/prompts/speckit.tasks.prompt.md,sha256=iPxXwon5nV6dNcJV8-JoP3PssKUVXctNiG-C9SsRhB8,29
951
951
  agentic_devtools/_bundled_skills/prompts/speckit.taskstoissues.prompt.md,sha256=L5Y21PMSoUcPAAdHy2Jnf-wGVdi04jV_pPvyOJZfpm0,37
952
- agentic_devtools-0.2.413.dist-info/METADATA,sha256=1N2e2_UOK6srGlV22hZ3SvKzeCoqsngmSBrAj1a4rDg,33466
953
- agentic_devtools-0.2.413.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
954
- agentic_devtools-0.2.413.dist-info/entry_points.txt,sha256=1xH6yqltFD5Gs3qbHN9zpKwwG3nXpWoC7x0w1Ip7LCw,11726
955
- agentic_devtools-0.2.413.dist-info/licenses/LICENSE,sha256=yBEDdICksxhBYLWoERKp9MTqwGnUF6Ryj9BTLwXTc6k,1082
956
- agentic_devtools-0.2.413.dist-info/RECORD,,
952
+ agentic_devtools-0.2.415.dist-info/METADATA,sha256=0YFc2rqKU6aEqndgc0b1TPrbskguyu3vIIraNawyBCg,33466
953
+ agentic_devtools-0.2.415.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
954
+ agentic_devtools-0.2.415.dist-info/entry_points.txt,sha256=1xH6yqltFD5Gs3qbHN9zpKwwG3nXpWoC7x0w1Ip7LCw,11726
955
+ agentic_devtools-0.2.415.dist-info/licenses/LICENSE,sha256=yBEDdICksxhBYLWoERKp9MTqwGnUF6Ryj9BTLwXTc6k,1082
956
+ agentic_devtools-0.2.415.dist-info/RECORD,,