atdd 0.3.2__py3-none-any.whl → 0.3.3__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.
atdd/cli.py CHANGED
@@ -45,7 +45,7 @@ from atdd.coach.commands.session import SessionManager
45
45
  from atdd.coach.commands.sync import AgentConfigSync
46
46
  from atdd.coach.commands.gate import ATDDGate
47
47
  from atdd.coach.utils.repo import find_repo_root
48
- from atdd.version_check import print_update_notice
48
+ from atdd.version_check import print_update_notice, print_upgrade_sync_notice
49
49
 
50
50
 
51
51
  class ATDDCoach:
@@ -436,10 +436,14 @@ Phase descriptions:
436
436
 
437
437
 
438
438
  def cli() -> int:
439
- """CLI entry point with version check."""
439
+ """CLI entry point with version and upgrade checks."""
440
+ # Check if repo needs sync after ATDD upgrade (at startup)
441
+ print_upgrade_sync_notice()
442
+
440
443
  try:
441
444
  result = main()
442
445
  finally:
446
+ # Check for newer versions on PyPI (at end)
443
447
  print_update_notice()
444
448
  return result
445
449
 
@@ -160,11 +160,21 @@ class ProjectInitializer:
160
160
  print(f"Config already exists: {self.config_file}")
161
161
  return
162
162
 
163
+ # Get installed ATDD version
164
+ try:
165
+ from atdd import __version__
166
+ toolkit_version = __version__
167
+ except ImportError:
168
+ toolkit_version = "0.0.0"
169
+
163
170
  config = {
164
171
  "version": "1.0",
165
172
  "sync": {
166
173
  "agents": ["claude"], # Default: only Claude
167
174
  },
175
+ "toolkit": {
176
+ "last_version": toolkit_version, # Track installed version
177
+ },
168
178
  }
169
179
 
170
180
  with open(self.config_file, "w") as f:
@@ -115,6 +115,13 @@ class AgentConfigSync:
115
115
  unchanged_count += 1
116
116
 
117
117
  print(f"\nSync complete: {synced_count} updated, {unchanged_count} unchanged")
118
+
119
+ # Update toolkit.last_version to mark sync complete
120
+ from atdd.version_check import update_toolkit_version
121
+ if update_toolkit_version(self.config_file):
122
+ from atdd import __version__
123
+ print(f"Updated toolkit.last_version to {__version__}")
124
+
118
125
  return 0
119
126
 
120
127
  def verify(self) -> int:
atdd/version_check.py CHANGED
@@ -1,11 +1,13 @@
1
1
  """
2
2
  Version check for ATDD CLI.
3
3
 
4
- Checks PyPI for newer versions and notifies users. Uses a cached check
5
- to avoid adding latency to every command.
4
+ Two types of version checks:
5
+ 1. PyPI update check - notifies when a newer version is available on PyPI
6
+ 2. Repo sync check - notifies when installed version is newer than repo's last_version
6
7
 
7
8
  Cache location: ~/.atdd/version_cache.json
8
- Disable: Set ATDD_NO_UPDATE_CHECK=1 environment variable
9
+ Disable PyPI check: Set ATDD_NO_UPDATE_CHECK=1
10
+ Disable sync reminder: Set ATDD_NO_UPGRADE_NOTICE=1
9
11
  """
10
12
  import json
11
13
  import os
@@ -16,6 +18,8 @@ from typing import Optional, Tuple
16
18
  from urllib.request import urlopen
17
19
  from urllib.error import URLError
18
20
 
21
+ import yaml
22
+
19
23
  from atdd import __version__
20
24
 
21
25
  # Check once per day (86400 seconds)
@@ -124,3 +128,107 @@ def print_update_notice() -> None:
124
128
  print(notice, file=sys.stderr)
125
129
  except Exception:
126
130
  pass # Never fail the main command due to version check
131
+
132
+
133
+ # --- Repo sync upgrade check ---
134
+
135
+ def _load_repo_config() -> Tuple[Optional[dict], Optional[Path]]:
136
+ """
137
+ Load .atdd/config.yaml from current directory.
138
+
139
+ Returns:
140
+ Tuple of (config_dict, config_path) or (None, None) if not found.
141
+ """
142
+ config_path = Path.cwd() / ".atdd" / "config.yaml"
143
+ if not config_path.exists():
144
+ return None, None
145
+
146
+ try:
147
+ with open(config_path) as f:
148
+ return yaml.safe_load(f) or {}, config_path
149
+ except (yaml.YAMLError, OSError):
150
+ return None, None
151
+
152
+
153
+ def _get_last_toolkit_version(config: dict) -> Optional[str]:
154
+ """Extract toolkit.last_version from config."""
155
+ toolkit = config.get("toolkit", {})
156
+ return toolkit.get("last_version")
157
+
158
+
159
+ def check_upgrade_sync_needed() -> Optional[str]:
160
+ """
161
+ Check if repo needs sync after ATDD upgrade.
162
+
163
+ Compares installed version vs toolkit.last_version in .atdd/config.yaml.
164
+
165
+ Returns:
166
+ Message to display if sync needed, None otherwise.
167
+ """
168
+ # Respect disable flag
169
+ if os.environ.get("ATDD_NO_UPGRADE_NOTICE", "").lower() in ("1", "true", "yes"):
170
+ return None
171
+
172
+ # Skip if running in development
173
+ if __version__ == "0.0.0":
174
+ return None
175
+
176
+ config, config_path = _load_repo_config()
177
+ if config is None:
178
+ # No .atdd/config.yaml - not an ATDD repo or not initialized
179
+ return None
180
+
181
+ last_version = _get_last_toolkit_version(config)
182
+ if last_version is None:
183
+ # First run or old config without toolkit.last_version
184
+ # Treat as needing sync
185
+ return f"ATDD upgraded to {__version__}. Run: atdd sync"
186
+
187
+ # Compare versions
188
+ if _is_newer(__version__, last_version):
189
+ return f"ATDD upgraded ({last_version} → {__version__}). Run: atdd sync"
190
+
191
+ return None
192
+
193
+
194
+ def update_toolkit_version(config_path: Optional[Path] = None) -> bool:
195
+ """
196
+ Update toolkit.last_version in .atdd/config.yaml to current installed version.
197
+
198
+ Args:
199
+ config_path: Path to config file. Defaults to .atdd/config.yaml in cwd.
200
+
201
+ Returns:
202
+ True if updated, False otherwise.
203
+ """
204
+ if config_path is None:
205
+ config_path = Path.cwd() / ".atdd" / "config.yaml"
206
+
207
+ if not config_path.exists():
208
+ return False
209
+
210
+ try:
211
+ with open(config_path) as f:
212
+ config = yaml.safe_load(f) or {}
213
+
214
+ # Update toolkit.last_version
215
+ if "toolkit" not in config:
216
+ config["toolkit"] = {}
217
+ config["toolkit"]["last_version"] = __version__
218
+
219
+ with open(config_path, "w") as f:
220
+ yaml.dump(config, f, default_flow_style=False, sort_keys=False)
221
+
222
+ return True
223
+ except (yaml.YAMLError, OSError):
224
+ return False
225
+
226
+
227
+ def print_upgrade_sync_notice() -> None:
228
+ """Print upgrade sync notice to stderr if needed."""
229
+ try:
230
+ notice = check_upgrade_sync_needed()
231
+ if notice:
232
+ print(f"\n⚠️ {notice}\n", file=sys.stderr)
233
+ except Exception:
234
+ pass # Never fail the main command
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atdd
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: ATDD Platform - Acceptance Test Driven Development toolkit
5
5
  License: MIT
6
6
  Requires-Python: >=3.10
@@ -1,8 +1,8 @@
1
1
  atdd/__init__.py,sha256=-S8i9OahH-t9FJkPn6nprxipnjVum3rLeVsCS74T6eY,156
2
2
  atdd/__main__.py,sha256=B0sXDQLjFN9GowTlXo4NMWwPZPjDsrT8Frq7DnbdOD8,77
3
- atdd/cli.py,sha256=osZ_WFMbp3BFGgH-K7vApnoeulgeSU7-z6LB86-htnI,14534
3
+ atdd/cli.py,sha256=IiD7CpLEOSpgldgI55bc540cBGwKug_rUiih41aq5R4,14722
4
4
  atdd/conftest.py,sha256=Fj3kIhCETbj2QBCIjySBgdS3stKNRZcZzKTJr7A4LaQ,5300
5
- atdd/version_check.py,sha256=B9MbbxO_sJrEC3fxFJhTlOIkLLTRQCDO1_8ec1KvuWY,3540
5
+ atdd/version_check.py,sha256=BlPcLwzNnm457vWUolB3wMtZEI-fXvpzfm6p8U_j7rc,6684
6
6
  atdd/coach/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  atdd/coach/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  atdd/coach/commands/add_persistence_metadata.py,sha256=xAdeO9k53CIGTBhzNQbWenuzeoWKZimDBR9xBWaA3NU,6887
@@ -10,13 +10,13 @@ atdd/coach/commands/analyze_migrations.py,sha256=2bLfR7OwicBXAZWB4R3Sm4d5jFe87d0
10
10
  atdd/coach/commands/consumers.py,sha256=7vTexse8xznXSzNWjPGYuF4iJ8ZWymmOSkpcA6IWyxU,27514
11
11
  atdd/coach/commands/gate.py,sha256=_V2GypqoGixTs_kLWxFF3HgEt-Wi2r6Iv0YL75yWrWo,5829
12
12
  atdd/coach/commands/infer_governance_status.py,sha256=91-VnI64mOzijc1Cgkmr7cnNCir2-z2ITA2-SGwk3TU,4473
13
- atdd/coach/commands/initializer.py,sha256=Hli3hlL_aHnuDIzK0lHzE6KjG2QGvl2E2TvjmYoPPNE,6069
13
+ atdd/coach/commands/initializer.py,sha256=s7NnITF4c6xGlN6ZJVjyv4a4jxGMjK9OPG1C-OIG55A,6385
14
14
  atdd/coach/commands/interface.py,sha256=PPCwICFNN4ddPqucUATIiBrfEkDO66MZbYQkwNu6lm4,40459
15
15
  atdd/coach/commands/inventory.py,sha256=Xakb6U3SfnEcR7rhZ0Wg8SK5frg-oPE7C0fCvE-VoH4,20923
16
16
  atdd/coach/commands/migration.py,sha256=KlRXXM1O-ZqfwGOWxN5C_gxfTttbXUG_yq9pOLYsGnQ,8119
17
17
  atdd/coach/commands/registry.py,sha256=r1QWg841eK6bS4vEbYEviylDCpFkInUVMTsf5h4ArrQ,58344
18
18
  atdd/coach/commands/session.py,sha256=MhuWXd5TR6bB3w0t8vANeZx3L476qwLT6EUQMwg-wQA,14268
19
- atdd/coach/commands/sync.py,sha256=dA6BYsebTT1zOMXgea6DqawkvjU_N-JJJMFj9GP2onk,12259
19
+ atdd/coach/commands/sync.py,sha256=4HNzg8AVaePDwhyBlQcywvNVZDP5NIW8TBsEPbqmJuQ,12545
20
20
  atdd/coach/commands/test_interface.py,sha256=a7ut2Hhk0PnQ5LfJZkoQwfkfkVuB5OHA4QBwOS0-jcg,16870
21
21
  atdd/coach/commands/test_runner.py,sha256=Lutclc8Qr8BmLBvAYshXAjf8ynUM4eJgHF8GLPa1Usw,3929
22
22
  atdd/coach/commands/traceability.py,sha256=SYl0dRB_nQZJKe1IOCHxX193vaxOChVjnh6hcqcSOB0,163581
@@ -181,9 +181,9 @@ atdd/tester/validators/test_red_supabase_layer_structure.py,sha256=26cnzPZAwSFy0
181
181
  atdd/tester/validators/test_telemetry_structure.py,sha256=hIUnU2WU-8PNIg9EVHe2fnUdIQKIOUm5AWEtCBUXLVk,22467
182
182
  atdd/tester/validators/test_typescript_test_naming.py,sha256=E-TyGv_GVlTfsbyuxrtv9sOWSZS_QcpH6rrJFbWoeeU,11280
183
183
  atdd/tester/validators/test_typescript_test_structure.py,sha256=eV89SD1RaKtchBZupqhnJmaruoROosf3LwB4Fwe4UJI,2612
184
- atdd-0.3.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
185
- atdd-0.3.2.dist-info/METADATA,sha256=m-x-Bl0VySZE_g8MWapVUJ523yIBni8nTxCxR_WM18s,7081
186
- atdd-0.3.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
187
- atdd-0.3.2.dist-info/entry_points.txt,sha256=-C3yrA1WQQfN3iuGmSzPapA5cKVBEYU5Q1HUffSJTbY,38
188
- atdd-0.3.2.dist-info/top_level.txt,sha256=VKkf6Uiyrm4RS6ULCGM-v8AzYN8K2yg8SMqwJLoO-xs,5
189
- atdd-0.3.2.dist-info/RECORD,,
184
+ atdd-0.3.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
185
+ atdd-0.3.3.dist-info/METADATA,sha256=K0LQB_4qxlKQ4o7hmBpSDG2zMRoNVbI4QCUggzEb4ws,7081
186
+ atdd-0.3.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
187
+ atdd-0.3.3.dist-info/entry_points.txt,sha256=-C3yrA1WQQfN3iuGmSzPapA5cKVBEYU5Q1HUffSJTbY,38
188
+ atdd-0.3.3.dist-info/top_level.txt,sha256=VKkf6Uiyrm4RS6ULCGM-v8AzYN8K2yg8SMqwJLoO-xs,5
189
+ atdd-0.3.3.dist-info/RECORD,,
File without changes