codex-autorunner 0.1.2__py3-none-any.whl → 1.0.0__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.
Files changed (189) hide show
  1. codex_autorunner/__main__.py +4 -0
  2. codex_autorunner/agents/opencode/client.py +68 -35
  3. codex_autorunner/agents/opencode/logging.py +21 -5
  4. codex_autorunner/agents/opencode/run_prompt.py +1 -0
  5. codex_autorunner/agents/opencode/runtime.py +118 -30
  6. codex_autorunner/agents/opencode/supervisor.py +36 -48
  7. codex_autorunner/agents/registry.py +136 -8
  8. codex_autorunner/api.py +25 -0
  9. codex_autorunner/bootstrap.py +16 -35
  10. codex_autorunner/cli.py +157 -139
  11. codex_autorunner/core/about_car.py +44 -32
  12. codex_autorunner/core/adapter_utils.py +21 -0
  13. codex_autorunner/core/app_server_logging.py +7 -3
  14. codex_autorunner/core/app_server_prompts.py +27 -260
  15. codex_autorunner/core/app_server_threads.py +15 -26
  16. codex_autorunner/core/codex_runner.py +6 -0
  17. codex_autorunner/core/config.py +390 -100
  18. codex_autorunner/core/docs.py +10 -2
  19. codex_autorunner/core/drafts.py +82 -0
  20. codex_autorunner/core/engine.py +278 -262
  21. codex_autorunner/core/flows/__init__.py +25 -0
  22. codex_autorunner/core/flows/controller.py +178 -0
  23. codex_autorunner/core/flows/definition.py +82 -0
  24. codex_autorunner/core/flows/models.py +75 -0
  25. codex_autorunner/core/flows/runtime.py +351 -0
  26. codex_autorunner/core/flows/store.py +485 -0
  27. codex_autorunner/core/flows/transition.py +133 -0
  28. codex_autorunner/core/flows/worker_process.py +242 -0
  29. codex_autorunner/core/hub.py +15 -9
  30. codex_autorunner/core/locks.py +4 -0
  31. codex_autorunner/core/prompt.py +15 -7
  32. codex_autorunner/core/redaction.py +29 -0
  33. codex_autorunner/core/review_context.py +5 -8
  34. codex_autorunner/core/run_index.py +6 -0
  35. codex_autorunner/core/runner_process.py +5 -2
  36. codex_autorunner/core/state.py +0 -88
  37. codex_autorunner/core/static_assets.py +55 -0
  38. codex_autorunner/core/supervisor_utils.py +67 -0
  39. codex_autorunner/core/update.py +20 -11
  40. codex_autorunner/core/update_runner.py +2 -0
  41. codex_autorunner/core/utils.py +29 -2
  42. codex_autorunner/discovery.py +2 -4
  43. codex_autorunner/flows/ticket_flow/__init__.py +3 -0
  44. codex_autorunner/flows/ticket_flow/definition.py +91 -0
  45. codex_autorunner/integrations/agents/__init__.py +27 -0
  46. codex_autorunner/integrations/agents/agent_backend.py +142 -0
  47. codex_autorunner/integrations/agents/codex_backend.py +307 -0
  48. codex_autorunner/integrations/agents/opencode_backend.py +325 -0
  49. codex_autorunner/integrations/agents/run_event.py +71 -0
  50. codex_autorunner/integrations/app_server/client.py +576 -92
  51. codex_autorunner/integrations/app_server/supervisor.py +59 -33
  52. codex_autorunner/integrations/telegram/adapter.py +141 -167
  53. codex_autorunner/integrations/telegram/api_schemas.py +120 -0
  54. codex_autorunner/integrations/telegram/config.py +175 -0
  55. codex_autorunner/integrations/telegram/constants.py +16 -1
  56. codex_autorunner/integrations/telegram/dispatch.py +17 -0
  57. codex_autorunner/integrations/telegram/doctor.py +47 -0
  58. codex_autorunner/integrations/telegram/handlers/callbacks.py +0 -4
  59. codex_autorunner/integrations/telegram/handlers/commands/__init__.py +2 -0
  60. codex_autorunner/integrations/telegram/handlers/commands/execution.py +53 -57
  61. codex_autorunner/integrations/telegram/handlers/commands/files.py +2 -6
  62. codex_autorunner/integrations/telegram/handlers/commands/flows.py +227 -0
  63. codex_autorunner/integrations/telegram/handlers/commands/formatting.py +1 -1
  64. codex_autorunner/integrations/telegram/handlers/commands/github.py +41 -582
  65. codex_autorunner/integrations/telegram/handlers/commands/workspace.py +8 -8
  66. codex_autorunner/integrations/telegram/handlers/commands_runtime.py +133 -475
  67. codex_autorunner/integrations/telegram/handlers/commands_spec.py +11 -4
  68. codex_autorunner/integrations/telegram/handlers/messages.py +120 -9
  69. codex_autorunner/integrations/telegram/helpers.py +88 -16
  70. codex_autorunner/integrations/telegram/outbox.py +208 -37
  71. codex_autorunner/integrations/telegram/progress_stream.py +3 -10
  72. codex_autorunner/integrations/telegram/service.py +214 -40
  73. codex_autorunner/integrations/telegram/state.py +100 -2
  74. codex_autorunner/integrations/telegram/ticket_flow_bridge.py +322 -0
  75. codex_autorunner/integrations/telegram/transport.py +36 -3
  76. codex_autorunner/integrations/telegram/trigger_mode.py +53 -0
  77. codex_autorunner/manifest.py +2 -0
  78. codex_autorunner/plugin_api.py +22 -0
  79. codex_autorunner/routes/__init__.py +23 -14
  80. codex_autorunner/routes/analytics.py +239 -0
  81. codex_autorunner/routes/base.py +81 -109
  82. codex_autorunner/routes/file_chat.py +836 -0
  83. codex_autorunner/routes/flows.py +980 -0
  84. codex_autorunner/routes/messages.py +459 -0
  85. codex_autorunner/routes/system.py +6 -1
  86. codex_autorunner/routes/usage.py +87 -0
  87. codex_autorunner/routes/workspace.py +271 -0
  88. codex_autorunner/server.py +2 -1
  89. codex_autorunner/static/agentControls.js +1 -0
  90. codex_autorunner/static/agentEvents.js +248 -0
  91. codex_autorunner/static/app.js +25 -22
  92. codex_autorunner/static/autoRefresh.js +29 -1
  93. codex_autorunner/static/bootstrap.js +1 -0
  94. codex_autorunner/static/bus.js +1 -0
  95. codex_autorunner/static/cache.js +1 -0
  96. codex_autorunner/static/constants.js +20 -4
  97. codex_autorunner/static/dashboard.js +162 -196
  98. codex_autorunner/static/diffRenderer.js +37 -0
  99. codex_autorunner/static/docChatCore.js +324 -0
  100. codex_autorunner/static/docChatStorage.js +65 -0
  101. codex_autorunner/static/docChatVoice.js +65 -0
  102. codex_autorunner/static/docEditor.js +133 -0
  103. codex_autorunner/static/env.js +1 -0
  104. codex_autorunner/static/eventSummarizer.js +166 -0
  105. codex_autorunner/static/fileChat.js +182 -0
  106. codex_autorunner/static/health.js +155 -0
  107. codex_autorunner/static/hub.js +41 -118
  108. codex_autorunner/static/index.html +787 -858
  109. codex_autorunner/static/liveUpdates.js +1 -0
  110. codex_autorunner/static/loader.js +1 -0
  111. codex_autorunner/static/messages.js +470 -0
  112. codex_autorunner/static/mobileCompact.js +2 -1
  113. codex_autorunner/static/settings.js +24 -211
  114. codex_autorunner/static/styles.css +7567 -3865
  115. codex_autorunner/static/tabs.js +28 -5
  116. codex_autorunner/static/terminal.js +14 -0
  117. codex_autorunner/static/terminalManager.js +34 -59
  118. codex_autorunner/static/ticketChatActions.js +333 -0
  119. codex_autorunner/static/ticketChatEvents.js +16 -0
  120. codex_autorunner/static/ticketChatStorage.js +16 -0
  121. codex_autorunner/static/ticketChatStream.js +264 -0
  122. codex_autorunner/static/ticketEditor.js +750 -0
  123. codex_autorunner/static/ticketVoice.js +9 -0
  124. codex_autorunner/static/tickets.js +1315 -0
  125. codex_autorunner/static/utils.js +32 -3
  126. codex_autorunner/static/voice.js +1 -0
  127. codex_autorunner/static/workspace.js +672 -0
  128. codex_autorunner/static/workspaceApi.js +53 -0
  129. codex_autorunner/static/workspaceFileBrowser.js +504 -0
  130. codex_autorunner/tickets/__init__.py +20 -0
  131. codex_autorunner/tickets/agent_pool.py +377 -0
  132. codex_autorunner/tickets/files.py +85 -0
  133. codex_autorunner/tickets/frontmatter.py +55 -0
  134. codex_autorunner/tickets/lint.py +102 -0
  135. codex_autorunner/tickets/models.py +95 -0
  136. codex_autorunner/tickets/outbox.py +232 -0
  137. codex_autorunner/tickets/replies.py +179 -0
  138. codex_autorunner/tickets/runner.py +823 -0
  139. codex_autorunner/tickets/spec_ingest.py +77 -0
  140. codex_autorunner/web/app.py +269 -91
  141. codex_autorunner/web/middleware.py +3 -4
  142. codex_autorunner/web/schemas.py +89 -109
  143. codex_autorunner/web/static_assets.py +1 -44
  144. codex_autorunner/workspace/__init__.py +40 -0
  145. codex_autorunner/workspace/paths.py +319 -0
  146. {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/METADATA +18 -21
  147. codex_autorunner-1.0.0.dist-info/RECORD +251 -0
  148. {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/WHEEL +1 -1
  149. codex_autorunner/agents/execution/policy.py +0 -292
  150. codex_autorunner/agents/factory.py +0 -52
  151. codex_autorunner/agents/orchestrator.py +0 -358
  152. codex_autorunner/core/doc_chat.py +0 -1446
  153. codex_autorunner/core/snapshot.py +0 -580
  154. codex_autorunner/integrations/github/chatops.py +0 -268
  155. codex_autorunner/integrations/github/pr_flow.py +0 -1314
  156. codex_autorunner/routes/docs.py +0 -381
  157. codex_autorunner/routes/github.py +0 -327
  158. codex_autorunner/routes/runs.py +0 -250
  159. codex_autorunner/spec_ingest.py +0 -812
  160. codex_autorunner/static/docChatActions.js +0 -287
  161. codex_autorunner/static/docChatEvents.js +0 -300
  162. codex_autorunner/static/docChatRender.js +0 -205
  163. codex_autorunner/static/docChatStream.js +0 -361
  164. codex_autorunner/static/docs.js +0 -20
  165. codex_autorunner/static/docsClipboard.js +0 -69
  166. codex_autorunner/static/docsCrud.js +0 -257
  167. codex_autorunner/static/docsDocUpdates.js +0 -62
  168. codex_autorunner/static/docsDrafts.js +0 -16
  169. codex_autorunner/static/docsElements.js +0 -69
  170. codex_autorunner/static/docsInit.js +0 -285
  171. codex_autorunner/static/docsParse.js +0 -160
  172. codex_autorunner/static/docsSnapshot.js +0 -87
  173. codex_autorunner/static/docsSpecIngest.js +0 -263
  174. codex_autorunner/static/docsState.js +0 -127
  175. codex_autorunner/static/docsThreadRegistry.js +0 -44
  176. codex_autorunner/static/docsUi.js +0 -153
  177. codex_autorunner/static/docsVoice.js +0 -56
  178. codex_autorunner/static/github.js +0 -504
  179. codex_autorunner/static/logs.js +0 -678
  180. codex_autorunner/static/review.js +0 -157
  181. codex_autorunner/static/runs.js +0 -418
  182. codex_autorunner/static/snapshot.js +0 -124
  183. codex_autorunner/static/state.js +0 -94
  184. codex_autorunner/static/todoPreview.js +0 -27
  185. codex_autorunner/workspace.py +0 -16
  186. codex_autorunner-0.1.2.dist-info/RECORD +0 -222
  187. {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/entry_points.txt +0 -0
  188. {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/licenses/LICENSE +0 -0
  189. {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import importlib.metadata
3
4
  import logging
4
5
  from dataclasses import dataclass
5
- from typing import Any, Callable, Literal, Optional
6
+ from typing import Any, Callable, Iterable, Literal, Optional
6
7
 
8
+ from ..plugin_api import CAR_AGENT_ENTRYPOINT_GROUP, CAR_PLUGIN_API_VERSION
7
9
  from .base import AgentHarness
8
10
  from .codex.harness import CodexHarness
9
11
  from .opencode.harness import OpenCodeHarness
@@ -22,11 +24,20 @@ AgentCapability = Literal[
22
24
 
23
25
  @dataclass(frozen=True)
24
26
  class AgentDescriptor:
27
+ """A registered agent backend.
28
+
29
+ Built-in backends live in `_BUILTIN_AGENTS`. Additional backends MAY be loaded
30
+ via Python entry points (see `CAR_AGENT_ENTRYPOINT_GROUP`).
31
+
32
+ Plugins SHOULD set `plugin_api_version` to `CAR_PLUGIN_API_VERSION`.
33
+ """
34
+
25
35
  id: str
26
36
  name: str
27
37
  capabilities: frozenset[AgentCapability]
28
38
  make_harness: Callable[[Any], AgentHarness]
29
39
  healthcheck: Optional[Callable[[Any], bool]] = None
40
+ plugin_api_version: int = CAR_PLUGIN_API_VERSION
30
41
 
31
42
 
32
43
  def _make_codex_harness(ctx: Any) -> AgentHarness:
@@ -54,7 +65,7 @@ def _check_opencode_health(ctx: Any) -> bool:
54
65
  return supervisor is not None
55
66
 
56
67
 
57
- _REGISTERED_AGENTS: dict[str, AgentDescriptor] = {
68
+ _BUILTIN_AGENTS: dict[str, AgentDescriptor] = {
58
69
  "codex": AgentDescriptor(
59
70
  id="codex",
60
71
  name="Codex",
@@ -88,32 +99,146 @@ _REGISTERED_AGENTS: dict[str, AgentDescriptor] = {
88
99
  ),
89
100
  }
90
101
 
102
+ # Lazy-loaded cache of built-in + plugin agents.
103
+ _AGENT_CACHE: Optional[dict[str, AgentDescriptor]] = None
104
+
105
+
106
+ def _select_entry_points(group: str) -> Iterable[importlib.metadata.EntryPoint]:
107
+ """Compatibility wrapper for `importlib.metadata.entry_points()` across py versions."""
108
+
109
+ eps = importlib.metadata.entry_points()
110
+ # Python 3.9: may return a dict
111
+ if isinstance(eps, dict):
112
+ return eps.get(group, [])
113
+ if hasattr(eps, "select"):
114
+ return list(eps.select(group=group))
115
+ return []
116
+
117
+
118
+ def _load_agent_plugins() -> dict[str, AgentDescriptor]:
119
+ loaded: dict[str, AgentDescriptor] = {}
120
+ for ep in _select_entry_points(CAR_AGENT_ENTRYPOINT_GROUP):
121
+ try:
122
+ obj = ep.load()
123
+ except Exception as exc: # noqa: BLE001
124
+ _logger.warning(
125
+ "Failed to load agent plugin entry point %s:%s: %s",
126
+ ep.group,
127
+ ep.name,
128
+ exc,
129
+ )
130
+ continue
131
+
132
+ descriptor: Optional[AgentDescriptor] = None
133
+ if isinstance(obj, AgentDescriptor):
134
+ descriptor = obj
135
+ elif callable(obj):
136
+ try:
137
+ maybe = obj()
138
+ except Exception as exc: # noqa: BLE001
139
+ _logger.warning(
140
+ "Agent plugin entry point %s:%s factory failed: %s",
141
+ ep.group,
142
+ ep.name,
143
+ exc,
144
+ )
145
+ continue
146
+ if isinstance(maybe, AgentDescriptor):
147
+ descriptor = maybe
148
+
149
+ if descriptor is None:
150
+ _logger.warning(
151
+ "Ignoring agent plugin entry point %s:%s: expected AgentDescriptor or factory",
152
+ ep.group,
153
+ ep.name,
154
+ )
155
+ continue
156
+
157
+ agent_id = (descriptor.id or "").strip().lower()
158
+ if not agent_id:
159
+ _logger.warning(
160
+ "Ignoring agent plugin entry point %s:%s: missing id",
161
+ ep.group,
162
+ ep.name,
163
+ )
164
+ continue
165
+
166
+ if descriptor.plugin_api_version != CAR_PLUGIN_API_VERSION:
167
+ _logger.warning(
168
+ "Ignoring agent plugin %s (api_version=%s): expected %s",
169
+ agent_id,
170
+ descriptor.plugin_api_version,
171
+ CAR_PLUGIN_API_VERSION,
172
+ )
173
+ continue
174
+
175
+ if agent_id in _BUILTIN_AGENTS:
176
+ _logger.warning(
177
+ "Ignoring agent plugin %s: conflicts with built-in agent id",
178
+ agent_id,
179
+ )
180
+ continue
181
+ if agent_id in loaded:
182
+ _logger.warning(
183
+ "Ignoring duplicate agent plugin id %s from entry point %s:%s",
184
+ agent_id,
185
+ ep.group,
186
+ ep.name,
187
+ )
188
+ continue
189
+
190
+ loaded[agent_id] = descriptor
191
+ _logger.info("Loaded agent plugin: %s (%s)", agent_id, descriptor.name)
192
+
193
+ return loaded
194
+
195
+
196
+ def _all_agents() -> dict[str, AgentDescriptor]:
197
+ global _AGENT_CACHE
198
+ if _AGENT_CACHE is None:
199
+ agents = _BUILTIN_AGENTS.copy()
200
+ agents.update(_load_agent_plugins())
201
+ _AGENT_CACHE = agents
202
+ return _AGENT_CACHE
203
+
204
+
205
+ def reload_agents() -> dict[str, AgentDescriptor]:
206
+ """Clear the plugin cache and reload agent backends.
207
+
208
+ This is primarily useful for tests and local development.
209
+ """
210
+
211
+ global _AGENT_CACHE
212
+ _AGENT_CACHE = None
213
+ return get_registered_agents()
214
+
91
215
 
92
216
  def get_registered_agents() -> dict[str, AgentDescriptor]:
93
- return _REGISTERED_AGENTS.copy()
217
+ return _all_agents().copy()
94
218
 
95
219
 
96
220
  def get_available_agents(app_ctx: Any) -> dict[str, AgentDescriptor]:
97
- available = {}
98
- for agent_id, descriptor in _REGISTERED_AGENTS.items():
221
+ available: dict[str, AgentDescriptor] = {}
222
+ for agent_id, descriptor in _all_agents().items():
99
223
  if descriptor.healthcheck is None or descriptor.healthcheck(app_ctx):
100
224
  available[agent_id] = descriptor
101
225
  return available
102
226
 
103
227
 
104
228
  def get_agent_descriptor(agent_id: str) -> Optional[AgentDescriptor]:
105
- return _REGISTERED_AGENTS.get(agent_id)
229
+ normalized = (agent_id or "").strip().lower()
230
+ return _all_agents().get(normalized)
106
231
 
107
232
 
108
233
  def validate_agent_id(agent_id: str) -> str:
109
234
  normalized = (agent_id or "").strip().lower()
110
- if normalized not in _REGISTERED_AGENTS:
235
+ if normalized not in _all_agents():
111
236
  raise ValueError(f"Unknown agent: {agent_id!r}")
112
237
  return normalized
113
238
 
114
239
 
115
240
  def has_capability(agent_id: str, capability: AgentCapability) -> bool:
116
- descriptor = _REGISTERED_AGENTS.get(agent_id)
241
+ descriptor = get_agent_descriptor(agent_id)
117
242
  if descriptor is None:
118
243
  return False
119
244
  return capability in descriptor.capabilities
@@ -122,9 +247,12 @@ def has_capability(agent_id: str, capability: AgentCapability) -> bool:
122
247
  __all__ = [
123
248
  "AgentCapability",
124
249
  "AgentDescriptor",
250
+ "CAR_PLUGIN_API_VERSION",
251
+ "CAR_AGENT_ENTRYPOINT_GROUP",
125
252
  "get_registered_agents",
126
253
  "get_available_agents",
127
254
  "get_agent_descriptor",
128
255
  "validate_agent_id",
129
256
  "has_capability",
257
+ "reload_agents",
130
258
  ]
@@ -0,0 +1,25 @@
1
+ """Stable public API for Codex Autorunner plugins.
2
+
3
+ Everything else in the codebase should be treated as internal unless documented.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from .agents.base import AgentHarness
9
+ from .agents.registry import AgentCapability, AgentDescriptor, reload_agents
10
+ from .agents.types import AgentId, ConversationRef, ModelCatalog, ModelSpec, TurnRef
11
+ from .plugin_api import CAR_AGENT_ENTRYPOINT_GROUP, CAR_PLUGIN_API_VERSION
12
+
13
+ __all__ = [
14
+ "AgentCapability",
15
+ "AgentDescriptor",
16
+ "AgentHarness",
17
+ "AgentId",
18
+ "ConversationRef",
19
+ "ModelCatalog",
20
+ "ModelSpec",
21
+ "TurnRef",
22
+ "CAR_AGENT_ENTRYPOINT_GROUP",
23
+ "CAR_PLUGIN_API_VERSION",
24
+ "reload_agents",
25
+ ]
@@ -15,41 +15,17 @@ from .core.utils import atomic_write
15
15
  from .manifest import load_manifest
16
16
 
17
17
  GITIGNORE_CONTENT = "*"
18
+ GENERATED_CONFIG_HEADER = "# GENERATED by CAR - DO NOT EDIT\n"
18
19
 
19
20
 
20
21
  def sample_todo() -> str:
21
22
  return """# TODO\n\n- [ ] Replace this item with your first task\n- [ ] Add another task\n- [x] Example completed item\n"""
22
23
 
23
24
 
24
- def sample_opinions() -> str:
25
- return """# Opinions\n\n- Prefer small, well-tested changes.\n- Keep docs in sync with code.\n- Avoid unnecessary dependencies.\n"""
26
-
27
-
28
25
  def sample_spec() -> str:
29
26
  return """# Spec\n\n## Context\n- Add project background and goals here.\n\n## Requirements\n- Requirement 1\n- Requirement 2\n\n## Non-goals\n- Out of scope items\n"""
30
27
 
31
28
 
32
- def sample_summary() -> str:
33
- return """# Summary
34
-
35
- This doc is the **user-facing report and handoff** for work done by CAR agents.
36
-
37
- Use it for:
38
- - Anything that requires **user action** or an **external party** (not agents).
39
- - Unresolved decisions or blockers that agents can’t finish autonomously.
40
- - A final condensed report once TODO is complete.
41
-
42
- ## External/user actions
43
- - (none)
44
-
45
- ## Open questions / blockers
46
- - (none)
47
-
48
- ## Final report
49
- - (pending)
50
- """
51
-
52
-
53
29
  def _seed_doc(path: Path, force: bool, content: str) -> None:
54
30
  if path.exists() and not force:
55
31
  return
@@ -82,6 +58,7 @@ def write_hub_config(hub_root: Path, force: bool = False) -> Path:
82
58
  return config_path
83
59
  config_path.parent.mkdir(parents=True, exist_ok=True)
84
60
  with config_path.open("w", encoding="utf-8") as f:
61
+ f.write(GENERATED_CONFIG_HEADER)
85
62
  yaml.safe_dump(
86
63
  resolve_hub_config_data(hub_root),
87
64
  f,
@@ -116,21 +93,25 @@ def seed_repo_files(
116
93
  if not log_path.exists() or force:
117
94
  log_path.write_text("", encoding="utf-8")
118
95
 
119
- _seed_doc(ca_dir / "TODO.md", force, sample_todo())
120
- _seed_doc(ca_dir / "PROGRESS.md", force, "# Progress\n\n")
121
- _seed_doc(ca_dir / "OPINIONS.md", force, sample_opinions())
122
- _seed_doc(ca_dir / "SPEC.md", force, sample_spec())
123
- _seed_doc(ca_dir / "SUMMARY.md", force, sample_summary())
96
+ tickets_dir = ca_dir / "tickets"
97
+ if not tickets_dir.exists() or force:
98
+ tickets_dir.mkdir(parents=True, exist_ok=True)
99
+
100
+ workspace_dir = ca_dir / "workspace"
101
+ if not workspace_dir.exists() or force:
102
+ workspace_dir.mkdir(parents=True, exist_ok=True)
103
+
104
+ _seed_doc(workspace_dir / "active_context.md", force, sample_todo())
105
+ _seed_doc(workspace_dir / "decisions.md", force, "# Decisions\n\n")
106
+ _seed_doc(workspace_dir / "spec.md", force, sample_spec())
124
107
 
125
108
  # Seed an always-available briefing doc for interactive Codex sessions.
126
109
  ensure_about_car_file_for_repo(
127
110
  repo_root,
128
111
  doc_paths={
129
- "todo": ca_dir / "TODO.md",
130
- "progress": ca_dir / "PROGRESS.md",
131
- "opinions": ca_dir / "OPINIONS.md",
132
- "spec": ca_dir / "SPEC.md",
133
- "summary": ca_dir / "SUMMARY.md",
112
+ "active_context": workspace_dir / "active_context.md",
113
+ "decisions": workspace_dir / "decisions.md",
114
+ "spec": workspace_dir / "spec.md",
134
115
  },
135
116
  force=force,
136
117
  )