Skip to content

feat(cli): add claude-code init templates (sync / async / temporal)#435

Merged
declan-scale merged 5 commits into
nextfrom
declan-scale/init-templates-claude-code
Jun 24, 2026
Merged

feat(cli): add claude-code init templates (sync / async / temporal)#435
declan-scale merged 5 commits into
nextfrom
declan-scale/init-templates-claude-code

Conversation

@declan-scale

@declan-scale declan-scale commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Ninth slice of #425. Adds the claude-code agentex init templates across all three tiers.

  • New default-claude-code, sync-claude-code, temporal-claude-code template dirs.
  • Wires DEFAULT_CLAUDE_CODE, SYNC_CLAUDE_CODE, TEMPORAL_CLAUDE_CODE into init.py (enum, project-files map, prompts).
  • Scaffolded code imports ClaudeCodeTurn from the agentex.lib.adk facade.

Test plan

  • pytest tests/lib/cli/ -k "init or template" — 23 passed (all render to valid Python)

Notes

Stacked on #434. Retarget to next after the chain merges.

🤖 Generated with Claude Code

Greptile Summary

  • Adds Claude Code agentex init templates for default, sync, and Temporal project tiers.
  • Wires the new Claude Code template choices into CLI init prompts and project-file generation.
  • Expands ADK/harness unification and test coverage across OpenAI, Claude Code, Codex, LangGraph, and Pydantic AI integrations.

Confidence Score: 4/5

Merge should wait for fixes to the OpenAI sync stream event lifecycle and the generated Claude Code Dockerfile.

The changed areas are covered by focused checks, and the remaining concerns are localized to stream event ordering and template rendering.

src/agentex/lib/adk/_modules/_openai_sync.py and src/agentex/lib/cli/templates/default-claude-code/Dockerfile.j2

T-Rex T-Rex Logs

What T-Rex did

  • Ran a focused pytest harness against the real sync OpenAI converter with mocked OpenAI SDK events for a reasoning delta followed by an output item done, and observed that the reasoning start event at index 1 did not have a corresponding done event.
  • Ran a focused pytest harness against the real _openai_sync.py converter with a mocked OpenAI stream containing a reasoning delta followed by a normal answer text delta, and confirmed that the reasoning and text start indices reused index 1.
  • Rendered the default-claude-code Dockerfile.j2 template into a temporary project and validated that the next Dockerfile command would fail parsing due to a broken line continuation in the template.
  • Checked the Claude Code scaffold process and confirmed that after scaffolding, template directories and Jinja files exist, init wiring passes, and create_project_structure runs for all three tiers with the generated Claude Code sources importing the facade.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (3)

  1. General comment

    P1 Claude Code scaffolds omit expected root files

    • Bug
      • The new Claude Code templates are selectable and create projects, but the generated structures do not match the requested contract. default-claude-code and sync-claude-code generate project/acp.py plus root files such as manifest.yaml, README.md, pyproject.toml, Dockerfile, etc., but no agentex.json. temporal-claude-code generates project/acp.py, project/activities.py, project/workflow.py, and project/run_worker.py, but the expected contract requires run_worker.py at the project root and also expects agentex.json.
    • Cause
      • create_project_structure writes every file listed in project_files into code_dir (project/), so temporal run_worker.py is placed under project/run_worker.py. The root template map also renders manifest.yaml but does not render an agentex.json root file for these templates.
    • Fix
      • Update the Claude Code scaffold mapping/rendering so temporal-claude-code emits run_worker.py at the scaffold root when that is the intended contract, and add or map the expected agentex.json root template for the new Claude Code templates. If manifest.yaml is the intended replacement for agentex.json, update the contract/tests accordingly.

    T-Rex Ran code and verified through T-Rex

  2. src/agentex/lib/adk/_modules/_openai_sync.py, line 178-184 (link)

    P1 Reasoning never closes

    Reasoning items are opened with StreamTaskMessageStart, but this completion branch skips the matching StreamTaskMessageDone. A reasoning stream that emits deltas and then completes leaves sync ACP clients with an unterminated reasoning message, and tracing has to mark the reasoning span incomplete during final cleanup instead of closing it normally.

    Artifacts

    Repro: focused pytest harness for mocked sync OpenAI reasoning stream

    • Contains supporting evidence from the run (text/x-python; charset=utf-8).

    Repro: verbose pytest output showing missing reasoning done event

    • Keeps the command output available without making the summary code-heavy.

    View artifacts

    T-Rex Ran code and verified through T-Rex

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/agentex/lib/adk/_modules/_openai_sync.py
    Line: 178-184
    
    Comment:
    **Reasoning never closes**
    
    Reasoning items are opened with `StreamTaskMessageStart`, but this completion branch skips the matching `StreamTaskMessageDone`. A reasoning stream that emits deltas and then completes leaves sync ACP clients with an unterminated reasoning message, and tracing has to mark the reasoning span incomplete during final cleanup instead of closing it normally.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

  3. src/agentex/lib/adk/_modules/_openai_sync.py, line 295-306 (link)

    P1 Text reuses reasoning index

    After a reasoning item, the next text item can reuse the reasoning message index. The reasoning handlers increment message_index, but this branch only increments text indices after tool output, so a normal reasoning-then-answer stream can emit the final text Start with the same index as the reasoning message. Downstream consumers key open contexts by index, so the answer can overwrite or alias the reasoning context and mix text output into the reasoning message.

    Artifacts

    Repro: focused pytest harness for reasoning then text index reuse

    • Contains supporting evidence from the run (text/x-python; charset=utf-8).

    Repro: verbose pytest output showing duplicate reasoning and text start indices

    • Keeps the command output available without making the summary code-heavy.

    View artifacts

    T-Rex Ran code and verified through T-Rex

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/agentex/lib/adk/_modules/_openai_sync.py
    Line: 295-306
    
    Comment:
    **Text reuses reasoning index**
    
    After a reasoning item, the next text item can reuse the reasoning message index. The reasoning handlers increment `message_index`, but this branch only increments text indices after tool output, so a normal reasoning-then-answer stream can emit the final text `Start` with the same index as the reasoning message. Downstream consumers key open contexts by `index`, so the answer can overwrite or alias the reasoning context and mix text output into the reasoning message.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
src/agentex/lib/adk/_modules/_openai_sync.py:178-184
**Reasoning never closes**

Reasoning items are opened with `StreamTaskMessageStart`, but this completion branch skips the matching `StreamTaskMessageDone`. A reasoning stream that emits deltas and then completes leaves sync ACP clients with an unterminated reasoning message, and tracing has to mark the reasoning span incomplete during final cleanup instead of closing it normally.

### Issue 2 of 2
src/agentex/lib/adk/_modules/_openai_sync.py:295-306
**Text reuses reasoning index**

After a reasoning item, the next text item can reuse the reasoning message index. The reasoning handlers increment `message_index`, but this branch only increments text indices after tool output, so a normal reasoning-then-answer stream can emit the final text `Start` with the same index as the reasoning message. Downstream consumers key open contexts by `index`, so the answer can overwrite or alias the reasoning context and mix text output into the reasoning message.

Reviews (16): Last reviewed commit: "fix(cli): drain claude stdin and stop en..." | Re-trigger Greptile

@declan-scale declan-scale force-pushed the declan-scale/init-template-openai-agents branch from cd6bb70 to ab23d6d Compare June 23, 2026 15:28
@declan-scale declan-scale force-pushed the declan-scale/init-templates-claude-code branch from a92d37b to ff359de Compare June 23, 2026 15:28
@declan-scale

Copy link
Copy Markdown
Contributor Author

@greptile review

Comment thread src/agentex/lib/cli/templates/sync-claude-code/dev.ipynb.j2
Comment thread src/agentex/lib/cli/templates/default-claude-code/Dockerfile.j2 Outdated
Comment thread src/agentex/lib/cli/templates/sync-claude-code/Dockerfile.j2 Outdated
Comment thread src/agentex/lib/cli/templates/temporal-claude-code/project/workflow.py.j2 Outdated
Comment thread src/agentex/lib/cli/templates/temporal-claude-code/Dockerfile-uv.j2
@declan-scale declan-scale force-pushed the declan-scale/init-template-openai-agents branch from ab23d6d to d3f1fb6 Compare June 23, 2026 15:44
@declan-scale declan-scale force-pushed the declan-scale/init-templates-claude-code branch from ff359de to e031d5d Compare June 23, 2026 15:45
@declan-scale

Copy link
Copy Markdown
Contributor Author

@greptile review

Comment thread src/agentex/lib/cli/templates/default-claude-code/Dockerfile.j2
Comment thread src/agentex/lib/cli/templates/default-claude-code/manifest.yaml.j2 Outdated
Comment on lines +30 to +34
COPY {{ project_path_from_build_root }}/pyproject.toml {{ project_path_from_build_root }}/uv.lock ./

# Install dependencies (without project itself, for layer caching)
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --no-install-project --no-dev

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing uv lockfile

agentex init renders pyproject.toml for use_uv=True, but it does not create a uv.lock. A new Claude template project built immediately after init therefore fails during Docker build at this COPY ... uv.lock step before uv sync can run. Please either generate the lockfile during init, remove uv.lock from the COPY, or make the Dockerfile support a fresh project without a lockfile.

Artifacts

Repro: script that renders the uv project and checks the Docker COPY sources

  • Contains supporting evidence from the run (text/x-python; charset=utf-8).

Repro: command transcript showing no uv.lock and emulated Docker COPY failure

  • Keeps the command output available without making the summary code-heavy.

Repro: template availability check used during setup

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/cli/templates/default-claude-code/Dockerfile-uv.j2
Line: 30-34

Comment:
**Missing uv lockfile**

`agentex init` renders `pyproject.toml` for `use_uv=True`, but it does not create a `uv.lock`. A new Claude template project built immediately after init therefore fails during Docker build at this `COPY ... uv.lock` step before `uv sync` can run. Please either generate the lockfile during init, remove `uv.lock` from the `COPY`, or make the Dockerfile support a fresh project without a lockfile.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

Comment thread src/agentex/lib/cli/templates/default-claude-code/project/acp.py.j2
Comment thread src/agentex/lib/cli/templates/temporal-claude-code/project/workflow.py.j2 Outdated
Comment thread src/agentex/lib/cli/templates/temporal-claude-code/manifest.yaml.j2
Comment thread src/agentex/lib/cli/templates/sync-claude-code/manifest.yaml.j2 Outdated
@declan-scale declan-scale force-pushed the declan-scale/init-templates-claude-code branch from e031d5d to 8f7854a Compare June 23, 2026 16:25
@declan-scale

Copy link
Copy Markdown
Contributor Author

@greptile review

Comment thread src/agentex/lib/cli/templates/default-claude-code/Dockerfile-uv.j2
Comment thread src/agentex/lib/cli/templates/default-claude-code/.env.example.j2 Outdated
@declan-scale declan-scale force-pushed the declan-scale/init-template-openai-agents branch from 4310435 to 1638b8b Compare June 23, 2026 16:52
@declan-scale declan-scale force-pushed the declan-scale/init-templates-claude-code branch from 8f7854a to 6e3f0cf Compare June 23, 2026 16:52
@declan-scale

Copy link
Copy Markdown
Contributor Author

@greptile review

Comment thread src/agentex/lib/cli/templates/default-claude-code/manifest.yaml.j2 Outdated
Comment thread src/agentex/lib/cli/templates/default-claude-code/project/acp.py.j2
@declan-scale

Copy link
Copy Markdown
Contributor Author

@greptile review

1 similar comment
@declan-scale

Copy link
Copy Markdown
Contributor Author

@greptile review

@declan-scale declan-scale force-pushed the declan-scale/init-template-openai-agents branch from 1638b8b to 3a13b08 Compare June 23, 2026 19:53
@declan-scale declan-scale force-pushed the declan-scale/init-templates-claude-code branch from 47ea82c to 2003757 Compare June 23, 2026 19:53
@declan-scale declan-scale force-pushed the declan-scale/init-template-openai-agents branch from 3a13b08 to e4db8d5 Compare June 23, 2026 19:57
@declan-scale declan-scale force-pushed the declan-scale/init-templates-claude-code branch from 2003757 to 755d18e Compare June 23, 2026 19:57
Comment on lines +133 to +134
@acp.on_task_event_send
async def handle_task_event_send(params: SendEventParams):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Non-text events crash

SendEventParams.event.content is a TaskMessageContent union, not always text. Valid ACP payloads such as data or tool messages do not have a .content field, so sending one of those events to a generated Claude template raises AttributeError before the user message is recorded or any task error is streamed. Convert or reject non-text content before reading .content; the same assumption is present in the sync and temporal Claude handlers.

Artifacts

Repro: focused generated-template handler invocation script

  • Contains supporting evidence from the run (text/x-python; charset=utf-8).

Stack trace captured during the T-Rex run

  • Keeps the raw stack trace available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/cli/templates/default-claude-code/project/acp.py.j2
Line: 133-134

Comment:
**Non-text events crash**

`SendEventParams.event.content` is a `TaskMessageContent` union, not always text. Valid ACP payloads such as data or tool messages do not have a `.content` field, so sending one of those events to a generated Claude template raises `AttributeError` before the user message is recorded or any task error is streamed. Convert or reject non-text content before reading `.content`; the same assumption is present in the sync and temporal Claude handlers.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

@declan-scale declan-scale force-pushed the declan-scale/init-template-openai-agents branch from e4db8d5 to 9fc5e23 Compare June 23, 2026 22:04
@declan-scale declan-scale force-pushed the declan-scale/init-templates-claude-code branch from 755d18e to 63904c2 Compare June 23, 2026 22:04
Comment thread src/agentex/lib/cli/templates/temporal-claude-code/Dockerfile.j2
@declan-scale declan-scale force-pushed the declan-scale/init-templates-claude-code branch from 63904c2 to f819115 Compare June 23, 2026 22:29
@declan-scale declan-scale force-pushed the declan-scale/init-template-openai-agents branch from 9fc5e23 to f55806c Compare June 23, 2026 22:29

# Description of what your agent does
# Helps with documentation and discovery
description: {{ description }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Manifest can break

description is user-supplied text from the init prompt, but this template renders it as an unquoted YAML scalar. A description containing YAML-significant text such as Answers questions: with Claude or Claude # helper can make the generated manifest.yaml invalid or truncate the value, so the initialized agent may not load. The same unquoted-description issue exists in sync-claude-code/manifest.yaml.j2; quote the rendered description in both templates.

Artifacts

Repro: focused manifest render and YAML parse script

  • Contains supporting evidence from the run (text/x-python; charset=utf-8).

Repro: failing render and YAML parse output

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/cli/templates/default-claude-code/manifest.yaml.j2
Line: 68

Comment:
**Manifest can break**

`description` is user-supplied text from the init prompt, but this template renders it as an unquoted YAML scalar. A description containing YAML-significant text such as `Answers questions: with Claude` or `Claude # helper` can make the generated `manifest.yaml` invalid or truncate the value, so the initialized agent may not load. The same unquoted-description issue exists in `sync-claude-code/manifest.yaml.j2`; quote the rendered description in both templates.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

@declan-scale declan-scale force-pushed the declan-scale/init-template-openai-agents branch from f55806c to 9861b8b Compare June 24, 2026 00:38
@declan-scale declan-scale deleted the branch next June 24, 2026 00:46
Base automatically changed from declan-scale/init-template-openai-agents to next June 24, 2026 00:46
declan-scale and others added 5 commits June 23, 2026 20:46
Add default-claude-code, sync-claude-code and temporal-claude-code templates
across all three tiers, wiring the new TemplateType entries into the init flow.
Scaffolded code imports ClaudeCodeTurn from the agentex.lib.adk facade.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Install the Claude Code CLI (@anthropic-ai/claude-code) in all three
  Dockerfiles so deployed images can actually run `claude`.
- Wire ANTHROPIC_API_KEY (credential + env) in the default, sync and temporal
  manifests; the `claude` subprocess does not read LITELLM_API_KEY.
- Surface CLI failures: capture a bounded stderr tail and raise on a non-zero
  exit instead of silently completing the turn.
- Serialize temporal turns with an asyncio.Lock so overlapping signals don't
  race on _session_id, and raise the activity timeout to 30m for agentic runs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Match the manifest fix: the claude-code templates spawn the `claude` CLI,
which reads ANTHROPIC_API_KEY rather than LITELLM_API_KEY. Update the
default, sync and temporal .env.example scaffolds accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Round-3 Greptile parity: the uv-path Dockerfile-uv.j2 variants (default, sync,
temporal) installed node/npm but not the `claude` CLI, leaving use_uv=True
containers non-functional. Mirror the npm install -g @anthropic-ai/claude-code
step already added to the pip Dockerfiles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Round-4 Greptile review:
- await proc.stdin.drain() before close() in all three claude-code subprocess
  helpers (matches the codex helpers; flushes large prompts before EOF).
- Stop the manifest env block from setting ANTHROPIC_API_KEY to an empty string,
  which would shadow the credential mapping / .env value at runtime. The key now
  comes solely from the credential mapping (deploy) or .env (local).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@declan-scale declan-scale reopened this Jun 24, 2026
@declan-scale declan-scale force-pushed the declan-scale/init-templates-claude-code branch from f819115 to 4f2002c Compare June 24, 2026 01:57
@declan-scale declan-scale merged commit fd9bc4a into next Jun 24, 2026
50 checks passed
@declan-scale declan-scale deleted the declan-scale/init-templates-claude-code branch June 24, 2026 02:08
@stainless-app stainless-app Bot mentioned this pull request Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants