baton-pass 0.1.0__tar.gz

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.
Files changed (53) hide show
  1. baton_pass-0.1.0/.baton.toml +9 -0
  2. baton_pass-0.1.0/.cursor/rules/baton.mdc +126 -0
  3. baton_pass-0.1.0/.github/copilot-instructions.md +120 -0
  4. baton_pass-0.1.0/.gitignore +70 -0
  5. baton_pass-0.1.0/AGENTS.md +120 -0
  6. baton_pass-0.1.0/BATON.md +222 -0
  7. baton_pass-0.1.0/BATON_PLAN.md +683 -0
  8. baton_pass-0.1.0/CLAUDE.md +238 -0
  9. baton_pass-0.1.0/CONTRIBUTING.md +118 -0
  10. baton_pass-0.1.0/GEMINI.md +120 -0
  11. baton_pass-0.1.0/LICENSE +21 -0
  12. baton_pass-0.1.0/PKG-INFO +265 -0
  13. baton_pass-0.1.0/README.md +228 -0
  14. baton_pass-0.1.0/baton/__init__.py +3 -0
  15. baton_pass-0.1.0/baton/adapters/__init__.py +0 -0
  16. baton_pass-0.1.0/baton/adapters/base.py +354 -0
  17. baton_pass-0.1.0/baton/adapters/claude.py +12 -0
  18. baton_pass-0.1.0/baton/adapters/codex.py +12 -0
  19. baton_pass-0.1.0/baton/adapters/copilot.py +12 -0
  20. baton_pass-0.1.0/baton/adapters/cursor.py +58 -0
  21. baton_pass-0.1.0/baton/adapters/gemini.py +12 -0
  22. baton_pass-0.1.0/baton/adapters/registry.py +60 -0
  23. baton_pass-0.1.0/baton/cli.py +123 -0
  24. baton_pass-0.1.0/baton/commands/__init__.py +0 -0
  25. baton_pass-0.1.0/baton/commands/end.py +280 -0
  26. baton_pass-0.1.0/baton/commands/init.py +227 -0
  27. baton_pass-0.1.0/baton/commands/score.py +85 -0
  28. baton_pass-0.1.0/baton/commands/status.py +87 -0
  29. baton_pass-0.1.0/baton/commands/sync.py +77 -0
  30. baton_pass-0.1.0/baton/core/__init__.py +0 -0
  31. baton_pass-0.1.0/baton/core/config.py +66 -0
  32. baton_pass-0.1.0/baton/core/document.py +139 -0
  33. baton_pass-0.1.0/baton/core/gitdiff.py +103 -0
  34. baton_pass-0.1.0/baton/core/schema.py +241 -0
  35. baton_pass-0.1.0/baton/core/summarizer.py +180 -0
  36. baton_pass-0.1.0/baton/llm/__init__.py +44 -0
  37. baton_pass-0.1.0/baton/llm/anthropic_provider.py +75 -0
  38. baton_pass-0.1.0/baton/llm/base.py +28 -0
  39. baton_pass-0.1.0/baton/llm/openai_provider.py +65 -0
  40. baton_pass-0.1.0/baton/llm/vertex_provider.py +73 -0
  41. baton_pass-0.1.0/baton_architecture.svg +162 -0
  42. baton_pass-0.1.0/pyproject.toml +55 -0
  43. baton_pass-0.1.0/tests/__init__.py +0 -0
  44. baton_pass-0.1.0/tests/fixtures/sample_baton.md +90 -0
  45. baton_pass-0.1.0/tests/test_adapters.py +234 -0
  46. baton_pass-0.1.0/tests/test_document.py +135 -0
  47. baton_pass-0.1.0/tests/test_end.py +375 -0
  48. baton_pass-0.1.0/tests/test_gitdiff.py +167 -0
  49. baton_pass-0.1.0/tests/test_llm.py +133 -0
  50. baton_pass-0.1.0/tests/test_schema.py +259 -0
  51. baton_pass-0.1.0/tests/test_score.py +105 -0
  52. baton_pass-0.1.0/tests/test_status.py +98 -0
  53. baton_pass-0.1.0/tests/test_sync.py +111 -0
@@ -0,0 +1,9 @@
1
+ # .baton.toml — Baton project configuration
2
+ # https://github.com/AriS10223/baton
3
+
4
+ [baton]
5
+ min_diff_lines = 10
6
+ auto_sync = true
7
+
8
+ [adapters]
9
+ enabled = ["claude", "codex", "cursor", "gemini", "copilot"]
@@ -0,0 +1,126 @@
1
+ ---
2
+ description: Baton project context (auto-generated by baton sync)
3
+ globs:
4
+ alwaysApply: true
5
+ ---
6
+
7
+ <!-- BATON:START — auto-generated, do not edit by hand -->
8
+ # Baton — Project Context
9
+
10
+ > Last synced: 2026-06-04 · via cursor
11
+ >
12
+ > **Test:** could a new agent read this and contribute without breaking anything?
13
+
14
+ ## Baton
15
+
16
+ Maintain a single living onboarding document (BATON.md) that syncs to every AI agent's config file, so vibe coders never lose context when switching between Claude Code, Cursor, and Codex.
17
+
18
+ **Who:** Independent developers who use multiple AI coding tools in a single session **Stage:** prototype
19
+
20
+ ## Architecture
21
+
22
+ Python CLI tool with a plugin-style adapter system. BATON.md is the single source of truth. The adapters/ folder converts it into each agent's native file format. Core logic is split into document.py (parse/save), schema.py (definitions), config.py (settings), and the four command modules.
23
+
24
+ **Entry point:** `baton/cli.py`
25
+ **Data flow:** baton sync -> cli.py -> commands/sync.py -> BatonDocument.load(BATON.md) -> adapter.render(data) -> upsert_managed_block -> write agent file
26
+
27
+ | Path | Purpose |
28
+ |------|---------|
29
+ | `baton/` | Main package: CLI, commands, core logic, adapters |
30
+ | `baton/core/` | BatonDocument, schema definitions, config reader |
31
+ | `baton/adapters/` | One file per supported agent tool. Each implements BaseAdapter. |
32
+ | `baton/commands/` | One file per CLI command (init, sync, status, score) |
33
+ | `tests/` | pytest test suite with fixtures/ |
34
+
35
+ ## Tech Stack
36
+
37
+ | Tool | Version | Why | Gotchas |
38
+ |------|---------|-----|---------|
39
+ | Python | 3.10+ | Target audience is Python developers. tomllib (stdlib) available 3.11+; tomli backport for 3.10. | Use try/except tomllib import for 3.10 compat. Never add shims beyond this. |
40
+ | Typer | >=0.12 | Declarative CLI with rich help text and type inference. Wraps Click. | Entry point is baton.cli:main (not :app). All console.print strings must be CP1252-safe on Windows. |
41
+ | Rich | >=13 | Coloured terminal output for sync/status/score tables. | Import Rich directly. Never use Unicode outside basic Latin in console.print() calls on Windows CP1252. |
42
+ | ruamel.yaml | >=0.18 | Round-trip YAML parser that preserves inline # comments on save. PyYAML drops them. | Never switch to PyYAML. YAML fence regex must use \n before closing ``` to avoid false match inside string values. |
43
+ | tomllib/tomli | stdlib 3.11+ / tomli>=2.0 for 3.10 | Read .baton.toml config. No extra dependency on 3.11+. | Open files in binary mode: open(path, 'rb'). |
44
+
45
+ ## Laws (Never Violate)
46
+
47
+ > Hard constraints. Agents must not override these — ever.
48
+
49
+ 1. Never use PyYAML. All BATON.md parsing uses ruamel.yaml to preserve inline comments.
50
+ 2. schema.py is the single source of truth for section/field names. Never hardcode them in score.py or anywhere else.
51
+ 3. sync must never overwrite the full content of an agent file. Always use managed-block markers.
52
+ 4. SCORE_CHECKS in schema.py must total exactly 100 points. The assert enforces this at import time.
53
+ 5. No LLM calls in Increment 1. init, sync, status, score are purely deterministic.
54
+ 6. All Rich console.print() output must use only CP1252-safe (basic ASCII) characters.
55
+
56
+ ## Current Sprint: Ship Phase 1 Increment 1: init + sync + status + score (no LLM)
57
+
58
+ ### ✅ Done
59
+ - pyproject.toml, package skeleton, LICENSE, .gitignore *(confidence: stable)*
60
+ - core/schema.py with SCORE_CHECKS totalling 100 points *(confidence: stable)* — Assert at import time enforces the total.
61
+ - core/document.py: BatonDocument load/save round-trip *(confidence: stable)* — Extracts the yaml fenced block, parses with ruamel.yaml.
62
+ - core/config.py: .baton.toml reader *(confidence: stable)*
63
+ - adapters/base.py: BaseAdapter + managed-block utilities *(confidence: stable)* — upsert_managed_block / extract_managed_block are the core safety primitives.
64
+ - All five adapters (claude, codex, cursor, gemini, copilot) *(confidence: stable)* — Cursor overrides prepare_file() to handle MDC frontmatter.
65
+ - adapters/registry.py: detect_enabled + get_adapters *(confidence: stable)*
66
+ - commands/sync.py + commands/status.py + commands/score.py + commands/init.py *(confidence: stable)*
67
+ - cli.py wiring all four commands + stub for baton end *(confidence: stable)*
68
+ - Full test suite: 107 tests passing *(confidence: stable)*
69
+ - README.md, CONTRIBUTING.md, BATON.md dogfood, CLAUDE.md lessons log *(confidence: stable)*
70
+ - git init + pre-commit hook installed *(confidence: stable)*
71
+
72
+ ### 📋 Up Next
73
+ - Push to GitHub repo (URL from user) *[high]*
74
+ - Increment 2: baton end (git-diff parser + Anthropic summariser + Rich review UI) *[medium]*
75
+
76
+ ## Key Decisions
77
+
78
+ | # | Decision | Why | When | Tool |
79
+ |---|---------|-----|------|------|
80
+ | d001 | Managed-block markers (BATON-START/END) instead of full-file ownership | Users already have hand-written CLAUDE.md content. Overwriting it would be a critical UX failure. | 2026-06-04 | claude-code |
81
+ | d002 | Embed BATON.md template as a Python string in init.py | More reliable than importlib.resources for initial releases. No template file packaging needed. | 2026-06-04 | claude-code |
82
+ | d003 | schema.py defines SCORE_CHECKS; score.py imports them | Prevents scoring logic from drifting from the actual schema when fields change. | 2026-06-04 | claude-code |
83
+ | d004 | BATON.md format: markdown with schema inside a yaml fenced block | Human-readable as a GitHub markdown file, machine-parseable via regex fence extraction. | 2026-06-04 | claude-code |
84
+ | d005 | Pre-commit hook is non-blocking (exits 0, just prints a reminder) | Phase 1 trigger is manual. A blocking hook would annoy users committing unrelated code. | 2026-06-04 | claude-code |
85
+ | d006 | Increment 1 scope: init + sync + status + score (no LLM) | Ship the deterministic core first. Validate value before adding AI complexity. | 2026-06-04 | claude-code |
86
+
87
+ ## Anti-Decisions (Rejected Approaches)
88
+
89
+ > These were explicitly ruled out. Don't re-suggest them.
90
+
91
+ | # | Rejected | Why | When |
92
+ |---|---------|-----|------|
93
+ | a001 | Auto-detecting token limits in other tools | Claude Code, Cursor, and Codex do not expose their token state to external CLIs. Physically impossible in Phase 1. | 2026-06-04 |
94
+ | a002 | PyYAML for BATON.md parsing | PyYAML drops inline # comments on round-trip. ruamel.yaml preserves them. | 2026-06-04 |
95
+ | a003 | Separate template file (baton/templates/BATON.md.template) | importlib.resources is finicky for installable packages in Phase 1. Embedding the template as a string is simpler. | 2026-06-04 |
96
+ | a004 | Full-file sync (replacing the entire CLAUDE.md) | Users have hand-written content in these files. Full replacement would be destructive. | 2026-06-04 |
97
+
98
+ ## Landmines (Looks Wrong, But Intentional)
99
+
100
+ > Do NOT 'fix' these. They are correct as-is.
101
+
102
+ **`baton/core/schema.py (end of file)`**
103
+ - Looks like: Redundant assert after a list definition
104
+ - Actually: Enforces that SCORE_CHECKS always sums to exactly 100. If you add/remove a check and forget to rebalance points, this fails at import time.
105
+
106
+ **`baton/adapters/cursor.py: prepare_file()`**
107
+ - Looks like: Overcomplicated file-write logic vs. the four simpler adapters
108
+ - Actually: Cursor .mdc files require YAML frontmatter at the top. The frontmatter must be outside the managed block so it survives re-syncs.
109
+
110
+ **`baton/core/document.py: _YAML_FENCE_RE`**
111
+ - Looks like: Unnecessary \n before closing ``` in the regex
112
+ - Actually: Required. Without it, triple-backticks inside YAML string values prematurely terminate the match.
113
+
114
+ ## Open Questions
115
+
116
+ > Do NOT make unilateral decisions on these. Surface them to the human first.
117
+
118
+ 🔴 **[q001]** Should baton init auto-run baton sync immediately after scaffolding?
119
+ - Context: The template BATON.md has all empty values. Syncing empty content produces a mostly-empty context file.
120
+ - Discussion: Current: init does NOT auto-sync. User fills in BATON.md first, then runs baton sync.
121
+
122
+ 🔴 **[q002]** Should extract_managed_block skip content inside triple-backtick fences?
123
+ - Context: Currently, if a user writes the literal managed-block marker inside a code fence example, sync corrupts the file. See mistake log entry [2026-06-04].
124
+ - Discussion: Low priority for Phase 1 since it only affects files that document Baton itself.
125
+
126
+ <!-- BATON:END -->
@@ -0,0 +1,120 @@
1
+ <!-- BATON:START — auto-generated, do not edit by hand -->
2
+ # Baton — Project Context
3
+
4
+ > Last synced: 2026-06-04 · via copilot
5
+ >
6
+ > **Test:** could a new agent read this and contribute without breaking anything?
7
+
8
+ ## Baton
9
+
10
+ Maintain a single living onboarding document (BATON.md) that syncs to every AI agent's config file, so vibe coders never lose context when switching between Claude Code, Cursor, and Codex.
11
+
12
+ **Who:** Independent developers who use multiple AI coding tools in a single session **Stage:** prototype
13
+
14
+ ## Architecture
15
+
16
+ Python CLI tool with a plugin-style adapter system. BATON.md is the single source of truth. The adapters/ folder converts it into each agent's native file format. Core logic is split into document.py (parse/save), schema.py (definitions), config.py (settings), and the four command modules.
17
+
18
+ **Entry point:** `baton/cli.py`
19
+ **Data flow:** baton sync -> cli.py -> commands/sync.py -> BatonDocument.load(BATON.md) -> adapter.render(data) -> upsert_managed_block -> write agent file
20
+
21
+ | Path | Purpose |
22
+ |------|---------|
23
+ | `baton/` | Main package: CLI, commands, core logic, adapters |
24
+ | `baton/core/` | BatonDocument, schema definitions, config reader |
25
+ | `baton/adapters/` | One file per supported agent tool. Each implements BaseAdapter. |
26
+ | `baton/commands/` | One file per CLI command (init, sync, status, score) |
27
+ | `tests/` | pytest test suite with fixtures/ |
28
+
29
+ ## Tech Stack
30
+
31
+ | Tool | Version | Why | Gotchas |
32
+ |------|---------|-----|---------|
33
+ | Python | 3.10+ | Target audience is Python developers. tomllib (stdlib) available 3.11+; tomli backport for 3.10. | Use try/except tomllib import for 3.10 compat. Never add shims beyond this. |
34
+ | Typer | >=0.12 | Declarative CLI with rich help text and type inference. Wraps Click. | Entry point is baton.cli:main (not :app). All console.print strings must be CP1252-safe on Windows. |
35
+ | Rich | >=13 | Coloured terminal output for sync/status/score tables. | Import Rich directly. Never use Unicode outside basic Latin in console.print() calls on Windows CP1252. |
36
+ | ruamel.yaml | >=0.18 | Round-trip YAML parser that preserves inline # comments on save. PyYAML drops them. | Never switch to PyYAML. YAML fence regex must use \n before closing ``` to avoid false match inside string values. |
37
+ | tomllib/tomli | stdlib 3.11+ / tomli>=2.0 for 3.10 | Read .baton.toml config. No extra dependency on 3.11+. | Open files in binary mode: open(path, 'rb'). |
38
+
39
+ ## Laws (Never Violate)
40
+
41
+ > Hard constraints. Agents must not override these — ever.
42
+
43
+ 1. Never use PyYAML. All BATON.md parsing uses ruamel.yaml to preserve inline comments.
44
+ 2. schema.py is the single source of truth for section/field names. Never hardcode them in score.py or anywhere else.
45
+ 3. sync must never overwrite the full content of an agent file. Always use managed-block markers.
46
+ 4. SCORE_CHECKS in schema.py must total exactly 100 points. The assert enforces this at import time.
47
+ 5. No LLM calls in Increment 1. init, sync, status, score are purely deterministic.
48
+ 6. All Rich console.print() output must use only CP1252-safe (basic ASCII) characters.
49
+
50
+ ## Current Sprint: Ship Phase 1 Increment 1: init + sync + status + score (no LLM)
51
+
52
+ ### ✅ Done
53
+ - pyproject.toml, package skeleton, LICENSE, .gitignore *(confidence: stable)*
54
+ - core/schema.py with SCORE_CHECKS totalling 100 points *(confidence: stable)* — Assert at import time enforces the total.
55
+ - core/document.py: BatonDocument load/save round-trip *(confidence: stable)* — Extracts the yaml fenced block, parses with ruamel.yaml.
56
+ - core/config.py: .baton.toml reader *(confidence: stable)*
57
+ - adapters/base.py: BaseAdapter + managed-block utilities *(confidence: stable)* — upsert_managed_block / extract_managed_block are the core safety primitives.
58
+ - All five adapters (claude, codex, cursor, gemini, copilot) *(confidence: stable)* — Cursor overrides prepare_file() to handle MDC frontmatter.
59
+ - adapters/registry.py: detect_enabled + get_adapters *(confidence: stable)*
60
+ - commands/sync.py + commands/status.py + commands/score.py + commands/init.py *(confidence: stable)*
61
+ - cli.py wiring all four commands + stub for baton end *(confidence: stable)*
62
+ - Full test suite: 107 tests passing *(confidence: stable)*
63
+ - README.md, CONTRIBUTING.md, BATON.md dogfood, CLAUDE.md lessons log *(confidence: stable)*
64
+ - git init + pre-commit hook installed *(confidence: stable)*
65
+
66
+ ### 📋 Up Next
67
+ - Push to GitHub repo (URL from user) *[high]*
68
+ - Increment 2: baton end (git-diff parser + Anthropic summariser + Rich review UI) *[medium]*
69
+
70
+ ## Key Decisions
71
+
72
+ | # | Decision | Why | When | Tool |
73
+ |---|---------|-----|------|------|
74
+ | d001 | Managed-block markers (BATON-START/END) instead of full-file ownership | Users already have hand-written CLAUDE.md content. Overwriting it would be a critical UX failure. | 2026-06-04 | claude-code |
75
+ | d002 | Embed BATON.md template as a Python string in init.py | More reliable than importlib.resources for initial releases. No template file packaging needed. | 2026-06-04 | claude-code |
76
+ | d003 | schema.py defines SCORE_CHECKS; score.py imports them | Prevents scoring logic from drifting from the actual schema when fields change. | 2026-06-04 | claude-code |
77
+ | d004 | BATON.md format: markdown with schema inside a yaml fenced block | Human-readable as a GitHub markdown file, machine-parseable via regex fence extraction. | 2026-06-04 | claude-code |
78
+ | d005 | Pre-commit hook is non-blocking (exits 0, just prints a reminder) | Phase 1 trigger is manual. A blocking hook would annoy users committing unrelated code. | 2026-06-04 | claude-code |
79
+ | d006 | Increment 1 scope: init + sync + status + score (no LLM) | Ship the deterministic core first. Validate value before adding AI complexity. | 2026-06-04 | claude-code |
80
+
81
+ ## Anti-Decisions (Rejected Approaches)
82
+
83
+ > These were explicitly ruled out. Don't re-suggest them.
84
+
85
+ | # | Rejected | Why | When |
86
+ |---|---------|-----|------|
87
+ | a001 | Auto-detecting token limits in other tools | Claude Code, Cursor, and Codex do not expose their token state to external CLIs. Physically impossible in Phase 1. | 2026-06-04 |
88
+ | a002 | PyYAML for BATON.md parsing | PyYAML drops inline # comments on round-trip. ruamel.yaml preserves them. | 2026-06-04 |
89
+ | a003 | Separate template file (baton/templates/BATON.md.template) | importlib.resources is finicky for installable packages in Phase 1. Embedding the template as a string is simpler. | 2026-06-04 |
90
+ | a004 | Full-file sync (replacing the entire CLAUDE.md) | Users have hand-written content in these files. Full replacement would be destructive. | 2026-06-04 |
91
+
92
+ ## Landmines (Looks Wrong, But Intentional)
93
+
94
+ > Do NOT 'fix' these. They are correct as-is.
95
+
96
+ **`baton/core/schema.py (end of file)`**
97
+ - Looks like: Redundant assert after a list definition
98
+ - Actually: Enforces that SCORE_CHECKS always sums to exactly 100. If you add/remove a check and forget to rebalance points, this fails at import time.
99
+
100
+ **`baton/adapters/cursor.py: prepare_file()`**
101
+ - Looks like: Overcomplicated file-write logic vs. the four simpler adapters
102
+ - Actually: Cursor .mdc files require YAML frontmatter at the top. The frontmatter must be outside the managed block so it survives re-syncs.
103
+
104
+ **`baton/core/document.py: _YAML_FENCE_RE`**
105
+ - Looks like: Unnecessary \n before closing ``` in the regex
106
+ - Actually: Required. Without it, triple-backticks inside YAML string values prematurely terminate the match.
107
+
108
+ ## Open Questions
109
+
110
+ > Do NOT make unilateral decisions on these. Surface them to the human first.
111
+
112
+ 🔴 **[q001]** Should baton init auto-run baton sync immediately after scaffolding?
113
+ - Context: The template BATON.md has all empty values. Syncing empty content produces a mostly-empty context file.
114
+ - Discussion: Current: init does NOT auto-sync. User fills in BATON.md first, then runs baton sync.
115
+
116
+ 🔴 **[q002]** Should extract_managed_block skip content inside triple-backtick fences?
117
+ - Context: Currently, if a user writes the literal managed-block marker inside a code fence example, sync corrupts the file. See mistake log entry [2026-06-04].
118
+ - Discussion: Low priority for Phase 1 since it only affects files that document Baton itself.
119
+
120
+ <!-- BATON:END -->
@@ -0,0 +1,70 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ share/python-wheels/
20
+ *.egg-info/
21
+ .installed.cfg
22
+ *.egg
23
+ MANIFEST
24
+
25
+ # Virtual environments
26
+ .env
27
+ .venv
28
+ env/
29
+ venv/
30
+ ENV/
31
+ env.bak/
32
+ venv.bak/
33
+
34
+ # Testing
35
+ .tox/
36
+ .nox/
37
+ .coverage
38
+ .coverage.*
39
+ .cache
40
+ nosetests.xml
41
+ coverage.xml
42
+ *.cover
43
+ *.py,cover
44
+ .hypothesis/
45
+ .pytest_cache/
46
+ cover/
47
+ htmlcov/
48
+
49
+ # IDEs
50
+ .idea/
51
+ .vscode/
52
+ *.swp
53
+ *.swo
54
+
55
+ # OS
56
+ .DS_Store
57
+ Thumbs.db
58
+
59
+ # Secrets
60
+ .env.local
61
+ .env.*.local
62
+ *.pem
63
+ *.key
64
+
65
+ # Distribution
66
+ dist/
67
+ build/
68
+
69
+ # Baton internal
70
+ .baton/
@@ -0,0 +1,120 @@
1
+ <!-- BATON:START — auto-generated, do not edit by hand -->
2
+ # Baton — Project Context
3
+
4
+ > Last synced: 2026-06-04 · via codex
5
+ >
6
+ > **Test:** could a new agent read this and contribute without breaking anything?
7
+
8
+ ## Baton
9
+
10
+ Maintain a single living onboarding document (BATON.md) that syncs to every AI agent's config file, so vibe coders never lose context when switching between Claude Code, Cursor, and Codex.
11
+
12
+ **Who:** Independent developers who use multiple AI coding tools in a single session **Stage:** prototype
13
+
14
+ ## Architecture
15
+
16
+ Python CLI tool with a plugin-style adapter system. BATON.md is the single source of truth. The adapters/ folder converts it into each agent's native file format. Core logic is split into document.py (parse/save), schema.py (definitions), config.py (settings), and the four command modules.
17
+
18
+ **Entry point:** `baton/cli.py`
19
+ **Data flow:** baton sync -> cli.py -> commands/sync.py -> BatonDocument.load(BATON.md) -> adapter.render(data) -> upsert_managed_block -> write agent file
20
+
21
+ | Path | Purpose |
22
+ |------|---------|
23
+ | `baton/` | Main package: CLI, commands, core logic, adapters |
24
+ | `baton/core/` | BatonDocument, schema definitions, config reader |
25
+ | `baton/adapters/` | One file per supported agent tool. Each implements BaseAdapter. |
26
+ | `baton/commands/` | One file per CLI command (init, sync, status, score) |
27
+ | `tests/` | pytest test suite with fixtures/ |
28
+
29
+ ## Tech Stack
30
+
31
+ | Tool | Version | Why | Gotchas |
32
+ |------|---------|-----|---------|
33
+ | Python | 3.10+ | Target audience is Python developers. tomllib (stdlib) available 3.11+; tomli backport for 3.10. | Use try/except tomllib import for 3.10 compat. Never add shims beyond this. |
34
+ | Typer | >=0.12 | Declarative CLI with rich help text and type inference. Wraps Click. | Entry point is baton.cli:main (not :app). All console.print strings must be CP1252-safe on Windows. |
35
+ | Rich | >=13 | Coloured terminal output for sync/status/score tables. | Import Rich directly. Never use Unicode outside basic Latin in console.print() calls on Windows CP1252. |
36
+ | ruamel.yaml | >=0.18 | Round-trip YAML parser that preserves inline # comments on save. PyYAML drops them. | Never switch to PyYAML. YAML fence regex must use \n before closing ``` to avoid false match inside string values. |
37
+ | tomllib/tomli | stdlib 3.11+ / tomli>=2.0 for 3.10 | Read .baton.toml config. No extra dependency on 3.11+. | Open files in binary mode: open(path, 'rb'). |
38
+
39
+ ## Laws (Never Violate)
40
+
41
+ > Hard constraints. Agents must not override these — ever.
42
+
43
+ 1. Never use PyYAML. All BATON.md parsing uses ruamel.yaml to preserve inline comments.
44
+ 2. schema.py is the single source of truth for section/field names. Never hardcode them in score.py or anywhere else.
45
+ 3. sync must never overwrite the full content of an agent file. Always use managed-block markers.
46
+ 4. SCORE_CHECKS in schema.py must total exactly 100 points. The assert enforces this at import time.
47
+ 5. No LLM calls in Increment 1. init, sync, status, score are purely deterministic.
48
+ 6. All Rich console.print() output must use only CP1252-safe (basic ASCII) characters.
49
+
50
+ ## Current Sprint: Ship Phase 1 Increment 1: init + sync + status + score (no LLM)
51
+
52
+ ### ✅ Done
53
+ - pyproject.toml, package skeleton, LICENSE, .gitignore *(confidence: stable)*
54
+ - core/schema.py with SCORE_CHECKS totalling 100 points *(confidence: stable)* — Assert at import time enforces the total.
55
+ - core/document.py: BatonDocument load/save round-trip *(confidence: stable)* — Extracts the yaml fenced block, parses with ruamel.yaml.
56
+ - core/config.py: .baton.toml reader *(confidence: stable)*
57
+ - adapters/base.py: BaseAdapter + managed-block utilities *(confidence: stable)* — upsert_managed_block / extract_managed_block are the core safety primitives.
58
+ - All five adapters (claude, codex, cursor, gemini, copilot) *(confidence: stable)* — Cursor overrides prepare_file() to handle MDC frontmatter.
59
+ - adapters/registry.py: detect_enabled + get_adapters *(confidence: stable)*
60
+ - commands/sync.py + commands/status.py + commands/score.py + commands/init.py *(confidence: stable)*
61
+ - cli.py wiring all four commands + stub for baton end *(confidence: stable)*
62
+ - Full test suite: 107 tests passing *(confidence: stable)*
63
+ - README.md, CONTRIBUTING.md, BATON.md dogfood, CLAUDE.md lessons log *(confidence: stable)*
64
+ - git init + pre-commit hook installed *(confidence: stable)*
65
+
66
+ ### 📋 Up Next
67
+ - Push to GitHub repo (URL from user) *[high]*
68
+ - Increment 2: baton end (git-diff parser + Anthropic summariser + Rich review UI) *[medium]*
69
+
70
+ ## Key Decisions
71
+
72
+ | # | Decision | Why | When | Tool |
73
+ |---|---------|-----|------|------|
74
+ | d001 | Managed-block markers (BATON-START/END) instead of full-file ownership | Users already have hand-written CLAUDE.md content. Overwriting it would be a critical UX failure. | 2026-06-04 | claude-code |
75
+ | d002 | Embed BATON.md template as a Python string in init.py | More reliable than importlib.resources for initial releases. No template file packaging needed. | 2026-06-04 | claude-code |
76
+ | d003 | schema.py defines SCORE_CHECKS; score.py imports them | Prevents scoring logic from drifting from the actual schema when fields change. | 2026-06-04 | claude-code |
77
+ | d004 | BATON.md format: markdown with schema inside a yaml fenced block | Human-readable as a GitHub markdown file, machine-parseable via regex fence extraction. | 2026-06-04 | claude-code |
78
+ | d005 | Pre-commit hook is non-blocking (exits 0, just prints a reminder) | Phase 1 trigger is manual. A blocking hook would annoy users committing unrelated code. | 2026-06-04 | claude-code |
79
+ | d006 | Increment 1 scope: init + sync + status + score (no LLM) | Ship the deterministic core first. Validate value before adding AI complexity. | 2026-06-04 | claude-code |
80
+
81
+ ## Anti-Decisions (Rejected Approaches)
82
+
83
+ > These were explicitly ruled out. Don't re-suggest them.
84
+
85
+ | # | Rejected | Why | When |
86
+ |---|---------|-----|------|
87
+ | a001 | Auto-detecting token limits in other tools | Claude Code, Cursor, and Codex do not expose their token state to external CLIs. Physically impossible in Phase 1. | 2026-06-04 |
88
+ | a002 | PyYAML for BATON.md parsing | PyYAML drops inline # comments on round-trip. ruamel.yaml preserves them. | 2026-06-04 |
89
+ | a003 | Separate template file (baton/templates/BATON.md.template) | importlib.resources is finicky for installable packages in Phase 1. Embedding the template as a string is simpler. | 2026-06-04 |
90
+ | a004 | Full-file sync (replacing the entire CLAUDE.md) | Users have hand-written content in these files. Full replacement would be destructive. | 2026-06-04 |
91
+
92
+ ## Landmines (Looks Wrong, But Intentional)
93
+
94
+ > Do NOT 'fix' these. They are correct as-is.
95
+
96
+ **`baton/core/schema.py (end of file)`**
97
+ - Looks like: Redundant assert after a list definition
98
+ - Actually: Enforces that SCORE_CHECKS always sums to exactly 100. If you add/remove a check and forget to rebalance points, this fails at import time.
99
+
100
+ **`baton/adapters/cursor.py: prepare_file()`**
101
+ - Looks like: Overcomplicated file-write logic vs. the four simpler adapters
102
+ - Actually: Cursor .mdc files require YAML frontmatter at the top. The frontmatter must be outside the managed block so it survives re-syncs.
103
+
104
+ **`baton/core/document.py: _YAML_FENCE_RE`**
105
+ - Looks like: Unnecessary \n before closing ``` in the regex
106
+ - Actually: Required. Without it, triple-backticks inside YAML string values prematurely terminate the match.
107
+
108
+ ## Open Questions
109
+
110
+ > Do NOT make unilateral decisions on these. Surface them to the human first.
111
+
112
+ 🔴 **[q001]** Should baton init auto-run baton sync immediately after scaffolding?
113
+ - Context: The template BATON.md has all empty values. Syncing empty content produces a mostly-empty context file.
114
+ - Discussion: Current: init does NOT auto-sync. User fills in BATON.md first, then runs baton sync.
115
+
116
+ 🔴 **[q002]** Should extract_managed_block skip content inside triple-backtick fences?
117
+ - Context: Currently, if a user writes the literal managed-block marker inside a code fence example, sync corrupts the file. See mistake log entry [2026-06-04].
118
+ - Discussion: Low priority for Phase 1 since it only affects files that document Baton itself.
119
+
120
+ <!-- BATON:END -->