aiwcli 0.9.7 → 0.10.0
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.
- package/bin/run.js +5 -2
- package/dist/lib/claude-settings-types.d.ts +2 -0
- package/dist/templates/CLAUDE.md +49 -18
- package/dist/templates/_shared/.claude/settings.json +4 -0
- package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/archive_plan.py +87 -178
- package/dist/templates/_shared/hooks/context_monitor.py +128 -194
- package/dist/templates/_shared/hooks/file-suggestion.py +26 -23
- package/dist/templates/_shared/hooks/pre_compact.py +104 -0
- package/dist/templates/_shared/hooks/session_end.py +154 -0
- package/dist/templates/_shared/hooks/session_start.py +145 -59
- package/dist/templates/_shared/hooks/task_create_capture.py +26 -49
- package/dist/templates/_shared/hooks/task_update_capture.py +42 -100
- package/dist/templates/_shared/hooks/user_prompt_submit.py +63 -77
- package/dist/templates/_shared/lib/base/__init__.py +16 -0
- package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/constants.py +18 -4
- package/dist/templates/_shared/lib/base/hook_utils.py +199 -11
- package/dist/templates/_shared/lib/base/inference.py +121 -0
- package/dist/templates/_shared/lib/base/logger.py +291 -0
- package/dist/templates/_shared/lib/base/utils.py +49 -11
- package/dist/templates/_shared/lib/context/__init__.py +72 -80
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/context_formatter.py +316 -0
- package/dist/templates/_shared/lib/context/context_selector.py +491 -0
- package/dist/templates/_shared/lib/context/context_store.py +636 -0
- package/dist/templates/_shared/lib/context/plan_manager.py +204 -0
- package/dist/templates/_shared/lib/context/task_tracker.py +188 -0
- package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/document_generator.py +14 -40
- package/dist/templates/_shared/lib/templates/README.md +5 -13
- package/dist/templates/_shared/lib/templates/__init__.py +2 -6
- package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/plan_context.py +25 -79
- package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/save_handoff.py +39 -19
- package/dist/templates/_shared/scripts/status_line.py +701 -0
- package/dist/templates/_shared/workflows/handoff.md +9 -3
- package/dist/templates/cc-native/.claude/settings.json +64 -9
- package/dist/templates/cc-native/CC-NATIVE-README.md +25 -28
- package/dist/templates/cc-native/MIGRATION.md +1 -1
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +14 -39
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +1 -1
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +57 -22
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +57 -57
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +208 -158
- package/dist/templates/cc-native/_cc-native/hooks/plan_accepted.py +127 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +81 -0
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +26 -25
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +35 -10
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/debug.py +37 -22
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +103 -42
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +26 -21
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +12 -7
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +12 -7
- package/dist/templates/cc-native/_cc-native/lib/state.py +31 -16
- package/dist/templates/cc-native/_cc-native/lib/utils.py +210 -43
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +1 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/templates/_shared/hooks/context_enforcer.py +0 -625
- package/dist/templates/_shared/hooks/task_create_atomicity.py +0 -205
- package/dist/templates/_shared/lib/context/cache.py +0 -444
- package/dist/templates/_shared/lib/context/context_extractor.py +0 -115
- package/dist/templates/_shared/lib/context/context_manager.py +0 -1054
- package/dist/templates/_shared/lib/context/discovery.py +0 -444
- package/dist/templates/_shared/lib/context/event_log.py +0 -308
- package/dist/templates/_shared/lib/context/plan_archive.py +0 -101
- package/dist/templates/_shared/lib/context/task_sync.py +0 -290
- package/dist/templates/_shared/lib/templates/persona_questions.py +0 -113
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-agent-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/test_permission_request.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/async_archive.py +0 -68
- package/dist/templates/cc-native/_cc-native/lib/atomic_write.py +0 -98
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"""Persona-based question templates for plan clarification.
|
|
2
|
-
|
|
3
|
-
Uses distinct reasoning lenses to surface hidden constraints and assumptions.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
from typing import List, Dict
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass
|
|
11
|
-
class PersonaQuestion:
|
|
12
|
-
"""A clarifying question from a specific persona lens."""
|
|
13
|
-
|
|
14
|
-
persona: str
|
|
15
|
-
display_name: str
|
|
16
|
-
question: str
|
|
17
|
-
purpose: str
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
CLARIFICATION_PERSONAS: Dict[str, List[PersonaQuestion]] = {
|
|
21
|
-
"problem_validator": [
|
|
22
|
-
PersonaQuestion(
|
|
23
|
-
persona="problem_validator",
|
|
24
|
-
display_name="Questioning the Problem",
|
|
25
|
-
question="Can you describe the problem you're trying to solve without mentioning the solution?",
|
|
26
|
-
purpose="Separates problem from solution to check alignment",
|
|
27
|
-
),
|
|
28
|
-
PersonaQuestion(
|
|
29
|
-
persona="problem_validator",
|
|
30
|
-
display_name="Challenging the Approach",
|
|
31
|
-
question="What's the simplest possible way to achieve this outcome that we haven't considered?",
|
|
32
|
-
purpose="Identifies potential over-engineering",
|
|
33
|
-
),
|
|
34
|
-
],
|
|
35
|
-
"assumption_validator": [
|
|
36
|
-
PersonaQuestion(
|
|
37
|
-
persona="assumption_validator",
|
|
38
|
-
display_name="Surfacing Assumptions",
|
|
39
|
-
question="What must already be true about your users, systems, or constraints for this to succeed?",
|
|
40
|
-
purpose="Surfaces foundational assumptions that could invalidate the plan",
|
|
41
|
-
),
|
|
42
|
-
PersonaQuestion(
|
|
43
|
-
persona="assumption_validator",
|
|
44
|
-
display_name="Hidden Dependencies",
|
|
45
|
-
question="What are you assuming 'everyone knows' about this problem that might not be documented?",
|
|
46
|
-
purpose="Uncovers implicit knowledge that needs to be made explicit",
|
|
47
|
-
),
|
|
48
|
-
],
|
|
49
|
-
"user_advocate": [
|
|
50
|
-
PersonaQuestion(
|
|
51
|
-
persona="user_advocate",
|
|
52
|
-
display_name="Understanding Users",
|
|
53
|
-
question="Who specifically will use this, and what problem does it solve for them today?",
|
|
54
|
-
purpose="Grounds the plan in actual user needs",
|
|
55
|
-
),
|
|
56
|
-
PersonaQuestion(
|
|
57
|
-
persona="user_advocate",
|
|
58
|
-
display_name="Impact Assessment",
|
|
59
|
-
question="If we did nothing, what would happen? Who would be affected?",
|
|
60
|
-
purpose="Establishes urgency and stakes",
|
|
61
|
-
),
|
|
62
|
-
],
|
|
63
|
-
"tradeoff_illuminator": [
|
|
64
|
-
PersonaQuestion(
|
|
65
|
-
persona="tradeoff_illuminator",
|
|
66
|
-
display_name="Revealing Trade-offs",
|
|
67
|
-
question="What are you willing to sacrifice (scope, time, quality, features) to make this work?",
|
|
68
|
-
purpose="Forces explicit prioritization",
|
|
69
|
-
),
|
|
70
|
-
PersonaQuestion(
|
|
71
|
-
persona="tradeoff_illuminator",
|
|
72
|
-
display_name="Foreclosed Options",
|
|
73
|
-
question="What becomes harder or impossible to do later if we proceed this way?",
|
|
74
|
-
purpose="Surfaces opportunity costs and lock-in risks",
|
|
75
|
-
),
|
|
76
|
-
],
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def get_all_persona_questions() -> List[PersonaQuestion]:
|
|
81
|
-
"""Get all persona questions as a flat list."""
|
|
82
|
-
questions = []
|
|
83
|
-
for persona_qs in CLARIFICATION_PERSONAS.values():
|
|
84
|
-
questions.extend(persona_qs)
|
|
85
|
-
return questions
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def format_questions_for_prompt() -> str:
|
|
89
|
-
"""Format persona questions for injection into Claude prompt."""
|
|
90
|
-
lines = [
|
|
91
|
-
"### Persona-Based Clarifying Questions",
|
|
92
|
-
"",
|
|
93
|
-
"Ask 5-8 questions from these perspectives using AskUserQuestion:",
|
|
94
|
-
"",
|
|
95
|
-
]
|
|
96
|
-
|
|
97
|
-
for persona_qs in CLARIFICATION_PERSONAS.values():
|
|
98
|
-
for q in persona_qs:
|
|
99
|
-
lines.append(f"**{q.display_name}**")
|
|
100
|
-
lines.append(f'- Q: "{q.question}"')
|
|
101
|
-
lines.append(f"- Purpose: {q.purpose}")
|
|
102
|
-
lines.append("")
|
|
103
|
-
|
|
104
|
-
lines.extend(
|
|
105
|
-
[
|
|
106
|
-
"**Guidance:**",
|
|
107
|
-
"- Select questions most relevant to THIS plan (skip if already answered)",
|
|
108
|
-
"- Ask one at a time with clear context",
|
|
109
|
-
"- Use answers to refine the plan before ExitPlanMode",
|
|
110
|
-
]
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
return "\n".join(lines)
|
|
Binary file
|
package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-agent-review.cpython-313.pyc
DELETED
|
Binary file
|
|
Binary file
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"""Async background archival to avoid blocking user workflow."""
|
|
2
|
-
import threading
|
|
3
|
-
import json
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import Dict, Any, Callable, Optional
|
|
6
|
-
try:
|
|
7
|
-
from .atomic_write import atomic_write
|
|
8
|
-
from .constants import ENABLE_ROBUST_PLAN_WRITES
|
|
9
|
-
except ImportError:
|
|
10
|
-
# When imported directly via sys.path (not as a package)
|
|
11
|
-
from atomic_write import atomic_write
|
|
12
|
-
from constants import ENABLE_ROBUST_PLAN_WRITES
|
|
13
|
-
|
|
14
|
-
def archive_plan_async(
|
|
15
|
-
out_path: Path,
|
|
16
|
-
header: str,
|
|
17
|
-
plan: str,
|
|
18
|
-
callback: Optional[Callable] = None
|
|
19
|
-
) -> None:
|
|
20
|
-
"""
|
|
21
|
-
Archive plan in background thread. Non-blocking.
|
|
22
|
-
|
|
23
|
-
Args:
|
|
24
|
-
out_path: Destination file path
|
|
25
|
-
header: Plan header with metadata
|
|
26
|
-
plan: Plan content
|
|
27
|
-
callback: Optional callback(success: bool, error: str) on completion
|
|
28
|
-
"""
|
|
29
|
-
if not ENABLE_ROBUST_PLAN_WRITES:
|
|
30
|
-
# Legacy behavior - write directly
|
|
31
|
-
try:
|
|
32
|
-
out_path.write_text(header + plan + "\n", encoding="utf-8")
|
|
33
|
-
if callback:
|
|
34
|
-
callback(True, None)
|
|
35
|
-
except Exception as e:
|
|
36
|
-
if callback:
|
|
37
|
-
callback(False, str(e))
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
def _archive_worker():
|
|
41
|
-
success, error = atomic_write(out_path, header + plan + "\n")
|
|
42
|
-
|
|
43
|
-
if not success:
|
|
44
|
-
# Write sanitized error marker (no stack traces)
|
|
45
|
-
error_marker = out_path.with_suffix('.error')
|
|
46
|
-
error_content = f"Archive failed: {error}\n"
|
|
47
|
-
|
|
48
|
-
try:
|
|
49
|
-
# Use atomic write for error marker too
|
|
50
|
-
atomic_write(
|
|
51
|
-
error_marker,
|
|
52
|
-
error_content,
|
|
53
|
-
max_attempts=1 # Don't retry error marker
|
|
54
|
-
)
|
|
55
|
-
except Exception:
|
|
56
|
-
pass # Error marker is best-effort
|
|
57
|
-
|
|
58
|
-
if callback:
|
|
59
|
-
try:
|
|
60
|
-
callback(success, error)
|
|
61
|
-
except Exception as e:
|
|
62
|
-
# Log callback failures (daemon thread would otherwise swallow)
|
|
63
|
-
import sys
|
|
64
|
-
print(f"[async_archive] Callback failed: {e}", file=sys.stderr)
|
|
65
|
-
|
|
66
|
-
# Start background thread
|
|
67
|
-
thread = threading.Thread(target=_archive_worker, daemon=False)
|
|
68
|
-
thread.start()
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
"""Cross-platform atomic file writes with security."""
|
|
2
|
-
import os
|
|
3
|
-
import sys
|
|
4
|
-
import tempfile
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Optional
|
|
7
|
-
|
|
8
|
-
if sys.platform == 'win32':
|
|
9
|
-
import ctypes
|
|
10
|
-
from ctypes import wintypes
|
|
11
|
-
|
|
12
|
-
# Windows MoveFileEx flags
|
|
13
|
-
MOVEFILE_REPLACE_EXISTING = 0x1
|
|
14
|
-
MOVEFILE_WRITE_THROUGH = 0x8
|
|
15
|
-
|
|
16
|
-
def _atomic_replace_windows(src: Path, dst: Path) -> None:
|
|
17
|
-
"""Atomic file replacement on Windows using MoveFileEx."""
|
|
18
|
-
kernel32 = ctypes.windll.kernel32
|
|
19
|
-
|
|
20
|
-
# Set proper function prototypes for 64-bit safety
|
|
21
|
-
kernel32.MoveFileExW.argtypes = [wintypes.LPCWSTR, wintypes.LPCWSTR, wintypes.DWORD]
|
|
22
|
-
kernel32.MoveFileExW.restype = wintypes.BOOL
|
|
23
|
-
|
|
24
|
-
result = kernel32.MoveFileExW(
|
|
25
|
-
str(src),
|
|
26
|
-
str(dst),
|
|
27
|
-
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH
|
|
28
|
-
)
|
|
29
|
-
if not result:
|
|
30
|
-
error_code = kernel32.GetLastError()
|
|
31
|
-
# Use ctypes.WinError for human-readable error messages
|
|
32
|
-
raise ctypes.WinError(error_code)
|
|
33
|
-
|
|
34
|
-
def atomic_write(
|
|
35
|
-
path: Path,
|
|
36
|
-
content: str,
|
|
37
|
-
max_attempts: int = 2,
|
|
38
|
-
backoff_ms: list = None
|
|
39
|
-
) -> tuple:
|
|
40
|
-
"""
|
|
41
|
-
Write file atomically with retry logic.
|
|
42
|
-
|
|
43
|
-
Returns:
|
|
44
|
-
(success: bool, error_message: Optional[str])
|
|
45
|
-
"""
|
|
46
|
-
import time
|
|
47
|
-
|
|
48
|
-
if backoff_ms is None:
|
|
49
|
-
backoff_ms = [500, 1000]
|
|
50
|
-
|
|
51
|
-
for attempt in range(max_attempts):
|
|
52
|
-
try:
|
|
53
|
-
# Create temp file in same directory for atomic rename
|
|
54
|
-
temp_fd, temp_path_str = tempfile.mkstemp(
|
|
55
|
-
dir=path.parent,
|
|
56
|
-
prefix=f".{path.stem}_",
|
|
57
|
-
suffix=".tmp"
|
|
58
|
-
)
|
|
59
|
-
temp_path = Path(temp_path_str)
|
|
60
|
-
|
|
61
|
-
try:
|
|
62
|
-
# Write content to temp file
|
|
63
|
-
with os.fdopen(temp_fd, 'w', encoding='utf-8') as f:
|
|
64
|
-
f.write(content)
|
|
65
|
-
f.flush()
|
|
66
|
-
os.fsync(f.fileno()) # Force write to disk
|
|
67
|
-
|
|
68
|
-
# Set restrictive permissions before rename (chmod 600)
|
|
69
|
-
os.chmod(temp_path, 0o600)
|
|
70
|
-
|
|
71
|
-
# Platform-specific atomic rename
|
|
72
|
-
if sys.platform == 'win32':
|
|
73
|
-
_atomic_replace_windows(temp_path, path)
|
|
74
|
-
else:
|
|
75
|
-
temp_path.replace(path) # POSIX atomic
|
|
76
|
-
|
|
77
|
-
return (True, None)
|
|
78
|
-
|
|
79
|
-
except Exception as e:
|
|
80
|
-
# Clean up temp file on failure
|
|
81
|
-
try:
|
|
82
|
-
temp_path.unlink()
|
|
83
|
-
except Exception:
|
|
84
|
-
pass # Cleanup is best-effort
|
|
85
|
-
raise
|
|
86
|
-
|
|
87
|
-
except Exception as e:
|
|
88
|
-
if attempt < max_attempts - 1:
|
|
89
|
-
# Bounds-safe backoff indexing
|
|
90
|
-
wait_ms = backoff_ms[min(attempt, len(backoff_ms) - 1)]
|
|
91
|
-
time.sleep(wait_ms / 1000.0)
|
|
92
|
-
else:
|
|
93
|
-
# Sanitize error message (no paths, no stack trace)
|
|
94
|
-
error_type = type(e).__name__
|
|
95
|
-
error_msg = str(e).split('\n')[0][:200] # First line only, max 200 chars
|
|
96
|
-
return (False, f"{error_type}: {error_msg}")
|
|
97
|
-
|
|
98
|
-
return (False, "Max retry attempts exceeded")
|