atdd 0.2.1__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/__init__.py +6 -0
- atdd/__main__.py +4 -0
- atdd/cli.py +404 -0
- atdd/coach/__init__.py +0 -0
- atdd/coach/commands/__init__.py +0 -0
- atdd/coach/commands/add_persistence_metadata.py +215 -0
- atdd/coach/commands/analyze_migrations.py +188 -0
- atdd/coach/commands/consumers.py +720 -0
- atdd/coach/commands/infer_governance_status.py +149 -0
- atdd/coach/commands/initializer.py +177 -0
- atdd/coach/commands/interface.py +1078 -0
- atdd/coach/commands/inventory.py +565 -0
- atdd/coach/commands/migration.py +240 -0
- atdd/coach/commands/registry.py +1560 -0
- atdd/coach/commands/session.py +430 -0
- atdd/coach/commands/sync.py +405 -0
- atdd/coach/commands/test_interface.py +399 -0
- atdd/coach/commands/test_runner.py +141 -0
- atdd/coach/commands/tests/__init__.py +1 -0
- atdd/coach/commands/tests/test_telemetry_array_validation.py +235 -0
- atdd/coach/commands/traceability.py +4264 -0
- atdd/coach/conventions/session.convention.yaml +754 -0
- atdd/coach/overlays/__init__.py +2 -0
- atdd/coach/overlays/claude.md +2 -0
- atdd/coach/schemas/config.schema.json +34 -0
- atdd/coach/schemas/manifest.schema.json +101 -0
- atdd/coach/templates/ATDD.md +282 -0
- atdd/coach/templates/SESSION-TEMPLATE.md +327 -0
- atdd/coach/utils/__init__.py +0 -0
- atdd/coach/utils/graph/__init__.py +0 -0
- atdd/coach/utils/graph/urn.py +875 -0
- atdd/coach/validators/__init__.py +0 -0
- atdd/coach/validators/shared_fixtures.py +365 -0
- atdd/coach/validators/test_enrich_wagon_registry.py +167 -0
- atdd/coach/validators/test_registry.py +575 -0
- atdd/coach/validators/test_session_validation.py +1183 -0
- atdd/coach/validators/test_traceability.py +448 -0
- atdd/coach/validators/test_update_feature_paths.py +108 -0
- atdd/coach/validators/test_validate_contract_consumers.py +297 -0
- atdd/coder/__init__.py +1 -0
- atdd/coder/conventions/adapter.recipe.yaml +88 -0
- atdd/coder/conventions/backend.convention.yaml +460 -0
- atdd/coder/conventions/boundaries.convention.yaml +666 -0
- atdd/coder/conventions/commons.convention.yaml +460 -0
- atdd/coder/conventions/complexity.recipe.yaml +109 -0
- atdd/coder/conventions/component-naming.convention.yaml +178 -0
- atdd/coder/conventions/design.convention.yaml +327 -0
- atdd/coder/conventions/design.recipe.yaml +273 -0
- atdd/coder/conventions/dto.convention.yaml +660 -0
- atdd/coder/conventions/frontend.convention.yaml +542 -0
- atdd/coder/conventions/green.convention.yaml +1012 -0
- atdd/coder/conventions/presentation.convention.yaml +587 -0
- atdd/coder/conventions/refactor.convention.yaml +535 -0
- atdd/coder/conventions/technology.convention.yaml +206 -0
- atdd/coder/conventions/tests/__init__.py +0 -0
- atdd/coder/conventions/tests/test_adapter_recipe.py +302 -0
- atdd/coder/conventions/tests/test_complexity_recipe.py +289 -0
- atdd/coder/conventions/tests/test_component_taxonomy.py +278 -0
- atdd/coder/conventions/tests/test_component_urn_naming.py +165 -0
- atdd/coder/conventions/tests/test_thinness_recipe.py +286 -0
- atdd/coder/conventions/thinness.recipe.yaml +82 -0
- atdd/coder/conventions/train.convention.yaml +325 -0
- atdd/coder/conventions/verification.protocol.yaml +53 -0
- atdd/coder/schemas/design_system.schema.json +361 -0
- atdd/coder/validators/__init__.py +0 -0
- atdd/coder/validators/test_commons_structure.py +485 -0
- atdd/coder/validators/test_complexity.py +416 -0
- atdd/coder/validators/test_cross_language_consistency.py +431 -0
- atdd/coder/validators/test_design_system_compliance.py +413 -0
- atdd/coder/validators/test_dto_testing_patterns.py +268 -0
- atdd/coder/validators/test_green_cross_stack_layers.py +168 -0
- atdd/coder/validators/test_green_layer_dependencies.py +148 -0
- atdd/coder/validators/test_green_python_layer_structure.py +103 -0
- atdd/coder/validators/test_green_supabase_layer_structure.py +103 -0
- atdd/coder/validators/test_import_boundaries.py +396 -0
- atdd/coder/validators/test_init_file_urns.py +593 -0
- atdd/coder/validators/test_preact_layer_boundaries.py +221 -0
- atdd/coder/validators/test_presentation_convention.py +260 -0
- atdd/coder/validators/test_python_architecture.py +674 -0
- atdd/coder/validators/test_quality_metrics.py +420 -0
- atdd/coder/validators/test_station_master_pattern.py +244 -0
- atdd/coder/validators/test_train_infrastructure.py +454 -0
- atdd/coder/validators/test_train_urns.py +293 -0
- atdd/coder/validators/test_typescript_architecture.py +616 -0
- atdd/coder/validators/test_usecase_structure.py +421 -0
- atdd/coder/validators/test_wagon_boundaries.py +586 -0
- atdd/conftest.py +126 -0
- atdd/planner/__init__.py +1 -0
- atdd/planner/conventions/acceptance.convention.yaml +538 -0
- atdd/planner/conventions/appendix.convention.yaml +187 -0
- atdd/planner/conventions/artifact-naming.convention.yaml +852 -0
- atdd/planner/conventions/component.convention.yaml +670 -0
- atdd/planner/conventions/criteria.convention.yaml +141 -0
- atdd/planner/conventions/feature.convention.yaml +371 -0
- atdd/planner/conventions/interface.convention.yaml +382 -0
- atdd/planner/conventions/steps.convention.yaml +141 -0
- atdd/planner/conventions/train.convention.yaml +552 -0
- atdd/planner/conventions/wagon.convention.yaml +275 -0
- atdd/planner/conventions/wmbt.convention.yaml +258 -0
- atdd/planner/schemas/acceptance.schema.json +336 -0
- atdd/planner/schemas/appendix.schema.json +78 -0
- atdd/planner/schemas/component.schema.json +114 -0
- atdd/planner/schemas/feature.schema.json +197 -0
- atdd/planner/schemas/train.schema.json +192 -0
- atdd/planner/schemas/wagon.schema.json +281 -0
- atdd/planner/schemas/wmbt.schema.json +59 -0
- atdd/planner/validators/__init__.py +0 -0
- atdd/planner/validators/conftest.py +5 -0
- atdd/planner/validators/test_draft_wagon_registry.py +374 -0
- atdd/planner/validators/test_plan_cross_refs.py +240 -0
- atdd/planner/validators/test_plan_uniqueness.py +224 -0
- atdd/planner/validators/test_plan_urn_resolution.py +268 -0
- atdd/planner/validators/test_plan_wagons.py +174 -0
- atdd/planner/validators/test_train_validation.py +514 -0
- atdd/planner/validators/test_wagon_urn_chain.py +648 -0
- atdd/planner/validators/test_wmbt_consistency.py +327 -0
- atdd/planner/validators/test_wmbt_vocabulary.py +632 -0
- atdd/tester/__init__.py +1 -0
- atdd/tester/conventions/artifact.convention.yaml +257 -0
- atdd/tester/conventions/contract.convention.yaml +1009 -0
- atdd/tester/conventions/filename.convention.yaml +555 -0
- atdd/tester/conventions/migration.convention.yaml +509 -0
- atdd/tester/conventions/red.convention.yaml +797 -0
- atdd/tester/conventions/routing.convention.yaml +51 -0
- atdd/tester/conventions/telemetry.convention.yaml +458 -0
- atdd/tester/schemas/a11y.tmpl.json +17 -0
- atdd/tester/schemas/artifact.schema.json +189 -0
- atdd/tester/schemas/contract.schema.json +591 -0
- atdd/tester/schemas/contract.tmpl.json +95 -0
- atdd/tester/schemas/db.tmpl.json +20 -0
- atdd/tester/schemas/e2e.tmpl.json +17 -0
- atdd/tester/schemas/edge_function.tmpl.json +17 -0
- atdd/tester/schemas/event.tmpl.json +17 -0
- atdd/tester/schemas/http.tmpl.json +19 -0
- atdd/tester/schemas/job.tmpl.json +18 -0
- atdd/tester/schemas/load.tmpl.json +21 -0
- atdd/tester/schemas/metric.tmpl.json +19 -0
- atdd/tester/schemas/pack.schema.json +139 -0
- atdd/tester/schemas/realtime.tmpl.json +20 -0
- atdd/tester/schemas/rls.tmpl.json +18 -0
- atdd/tester/schemas/script.tmpl.json +16 -0
- atdd/tester/schemas/sec.tmpl.json +18 -0
- atdd/tester/schemas/storage.tmpl.json +18 -0
- atdd/tester/schemas/telemetry.schema.json +128 -0
- atdd/tester/schemas/telemetry_tracking_manifest.schema.json +143 -0
- atdd/tester/schemas/test_filename.schema.json +194 -0
- atdd/tester/schemas/test_intent.schema.json +179 -0
- atdd/tester/schemas/unit.tmpl.json +18 -0
- atdd/tester/schemas/visual.tmpl.json +18 -0
- atdd/tester/schemas/ws.tmpl.json +17 -0
- atdd/tester/utils/__init__.py +0 -0
- atdd/tester/utils/filename.py +300 -0
- atdd/tester/validators/__init__.py +0 -0
- atdd/tester/validators/cleanup_duplicate_headers.py +116 -0
- atdd/tester/validators/cleanup_duplicate_headers_v2.py +135 -0
- atdd/tester/validators/conftest.py +5 -0
- atdd/tester/validators/coverage_gap_report.py +321 -0
- atdd/tester/validators/fix_dual_ac_references.py +179 -0
- atdd/tester/validators/remove_duplicate_lines.py +93 -0
- atdd/tester/validators/test_acceptance_urn_filename_mapping.py +359 -0
- atdd/tester/validators/test_acceptance_urn_separator.py +166 -0
- atdd/tester/validators/test_artifact_naming_category.py +307 -0
- atdd/tester/validators/test_contract_schema_compliance.py +706 -0
- atdd/tester/validators/test_contracts_structure.py +200 -0
- atdd/tester/validators/test_coverage_adequacy.py +797 -0
- atdd/tester/validators/test_dual_ac_reference.py +225 -0
- atdd/tester/validators/test_fixture_validity.py +372 -0
- atdd/tester/validators/test_isolation.py +487 -0
- atdd/tester/validators/test_migration_coverage.py +204 -0
- atdd/tester/validators/test_migration_criteria.py +276 -0
- atdd/tester/validators/test_migration_generation.py +116 -0
- atdd/tester/validators/test_python_test_naming.py +410 -0
- atdd/tester/validators/test_red_layer_validation.py +95 -0
- atdd/tester/validators/test_red_python_layer_structure.py +87 -0
- atdd/tester/validators/test_red_supabase_layer_structure.py +90 -0
- atdd/tester/validators/test_telemetry_structure.py +634 -0
- atdd/tester/validators/test_typescript_test_naming.py +301 -0
- atdd/tester/validators/test_typescript_test_structure.py +84 -0
- atdd-0.2.1.dist-info/METADATA +221 -0
- atdd-0.2.1.dist-info/RECORD +184 -0
- atdd-0.2.1.dist-info/WHEEL +5 -0
- atdd-0.2.1.dist-info/entry_points.txt +2 -0
- atdd-0.2.1.dist-info/licenses/LICENSE +674 -0
- atdd-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Infer and set governance.status for contract schemas.
|
|
4
|
+
|
|
5
|
+
Inference rules:
|
|
6
|
+
- version 0.x.x → "draft" (still iterating)
|
|
7
|
+
- wagon_ref = "plan/unknown/_unknown.yaml" → "draft" (no real wagon)
|
|
8
|
+
- version 1.x.x+ with real wagon → "active" (stable)
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
python3 atdd/coach/commands/infer_governance_status.py [--dry-run]
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import sys
|
|
15
|
+
import json
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
REPO_ROOT = Path(__file__).resolve().parents[4]
|
|
19
|
+
CONTRACTS_DIR = REPO_ROOT / "contracts"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def infer_status(contract: dict) -> str:
|
|
23
|
+
"""Infer governance status from contract metadata."""
|
|
24
|
+
version = contract.get("version", "0.0.0")
|
|
25
|
+
metadata = contract.get("x-artifact-metadata", {})
|
|
26
|
+
traceability = metadata.get("traceability", {})
|
|
27
|
+
wagon_ref = traceability.get("wagon_ref", "")
|
|
28
|
+
|
|
29
|
+
# Rule 1: Version-based
|
|
30
|
+
if version.startswith("0."):
|
|
31
|
+
return "draft"
|
|
32
|
+
|
|
33
|
+
# Rule 2: Traceability-based
|
|
34
|
+
if wagon_ref == "plan/unknown/_unknown.yaml" or not wagon_ref:
|
|
35
|
+
return "draft"
|
|
36
|
+
|
|
37
|
+
# Rule 3: Active (1.x.x+ with real wagon)
|
|
38
|
+
return "active"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def infer_stability(contract: dict, status: str) -> str:
|
|
42
|
+
"""Infer governance stability."""
|
|
43
|
+
if status == "draft":
|
|
44
|
+
return "experimental"
|
|
45
|
+
|
|
46
|
+
# For active contracts, use stable (meta-schema only allows: experimental, stable, frozen)
|
|
47
|
+
return "stable"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def set_governance_status(contract_path: Path, dry_run: bool = False) -> dict:
|
|
51
|
+
"""Set governance.status for a contract."""
|
|
52
|
+
try:
|
|
53
|
+
with open(contract_path) as f:
|
|
54
|
+
contract = json.load(f)
|
|
55
|
+
|
|
56
|
+
metadata = contract.get("x-artifact-metadata", {})
|
|
57
|
+
if not metadata:
|
|
58
|
+
return {"status": "skip", "reason": "No x-artifact-metadata"}
|
|
59
|
+
|
|
60
|
+
governance = metadata.get("governance", {})
|
|
61
|
+
|
|
62
|
+
# Infer status
|
|
63
|
+
inferred_status = infer_status(contract)
|
|
64
|
+
inferred_stability = infer_stability(contract, inferred_status)
|
|
65
|
+
|
|
66
|
+
current_status = governance.get("status")
|
|
67
|
+
current_stability = governance.get("stability")
|
|
68
|
+
|
|
69
|
+
# Check if update needed
|
|
70
|
+
changes = []
|
|
71
|
+
if current_status != inferred_status:
|
|
72
|
+
governance["status"] = inferred_status
|
|
73
|
+
changes.append(f"status: {current_status} → {inferred_status}")
|
|
74
|
+
|
|
75
|
+
if current_stability != inferred_stability:
|
|
76
|
+
governance["stability"] = inferred_stability
|
|
77
|
+
changes.append(f"stability: {current_stability} → {inferred_stability}")
|
|
78
|
+
|
|
79
|
+
if not changes:
|
|
80
|
+
return {"status": "skip", "reason": "Already correct"}
|
|
81
|
+
|
|
82
|
+
# Update contract
|
|
83
|
+
metadata["governance"] = governance
|
|
84
|
+
contract["x-artifact-metadata"] = metadata
|
|
85
|
+
|
|
86
|
+
if not dry_run:
|
|
87
|
+
with open(contract_path, 'w') as f:
|
|
88
|
+
json.dump(contract, f, indent=2)
|
|
89
|
+
f.write('\n')
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
"status": "updated",
|
|
93
|
+
"changes": changes
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
except Exception as e:
|
|
97
|
+
return {"status": "error", "reason": str(e)}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def main():
|
|
101
|
+
"""Infer governance status for all contracts."""
|
|
102
|
+
dry_run = "--dry-run" in sys.argv
|
|
103
|
+
mode = "DRY RUN" if dry_run else "APPLY CHANGES"
|
|
104
|
+
|
|
105
|
+
print("=" * 80)
|
|
106
|
+
print(f"Infer Governance Status for Contracts ({mode})")
|
|
107
|
+
print("=" * 80)
|
|
108
|
+
print()
|
|
109
|
+
|
|
110
|
+
contract_files = list(CONTRACTS_DIR.rglob("*.schema.json"))
|
|
111
|
+
|
|
112
|
+
updated = 0
|
|
113
|
+
skipped = 0
|
|
114
|
+
errors = 0
|
|
115
|
+
|
|
116
|
+
for contract_path in sorted(contract_files):
|
|
117
|
+
result = set_governance_status(contract_path, dry_run)
|
|
118
|
+
|
|
119
|
+
if result["status"] == "updated":
|
|
120
|
+
rel_path = contract_path.relative_to(REPO_ROOT)
|
|
121
|
+
print(f"✅ {rel_path}")
|
|
122
|
+
for change in result["changes"]:
|
|
123
|
+
print(f" {change}")
|
|
124
|
+
updated += 1
|
|
125
|
+
elif result["status"] == "skip":
|
|
126
|
+
skipped += 1
|
|
127
|
+
elif result["status"] == "error":
|
|
128
|
+
print(f"❌ {contract_path.relative_to(REPO_ROOT)}: {result['reason']}")
|
|
129
|
+
errors += 1
|
|
130
|
+
|
|
131
|
+
print()
|
|
132
|
+
print("=" * 80)
|
|
133
|
+
print("SUMMARY")
|
|
134
|
+
print("=" * 80)
|
|
135
|
+
print(f"Total contracts: {len(contract_files)}")
|
|
136
|
+
print(f"✅ Updated: {updated}")
|
|
137
|
+
print(f"⏭️ Skipped: {skipped}")
|
|
138
|
+
print(f"❌ Errors: {errors}")
|
|
139
|
+
print("=" * 80)
|
|
140
|
+
|
|
141
|
+
if dry_run:
|
|
142
|
+
print()
|
|
143
|
+
print("This was a DRY RUN. No files were modified.")
|
|
144
|
+
print("Run without --dry-run to apply changes:")
|
|
145
|
+
print(" python3 atdd/coach/commands/infer_governance_status.py")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if __name__ == "__main__":
|
|
149
|
+
main()
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project initializer for ATDD structure in consumer repos.
|
|
3
|
+
|
|
4
|
+
Creates the following structure:
|
|
5
|
+
consumer-repo/
|
|
6
|
+
├── CLAUDE.md (with managed ATDD block)
|
|
7
|
+
├── atdd-sessions/
|
|
8
|
+
│ ├── SESSION-TEMPLATE.md (copied from package)
|
|
9
|
+
│ └── archive/
|
|
10
|
+
└── .atdd/
|
|
11
|
+
├── manifest.yaml (machine-readable session tracking)
|
|
12
|
+
└── config.yaml (agent sync configuration)
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
atdd init # Initialize ATDD structure
|
|
16
|
+
atdd init --force # Overwrite existing files
|
|
17
|
+
|
|
18
|
+
Convention: src/atdd/coach/conventions/session.convention.yaml
|
|
19
|
+
"""
|
|
20
|
+
import shutil
|
|
21
|
+
from datetime import date
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Optional
|
|
24
|
+
|
|
25
|
+
import yaml
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ProjectInitializer:
|
|
29
|
+
"""Initialize ATDD structure in consumer repo."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, target_dir: Optional[Path] = None):
|
|
32
|
+
"""
|
|
33
|
+
Initialize the ProjectInitializer.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
target_dir: Target directory for initialization. Defaults to cwd.
|
|
37
|
+
"""
|
|
38
|
+
self.target_dir = target_dir or Path.cwd()
|
|
39
|
+
self.sessions_dir = self.target_dir / "atdd-sessions"
|
|
40
|
+
self.archive_dir = self.sessions_dir / "archive"
|
|
41
|
+
self.atdd_config_dir = self.target_dir / ".atdd"
|
|
42
|
+
self.manifest_file = self.atdd_config_dir / "manifest.yaml"
|
|
43
|
+
self.config_file = self.atdd_config_dir / "config.yaml"
|
|
44
|
+
|
|
45
|
+
# Package template location
|
|
46
|
+
self.package_root = Path(__file__).parent.parent # src/atdd/coach
|
|
47
|
+
self.template_source = self.package_root / "templates" / "SESSION-TEMPLATE.md"
|
|
48
|
+
|
|
49
|
+
def init(self, force: bool = False) -> int:
|
|
50
|
+
"""
|
|
51
|
+
Create atdd-sessions/ and .atdd/ structure.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
force: If True, overwrite existing files.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
0 on success, 1 on error.
|
|
58
|
+
"""
|
|
59
|
+
# Check if already initialized
|
|
60
|
+
if self.sessions_dir.exists() and not force:
|
|
61
|
+
print(f"ATDD already initialized at {self.target_dir}")
|
|
62
|
+
print("Use --force to reinitialize")
|
|
63
|
+
return 1
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
# Create atdd-sessions/ directory
|
|
67
|
+
self.sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
68
|
+
print(f"Created: {self.sessions_dir}")
|
|
69
|
+
|
|
70
|
+
# Create archive subdirectory
|
|
71
|
+
self.archive_dir.mkdir(parents=True, exist_ok=True)
|
|
72
|
+
print(f"Created: {self.archive_dir}")
|
|
73
|
+
|
|
74
|
+
# Create .atdd/ config directory
|
|
75
|
+
self.atdd_config_dir.mkdir(parents=True, exist_ok=True)
|
|
76
|
+
print(f"Created: {self.atdd_config_dir}")
|
|
77
|
+
|
|
78
|
+
# Copy SESSION-TEMPLATE.md to atdd-sessions/
|
|
79
|
+
template_dest = self.sessions_dir / "SESSION-TEMPLATE.md"
|
|
80
|
+
if self.template_source.exists():
|
|
81
|
+
shutil.copy2(self.template_source, template_dest)
|
|
82
|
+
print(f"Copied: SESSION-TEMPLATE.md -> {template_dest}")
|
|
83
|
+
else:
|
|
84
|
+
print(f"Warning: Template not found at {self.template_source}")
|
|
85
|
+
|
|
86
|
+
# Create manifest.yaml
|
|
87
|
+
self._create_manifest(force)
|
|
88
|
+
|
|
89
|
+
# Create config.yaml
|
|
90
|
+
self._create_config(force)
|
|
91
|
+
|
|
92
|
+
# Sync agent config files
|
|
93
|
+
from atdd.coach.commands.sync import AgentConfigSync
|
|
94
|
+
syncer = AgentConfigSync(self.target_dir)
|
|
95
|
+
syncer.sync()
|
|
96
|
+
|
|
97
|
+
# Print next steps
|
|
98
|
+
print("\n" + "=" * 60)
|
|
99
|
+
print("ATDD initialized successfully!")
|
|
100
|
+
print("=" * 60)
|
|
101
|
+
print("\nNext steps:")
|
|
102
|
+
print(" 1. Create a new session:")
|
|
103
|
+
print(" atdd session new my-feature")
|
|
104
|
+
print("")
|
|
105
|
+
print(" 2. List existing sessions:")
|
|
106
|
+
print(" atdd session list")
|
|
107
|
+
print("")
|
|
108
|
+
print(" 3. Archive completed sessions:")
|
|
109
|
+
print(" atdd session archive 01")
|
|
110
|
+
print("")
|
|
111
|
+
print("Structure created:")
|
|
112
|
+
print(f" {self.sessions_dir}/")
|
|
113
|
+
print(f" {self.sessions_dir}/archive/")
|
|
114
|
+
print(f" {self.sessions_dir}/SESSION-TEMPLATE.md")
|
|
115
|
+
print(f" {self.atdd_config_dir}/")
|
|
116
|
+
print(f" {self.manifest_file}")
|
|
117
|
+
print(f" {self.config_file}")
|
|
118
|
+
print(f" CLAUDE.md (with ATDD managed block)")
|
|
119
|
+
|
|
120
|
+
return 0
|
|
121
|
+
|
|
122
|
+
except PermissionError as e:
|
|
123
|
+
print(f"Error: Permission denied - {e}")
|
|
124
|
+
return 1
|
|
125
|
+
except OSError as e:
|
|
126
|
+
print(f"Error: {e}")
|
|
127
|
+
return 1
|
|
128
|
+
|
|
129
|
+
def _create_manifest(self, force: bool = False) -> None:
|
|
130
|
+
"""
|
|
131
|
+
Create or update .atdd/manifest.yaml.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
force: If True, overwrite existing manifest.
|
|
135
|
+
"""
|
|
136
|
+
if self.manifest_file.exists() and not force:
|
|
137
|
+
print(f"Manifest already exists: {self.manifest_file}")
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
manifest = {
|
|
141
|
+
"version": "1.0",
|
|
142
|
+
"sessions_dir": "atdd-sessions",
|
|
143
|
+
"created": date.today().isoformat(),
|
|
144
|
+
"sessions": [],
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
with open(self.manifest_file, "w") as f:
|
|
148
|
+
yaml.dump(manifest, f, default_flow_style=False, sort_keys=False)
|
|
149
|
+
|
|
150
|
+
print(f"Created: {self.manifest_file}")
|
|
151
|
+
|
|
152
|
+
def _create_config(self, force: bool = False) -> None:
|
|
153
|
+
"""
|
|
154
|
+
Create or update .atdd/config.yaml.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
force: If True, overwrite existing config.
|
|
158
|
+
"""
|
|
159
|
+
if self.config_file.exists() and not force:
|
|
160
|
+
print(f"Config already exists: {self.config_file}")
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
config = {
|
|
164
|
+
"version": "1.0",
|
|
165
|
+
"sync": {
|
|
166
|
+
"agents": ["claude"], # Default: only Claude
|
|
167
|
+
},
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
with open(self.config_file, "w") as f:
|
|
171
|
+
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
|
172
|
+
|
|
173
|
+
print(f"Created: {self.config_file}")
|
|
174
|
+
|
|
175
|
+
def is_initialized(self) -> bool:
|
|
176
|
+
"""Check if ATDD is already initialized in target directory."""
|
|
177
|
+
return self.sessions_dir.exists() and self.manifest_file.exists()
|