codeforge-dev 1.4.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.
Files changed (131) hide show
  1. package/.devcontainer/.env +22 -0
  2. package/.devcontainer/CHANGELOG.md +197 -0
  3. package/.devcontainer/CLAUDE.md +117 -0
  4. package/.devcontainer/README.md +222 -0
  5. package/.devcontainer/config/main-system-prompt.md +502 -0
  6. package/.devcontainer/config/settings.json +47 -0
  7. package/.devcontainer/devcontainer.json +94 -0
  8. package/.devcontainer/features/README.md +113 -0
  9. package/.devcontainer/features/agent-browser/README.md +65 -0
  10. package/.devcontainer/features/agent-browser/devcontainer-feature.json +23 -0
  11. package/.devcontainer/features/agent-browser/install.sh +79 -0
  12. package/.devcontainer/features/ast-grep/README.md +24 -0
  13. package/.devcontainer/features/ast-grep/devcontainer-feature.json +24 -0
  14. package/.devcontainer/features/ast-grep/install.sh +51 -0
  15. package/.devcontainer/features/ccstatusline/README.md +296 -0
  16. package/.devcontainer/features/ccstatusline/devcontainer-feature.json +19 -0
  17. package/.devcontainer/features/ccstatusline/install.sh +290 -0
  18. package/.devcontainer/features/ccusage/README.md +205 -0
  19. package/.devcontainer/features/ccusage/devcontainer-feature.json +38 -0
  20. package/.devcontainer/features/ccusage/install.sh +132 -0
  21. package/.devcontainer/features/claude-code/README.md +498 -0
  22. package/.devcontainer/features/claude-code/config/settings.json +36 -0
  23. package/.devcontainer/features/claude-code/config/system-prompt.md +118 -0
  24. package/.devcontainer/features/claude-code/config/world-building-sp.md +1432 -0
  25. package/.devcontainer/features/claude-code/devcontainer-feature.json +42 -0
  26. package/.devcontainer/features/claude-code/install.sh +466 -0
  27. package/.devcontainer/features/claude-monitor/README.md +74 -0
  28. package/.devcontainer/features/claude-monitor/devcontainer-feature.json +38 -0
  29. package/.devcontainer/features/claude-monitor/install.sh +99 -0
  30. package/.devcontainer/features/lsp-servers/README.md +85 -0
  31. package/.devcontainer/features/lsp-servers/devcontainer-feature.json +40 -0
  32. package/.devcontainer/features/lsp-servers/install.sh +116 -0
  33. package/.devcontainer/features/mcp-qdrant/CHANGES.md +399 -0
  34. package/.devcontainer/features/mcp-qdrant/README.md +474 -0
  35. package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +57 -0
  36. package/.devcontainer/features/mcp-qdrant/install.sh +295 -0
  37. package/.devcontainer/features/mcp-qdrant/poststart-hook.sh +129 -0
  38. package/.devcontainer/features/mcp-reasoner/README.md +177 -0
  39. package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +20 -0
  40. package/.devcontainer/features/mcp-reasoner/install.sh +177 -0
  41. package/.devcontainer/features/mcp-reasoner/poststart-hook.sh +67 -0
  42. package/.devcontainer/features/notify-hook/README.md +86 -0
  43. package/.devcontainer/features/notify-hook/devcontainer-feature.json +23 -0
  44. package/.devcontainer/features/notify-hook/install.sh +38 -0
  45. package/.devcontainer/features/splitrail/README.md +140 -0
  46. package/.devcontainer/features/splitrail/devcontainer-feature.json +34 -0
  47. package/.devcontainer/features/splitrail/install.sh +129 -0
  48. package/.devcontainer/features/tree-sitter/README.md +138 -0
  49. package/.devcontainer/features/tree-sitter/devcontainer-feature.json +52 -0
  50. package/.devcontainer/features/tree-sitter/install.sh +173 -0
  51. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +106 -0
  52. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +7 -0
  53. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/hooks/hooks.json +17 -0
  54. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-file.py +101 -0
  55. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +7 -0
  56. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +17 -0
  57. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +137 -0
  58. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/.claude-plugin/plugin.json +8 -0
  59. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/SKILL.md +387 -0
  60. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/references/cli-flags-and-output.md +312 -0
  61. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/references/sdk-and-mcp.md +569 -0
  62. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/SKILL.md +309 -0
  63. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/references/compose-services.md +438 -0
  64. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/references/dockerfile-patterns.md +340 -0
  65. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/SKILL.md +412 -0
  66. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/references/container-lifecycle.md +388 -0
  67. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/references/resources-and-security.md +444 -0
  68. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/SKILL.md +344 -0
  69. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/middleware-and-lifespan.md +254 -0
  70. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/pydantic-models.md +245 -0
  71. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/routing-and-dependencies.md +255 -0
  72. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/sse-and-streaming.md +318 -0
  73. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/SKILL.md +345 -0
  74. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/references/agents-and-tools.md +271 -0
  75. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/references/models-and-streaming.md +422 -0
  76. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/SKILL.md +220 -0
  77. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/cross-vendor-principles.md +139 -0
  78. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/patterns-and-antipatterns.md +376 -0
  79. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/skill-authoring-patterns.md +356 -0
  80. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/SKILL.md +329 -0
  81. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/advanced-queries.md +314 -0
  82. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/javascript-patterns.md +323 -0
  83. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/python-patterns.md +354 -0
  84. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/schema-and-pragmas.md +326 -0
  85. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/SKILL.md +356 -0
  86. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/ai-sdk-svelte.md +128 -0
  87. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/component-patterns.md +332 -0
  88. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/layercake.md +203 -0
  89. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/migration-guide.md +350 -0
  90. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/runes-and-reactivity.md +328 -0
  91. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/spa-and-routing.md +262 -0
  92. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/svelte-dnd-action.md +181 -0
  93. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/SKILL.md +414 -0
  94. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/references/fastapi-testing.md +411 -0
  95. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/references/svelte-testing.md +538 -0
  96. package/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json +7 -0
  97. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json +7 -0
  98. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json +17 -0
  99. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +110 -0
  100. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json +7 -0
  101. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +17 -0
  102. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +7 -0
  103. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +17 -0
  104. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json +7 -0
  105. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json +17 -0
  106. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +108 -0
  107. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272create-pr.md +337 -0
  108. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272new.md +166 -0
  109. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272review-commit.md +290 -0
  110. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272work.md +257 -0
  111. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json +8 -0
  112. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/system-prompt.md +184 -0
  113. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/.claude-plugin/plugin.json +6 -0
  114. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/config/planning-instructions.md +14 -0
  115. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/functional-conjuring-map.md +989 -0
  116. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/hooks/hooks.json +33 -0
  117. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/__pycache__/post-enhance-task.cpython-314.pyc +0 -0
  118. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhance-planning.py +71 -0
  119. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-plan.sh +68 -0
  120. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-task.sh +120 -0
  121. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-plan.py +133 -0
  122. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-task.py +253 -0
  123. package/.devcontainer/scripts/setup-aliases.sh +80 -0
  124. package/.devcontainer/scripts/setup-config.sh +28 -0
  125. package/.devcontainer/scripts/setup-irie-claude.sh +32 -0
  126. package/.devcontainer/scripts/setup-plugins.sh +80 -0
  127. package/.devcontainer/scripts/setup.sh +58 -0
  128. package/LICENSE.txt +674 -0
  129. package/README.md +267 -0
  130. package/package.json +44 -0
  131. package/setup.js +83 -0
@@ -0,0 +1,388 @@
1
+ # Container Lifecycle -- Deep Dive
2
+
3
+ ## 1. Container Create and Run Options
4
+
5
+ ### Full run() Signature (Key Parameters)
6
+
7
+ ```python
8
+ container = client.containers.run(
9
+ image="python:3.12-slim",
10
+ command="python app.py",
11
+ name="my-app",
12
+ detach=True,
13
+
14
+ # Networking
15
+ ports={"8000/tcp": 8080, "8443/tcp": ("127.0.0.1", 8443)},
16
+ network="app-net",
17
+ hostname="app-host",
18
+
19
+ # Environment
20
+ environment={"DEBUG": "1", "DB_URL": "sqlite:///data.db"},
21
+ working_dir="/app",
22
+ user="1000:1000",
23
+
24
+ # Storage
25
+ volumes={"/host/data": {"bind": "/data", "mode": "rw"}},
26
+ mounts=[Mount(target="/config", source="/host/config", type="bind", read_only=True)],
27
+ tmpfs={"/tmp": "size=100m"},
28
+ read_only=True,
29
+
30
+ # Lifecycle
31
+ remove=True, # auto-remove on exit
32
+ init=True, # use tini as PID 1
33
+ restart_policy={"Name": "on-failure", "MaximumRetryCount": 3},
34
+ stop_signal="SIGTERM",
35
+
36
+ # Resources
37
+ mem_limit="512m",
38
+ nano_cpus=500_000_000, # 0.5 CPU
39
+
40
+ # Labels
41
+ labels={"app": "web", "version": "1.0"},
42
+
43
+ # Health
44
+ healthcheck={
45
+ "test": ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"],
46
+ "interval": 10_000_000_000,
47
+ "timeout": 5_000_000_000,
48
+ "retries": 3,
49
+ "start_period": 30_000_000_000,
50
+ },
51
+ )
52
+ ```
53
+
54
+ ### create() vs run()
55
+
56
+ `create()` accepts the same parameters as `run()` but does not start the container:
57
+
58
+ ```python
59
+ container = client.containers.create("alpine", command="echo hello", name="one-off")
60
+ # Configure further if needed...
61
+ container.start()
62
+ output = container.wait()
63
+ logs = container.logs()
64
+ container.remove()
65
+ ```
66
+
67
+ Use `create()` + `start()` when:
68
+ - Pre-start inspection or configuration is needed.
69
+ - Attaching to streams before start.
70
+ - Explicit lifecycle control is required.
71
+
72
+ ---
73
+
74
+ ## 2. exec_run Patterns
75
+
76
+ ### Basic Execution
77
+
78
+ ```python
79
+ result = container.exec_run("python -c 'print(1+1)'")
80
+ assert result.exit_code == 0
81
+ assert result.output == b"2\n"
82
+ ```
83
+
84
+ ### Streaming Output
85
+
86
+ For long-running commands or real-time output:
87
+
88
+ ```python
89
+ result = container.exec_run(
90
+ "python train_model.py --epochs 100",
91
+ stream=True,
92
+ demux=True,
93
+ )
94
+ for stdout_chunk, stderr_chunk in result.output:
95
+ if stdout_chunk:
96
+ print(stdout_chunk.decode(), end="")
97
+ if stderr_chunk:
98
+ print(f"ERR: {stderr_chunk.decode()}", end="")
99
+ ```
100
+
101
+ With `stream=True`, `result.output` is a generator. With `demux=True`, each yielded item is a tuple `(stdout_bytes | None, stderr_bytes | None)`.
102
+
103
+ ### Detached Execution
104
+
105
+ Fire-and-forget commands:
106
+
107
+ ```python
108
+ result = container.exec_run("cleanup.sh", detach=True)
109
+ # result.output is None; command runs in background
110
+ ```
111
+
112
+ ### Interactive-Style Execution
113
+
114
+ ```python
115
+ result = container.exec_run(
116
+ "/bin/sh -c 'cd /app && python manage.py migrate'",
117
+ environment={"DJANGO_SETTINGS_MODULE": "config.production"},
118
+ workdir="/app",
119
+ user="django",
120
+ )
121
+ if result.exit_code != 0:
122
+ raise RuntimeError(f"Migration failed: {result.output.decode()}")
123
+ ```
124
+
125
+ ### Full exec_run Signature
126
+
127
+ ```python
128
+ container.exec_run(
129
+ cmd, # str or list
130
+ stdout=True, # attach stdout
131
+ stderr=True, # attach stderr
132
+ stdin=False, # attach stdin
133
+ tty=False, # allocate pseudo-TTY
134
+ privileged=False, # extended privileges
135
+ user="", # user identity
136
+ detach=False, # run in background
137
+ stream=False, # return generator
138
+ socket=False, # return raw socket
139
+ environment=None, # dict of env vars
140
+ workdir=None, # working directory
141
+ demux=False, # separate stdout/stderr
142
+ )
143
+ # Returns: ExecResult(exit_code=int, output=bytes|generator|tuple)
144
+ ```
145
+
146
+ ---
147
+
148
+ ## 3. Log Streaming
149
+
150
+ ### Real-Time Tailing
151
+
152
+ ```python
153
+ import threading
154
+
155
+ def tail_logs(container):
156
+ for line in container.logs(stream=True, follow=True):
157
+ print(f"[{container.name}] {line.decode().strip()}")
158
+
159
+ thread = threading.Thread(target=tail_logs, args=(container,), daemon=True)
160
+ thread.start()
161
+ ```
162
+
163
+ ### Time-Bounded Logs
164
+
165
+ ```python
166
+ from datetime import datetime, timedelta
167
+
168
+ # Last hour
169
+ recent = container.logs(since=datetime.utcnow() - timedelta(hours=1))
170
+
171
+ # Between two timestamps
172
+ logs = container.logs(
173
+ since=datetime(2024, 1, 1),
174
+ until=datetime(2024, 1, 2),
175
+ )
176
+ ```
177
+
178
+ ### Separated Streams
179
+
180
+ ```python
181
+ stdout_only = container.logs(stdout=True, stderr=False)
182
+ stderr_only = container.logs(stdout=False, stderr=True)
183
+ ```
184
+
185
+ ### Full logs() Signature
186
+
187
+ ```python
188
+ container.logs(
189
+ stdout=True, # include stdout
190
+ stderr=True, # include stderr
191
+ stream=False, # return generator (blocking)
192
+ timestamps=False, # prefix lines with timestamps
193
+ tail="all", # "all" or int (number of lines)
194
+ since=None, # datetime, epoch int, or float
195
+ follow=False, # keep connection open for new output
196
+ until=None, # datetime, epoch int, or float
197
+ )
198
+ ```
199
+
200
+ ---
201
+
202
+ ## 4. Health Check Configuration
203
+
204
+ ### Setting Health Checks at Creation
205
+
206
+ ```python
207
+ container = client.containers.run(
208
+ "myapp:latest",
209
+ detach=True,
210
+ healthcheck={
211
+ "test": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"],
212
+ "interval": 10_000_000_000, # 10 seconds (nanoseconds)
213
+ "timeout": 5_000_000_000, # 5 seconds
214
+ "retries": 3,
215
+ "start_period": 30_000_000_000, # 30 seconds grace
216
+ },
217
+ )
218
+ ```
219
+
220
+ All time values are in **nanoseconds**. Helpers:
221
+
222
+ ```python
223
+ SECOND = 1_000_000_000
224
+ MINUTE = 60 * SECOND
225
+
226
+ healthcheck = {
227
+ "test": ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"],
228
+ "interval": 15 * SECOND,
229
+ "timeout": 5 * SECOND,
230
+ "retries": 3,
231
+ "start_period": 1 * MINUTE,
232
+ }
233
+ ```
234
+
235
+ Test formats:
236
+ - `["CMD", "executable", "arg1"]` -- run command directly.
237
+ - `["CMD-SHELL", "command string"]` -- run via `/bin/sh -c`.
238
+ - `["NONE"]` -- disable health checking.
239
+
240
+ ### Disabling Health Checks
241
+
242
+ ```python
243
+ container = client.containers.run("myapp", detach=True, healthcheck={"test": ["NONE"]})
244
+ ```
245
+
246
+ ---
247
+
248
+ ## 5. Health Status Polling
249
+
250
+ ### Reading Health Status
251
+
252
+ ```python
253
+ container.reload() # refresh cached state from daemon
254
+ health = container.attrs["State"]["Health"]
255
+
256
+ print(health["Status"]) # "healthy", "unhealthy", "starting"
257
+ print(health["FailingStreak"])
258
+ for log_entry in health["Log"]:
259
+ print(f" Exit: {log_entry['ExitCode']}, Output: {log_entry['Output'][:100]}")
260
+ ```
261
+
262
+ ### Polling Until Healthy
263
+
264
+ ```python
265
+ import time
266
+
267
+ def wait_for_healthy(container, timeout=60, interval=2):
268
+ """Poll container health status until healthy or timeout."""
269
+ deadline = time.monotonic() + timeout
270
+ while time.monotonic() < deadline:
271
+ container.reload()
272
+ health = container.attrs.get("State", {}).get("Health", {})
273
+ status = health.get("Status", "none")
274
+ if status == "healthy":
275
+ return True
276
+ if status == "unhealthy":
277
+ logs = health.get("Log", [])
278
+ last = logs[-1] if logs else {}
279
+ raise RuntimeError(
280
+ f"Container unhealthy: exit={last.get('ExitCode')}, "
281
+ f"output={last.get('Output', '')[:200]}"
282
+ )
283
+ time.sleep(interval)
284
+ raise TimeoutError(f"Container did not become healthy within {timeout}s")
285
+ ```
286
+
287
+ ### Async Health Polling
288
+
289
+ ```python
290
+ import asyncio
291
+
292
+ async def wait_for_healthy_async(container, timeout=60, interval=2):
293
+ deadline = asyncio.get_event_loop().time() + timeout
294
+ while asyncio.get_event_loop().time() < deadline:
295
+ container.reload()
296
+ status = container.attrs.get("State", {}).get("Health", {}).get("Status", "none")
297
+ if status == "healthy":
298
+ return True
299
+ if status == "unhealthy":
300
+ raise RuntimeError("Container unhealthy")
301
+ await asyncio.sleep(interval)
302
+ raise TimeoutError(f"Health check timeout after {timeout}s")
303
+ ```
304
+
305
+ ---
306
+
307
+ ## 6. Wait and Timeout
308
+
309
+ ### Blocking Wait
310
+
311
+ ```python
312
+ result = container.wait(timeout=120, condition="not-running")
313
+ # Returns: {"StatusCode": 0, "Error": None}
314
+
315
+ if result["StatusCode"] != 0:
316
+ logs = container.logs(tail=50)
317
+ raise RuntimeError(f"Container failed (exit {result['StatusCode']}): {logs.decode()}")
318
+ ```
319
+
320
+ `condition` options: `"not-running"` (default), `"next-exit"`, `"removed"`.
321
+
322
+ ### Timeout Handling
323
+
324
+ ```python
325
+ from requests.exceptions import ReadTimeout
326
+
327
+ try:
328
+ result = container.wait(timeout=30)
329
+ except ReadTimeout:
330
+ container.stop(timeout=5)
331
+ container.remove(force=True)
332
+ raise TimeoutError("Container did not exit within 30s")
333
+ ```
334
+
335
+ ---
336
+
337
+ ## 7. Auto-Remove Patterns
338
+
339
+ ### Ephemeral Containers
340
+
341
+ ```python
342
+ # Auto-remove on exit (daemon-side)
343
+ output = client.containers.run(
344
+ "python:3.12-slim",
345
+ command="python -c 'print(42)'",
346
+ remove=True,
347
+ )
348
+ print(output) # b"42\n"
349
+ ```
350
+
351
+ With `remove=True` and `detach=False` (default), the SDK blocks until the container exits, returns the output, and the daemon removes the container automatically.
352
+
353
+ ### Detached with Auto-Remove
354
+
355
+ ```python
356
+ container = client.containers.run(
357
+ "worker:latest",
358
+ detach=True,
359
+ remove=True,
360
+ )
361
+ # Container auto-removes when it exits; container.wait() still works
362
+ result = container.wait()
363
+ ```
364
+
365
+ Caveat: after auto-removal, `container.logs()` and `container.reload()` raise `NotFound`. Capture logs before waiting if needed.
366
+
367
+ ---
368
+
369
+ ## 8. Prune
370
+
371
+ Remove stopped containers, unused images, volumes, and networks:
372
+
373
+ ```python
374
+ # Stopped containers
375
+ result = client.containers.prune(filters={"until": "24h"})
376
+ print(result["SpaceReclaimed"])
377
+
378
+ # Dangling images
379
+ client.images.prune(filters={"dangling": True})
380
+
381
+ # Unused volumes
382
+ client.volumes.prune()
383
+
384
+ # Unused networks
385
+ client.networks.prune()
386
+ ```
387
+
388
+ Prune returns a dict with `SpaceReclaimed` (bytes) and the list of deleted resource IDs.