Skip to content

refactor(monorepo): added typescript templates into extension-template#3

Merged
poshinchen merged 1 commit into
strands-agents:mainfrom
poshinchen:refactor/monorepo-python-typescript
May 22, 2026
Merged

refactor(monorepo): added typescript templates into extension-template#3
poshinchen merged 1 commit into
strands-agents:mainfrom
poshinchen:refactor/monorepo-python-typescript

Conversation

@poshinchen
Copy link
Copy Markdown
Contributor

@poshinchen poshinchen commented May 19, 2026

Summary

Restructures extension-template-python into a language-agnostic monorepo extension-template/ that hosts both Python and TypeScript templates side-by-side.

Existing Python files move under python/ unchanged, a parallel typescript/ template is added with the same five extension-point skeletons, an interactive setup script, and CI/publish workflows.

What's in the diff

Layout

  • All existing files (pyproject.toml, setup_template.py, src/strands_template/, tests/, README.md) relocate to python/ via git mv (no content changes other than minor README/setup-script tweaks for the new path).
  • New typescript/ sibling holds the equivalent template.
  • New top-level README.md introduces both subprojects.

TypeScript template (typescript/)

  • Five component skeletons using real @strands-agents/sdk types:
    • src/tool.tstool() factory + Zod schema
    • src/model.tsModel subclass with stream(), updateConfig, getConfig
    • src/plugin.tsPlugin impl with hook + self-contained tool
    • src/session-manager.tsSnapshotStorage impl (the TS SDK's customization point for session persistence; SessionManager is consumed as-is)
    • src/conversation-manager.tsConversationManager subclass with reduce()
  • setup-template.ts — Node port of setup_template.py, runs via npm run setup (uses tsx). Same UX: prompts for package name, components, author info; renames/rewrites/deletes accordingly; prunes setup-only entries from package.json; self-destructs.
  • Tooling matches sdk-typescript: npm + plain tsc build, vitest, ESLint flat config, Prettier, strict tsconfig (NodeNext, ES2022, exactOptionalPropertyTypes, etc.). Aggregate script is npm run check to avoid the prepare lifecycle-hook collision.
  • One vitest test per component.

CI/publish

  • ci.yml / publish.yml renamed to ci-python.yml / publish-python.yml, scoped via paths: filters and working-directory: python. Hatch-vcs is configured to strip a python-v tag prefix.
  • New ci-typescript.yml (Node 20 & 22 matrix) and publish-typescript.yml (publishes on typescript-v* tags via NPM_TOKEN).
  • Language-prefixed tags let the two halves of the monorepo release independently.

Test plan

  • Existing Python tests untouched; Python CI now keys off python/** paths.
  • cd typescript && npm install && npm run check passes locally (format, lint, type-check, test — 5 files / 7 tests).
  • npm run setup end-to-end exercised: scaffolds a new package, deletes unselected components, removes setup-only deps and the script itself.
  • Verify TS workflow runs on a draft tag (typescript-v0.0.0-test) once NPM_TOKEN is configured.

Notes for reviewers

  • Repo rename: this PR assumes the GitHub repo is/will be renamed from extension-template-pythonextension-template to match the on-disk directory rename. Workflow paths and the new root README assume that name; if you'd rather keep the repo name, only README copy needs adjusting.
  • extension-template/python/ README still describes the same UX users had before, with a one-line note pointing at the monorepo root.
  • TS template peer-depends on @strands-agents/sdk >=0.1.0 and zod ^4.1.12. Bump the lower bound if the SDK ships a different first stable version.

@poshinchen poshinchen force-pushed the refactor/monorepo-python-typescript branch 2 times, most recently from a443a35 to 9d605d8 Compare May 19, 2026 18:35
@poshinchen poshinchen requested review from notowen333 and zastrowm May 19, 2026 18:36
@zastrowm zastrowm requested review from mkmeral and removed request for zastrowm May 19, 2026 20:00
@mkmeral mkmeral self-assigned this May 20, 2026
Copy link
Copy Markdown

@agent-of-mkmeral agent-of-mkmeral left a comment

Choose a reason for hiding this comment

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

Tested end-to-end (cloned, ran npm run check — 7/7 pass; ran setup-template.ts with all 5 components via pexpect, post-setup npm run check — 7/7 pass). Three concrete fixes worth landing inline:

🟡 1. Stale npm run prepare in setup output + README

The package.json exposes npm run check (per the PR description, intentionally to dodge the npm prepare lifecycle hook). But setup-template.ts:352 and README.md both still tell users to run npm run prepare. Reproduced after setup:

$ npm run prepare
npm error Missing script: "prepare"

Fix:

// setup-template.ts
console.log('  3. Run checks: npm run check')

And same edit in typescript/README.md Next steps.

🟡 2. Setup leaves the other language behind

After running typescript/setup-template.ts for a TS-only project, the user's repo still has python/, python/pyproject.toml (with monorepo tag_regex/root=".."), .github/workflows/{ci,publish}-python.yml, and a top-level README.md that markets both languages. The Python setup script has the symmetric problem (leaves typescript/ + TS workflows).

The top-level README hand-waves this ("you can keep both subprojects... or delete the directory you don't need"), but for the 95% single-language case this is real manual cleanup most users will skip — their repo ends up shipping cruft.

Suggested: add a final prompt in each setup script:

Drop the other language from this repo? (y/n) [y]:

If yes: delete sibling dir + sibling workflows, rewrite top-level README.md to a single-language template, strip tag_regex/root from pyproject.toml (Python case).

🟢 3. @strands-agents/sdk >=0.1.0 peerDep — version doesn't exist on npm yet

The PR description already flags this. Just worth a one-line note in typescript/README.md so early adopters know npm install will currently fail until SDK ships, and to bump once it does.

Other observations (non-blocking)

  • replacements ordering comment in setup-template.ts is critical and well-noted — verified it works (file rename session-manager.tsgoogle-session-manager.ts happens correctly because session-manager runs before TemplateXxx).
  • Hatch-vcs root=".." + tag_regex="^python-v(?P<version>.+)$" + relative_to="pyproject.toml" is the right incantation for a monorepo — relative_to is required when root is non-default, comment captures that.
  • TS uses real SDK types (Model, Plugin, SnapshotStorage, ConversationManager) not stubs — nice. The SnapshotStorage vs SessionManager split is documented well in session-manager.ts.
  • CI scoping via paths: + working-directory: is correct — verified.
  • 5499-line package-lock.json shipped intentionally pins reproducible setup; fine but unusual for a template.

Net: direction is right (single canonical template, dual-language symmetry, clean tag-prefix release scheme). Worth landing the 3 fixes above before merging or as an immediate follow-up so the post-Use this template UX is actually clean.

@mkmeral
Copy link
Copy Markdown
Contributor

mkmeral commented May 20, 2026

Setup leaves the other language behind

I think this is the main blocker

@strands-agents/sdk >=0.1.0 peerDep — version doesn't exist on npm yet

should we scope to >=1.0 and <2.0?

@poshinchen
Copy link
Copy Markdown
Contributor Author

There are some options:

  1. Delete sibling, leave nested
    • Prompt at end: delete sibling language dir + sibling workflows + rewrite top-level README. Keep chosen language nested.
  2. Delete and hoist to root
    • Prompt at end: delete sibling, move chosen language to root, rewrite workflows + pyproject.toml + README links.

@mkmeral
Copy link
Copy Markdown
Contributor

mkmeral commented May 20, 2026

I think the latter is the best. After the script is run, I want the repo to look "beautiful" :D

so it should look like a proper TS or Python repo (whatever customer chooses)

@poshinchen poshinchen force-pushed the refactor/monorepo-python-typescript branch from 9d605d8 to 4d6dd77 Compare May 20, 2026 16:41
Comment thread typescript/setup-template.ts Outdated
Comment thread typescript/setup-template.ts
Comment thread typescript/setup-template.ts
Comment thread .github/workflows/publish-typescript.yml
@poshinchen poshinchen force-pushed the refactor/monorepo-python-typescript branch 2 times, most recently from 6b69c26 to e54a3be Compare May 20, 2026 18:15
Copy link
Copy Markdown

@agent-of-mkmeral agent-of-mkmeral left a comment

Choose a reason for hiding this comment

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

Re-review on e54a3be — hoist-to-root works, two leftover bugs in post-hoist README

TL;DR: The new hoistToRoot() / hoistWorkflows() / stripMonorepoFromReadme() flow (and Python equivalent) addresses the main blocker from the previous round — verified end-to-end on both languages. Two small bugs remain: post-hoist README still tells users to cd typescript (or cd python) and run npm run setup / python setup_template.py, which no longer exist after hoist. SDK peerDep range now scoped to >=1.0.0 <2.0.0 ✓.

📝 Full re-review (click to expand)

What I tested

Cloned poshinchen:refactor/monorepo-python-typescript@e54a3be, ran both setup scripts end-to-end via pexpect with all 5 components selected and dropSibling=y:

  • TypeScript: npm cinpm run check (7/7 pass) → npx tsx setup-template.ts (full hoist) → npm run check post-hoist (7/7 pass ✓)
  • Python: python3 setup_template.py (full hoist) — pyproject.toml de-monorepoized, workflows hoisted, top-level files cleaned up.

✅ What got fixed since previous review

Previous finding Status
Stale npm run prepare in setup output / README ✅ Fixed — now says npm run check
Setup leaves the other language behind ✅ Fixed — dropSibling prompt + full hoist-to-root in both scripts
@strands-agents/sdk >=0.1.0 peerDep ✅ Scoped to >=1.0.0 <2.0.0 (per @mkmeral's suggestion)

The hoist functions are well-factored — stripMonorepoFromReadme() rewrites the per-language README's monorepo-aware bits (LICENSE link ../LICENSELICENSE, typescript-v0.1.0v0.1.0, removes "This is the TypeScript half of the…" line), hoistWorkflows() strips paths: filters and working-directory: defaults from the workflows before renaming, and hoistToRoot() does the directory swap + chdir. After running, the resulting repo looks like a real single-language template — no monorepo, typescript-v, python-v, tag_regex, or root = ".." references anywhere. 👏

🟡 New findings — 2 small bugs

Bug 1: Post-hoist README still says `cd your-repo-name/{typescript,python}`

After hoist, README.md (now at repo root) still has:

TypeScript (typescript/README.md → root after hoist), L28:

git clone https://github.com/testuser/your-repo-name
cd your-repo-name/typescript     # ← directory no longer exists

Python (python/README.md → root after hoist), L28:

cd your-repo-name/python         # ← directory no longer exists

Users will follow this and immediately hit cd: no such file or directory.

Suggested fix: add to stripMonorepoFromReadme() (TS side, mirror in Python):

next = next.replace(/cd your-repo-name\/typescript\s*\n/g, '\n')
// Python equivalent
next = next.replace(/cd your-repo-name\/python\s*\n/g, '\n')

Or more robustly, swap the entire cd your-repo-name/{lang} line for cd your-repo-name.

Bug 2: Post-hoist README "Run the setup script" section is now stale (script self-deleted)

The setup script self-destructs (correct), but the README still walks users through running it:

TypeScript README L30-37 (post-hoist):

### 2. Run the setup script
…
```bash
npm install
npm run setup           # ← script and `setup` script entry both gone

**Python README L31-36 (post-hoist):**

2. Run the setup script

python setup_template.py    # ← file deleted

Users see this section after the script has already removed itself. Confusing on first read.

**Suggested fix:** in `stripMonorepoFromReadme()`, also strip the entire `### N. Run the setup script` section between `### 2.` and the next `###` heading. Renumber subsequent sections, or just delete steps 2 and renumber 3 → 2. Same on the Python side.

Alternative: have the setup script print a one-liner to README's top saying "This repo was generated from `extension-template`. To re-customize, see [original template]…" and remove the "Getting started" → "Run the setup script" sections altogether.
</details>

### 🟢 Minor — observations on the open threads

Quoting the 4 unresolved review threads from @notowen333 since they're worth thinking through:

- **L310 "do we need to maintain this by hand?"** (the `replacements` map): Yes, this is the source of truth for "what gets renamed during setup." Hard to avoid declaratively listing class/file names since they're spread across multiple files. Could be derived from a `COMPONENTS` schema that knows its own class/file names, but it's only ~20 lines and tightly coupled to the template content — IMO fine as-is.
- **L1 "clean this file up, entrypoint at top"**: Reasonable. Current order is helpers → `main()` at L271 → `main()` invocation at L467. Moving `main()` to the top (with helper functions below) would improve scanability for first-time readers. Not blocking.
- **L33 "shouldn't this just clone the template?"**: I think the implicit question is *why is this not a `degit`/`create-strands-extension` style scaffolder?* The current approach (template repo + post-clone setup script) is closer to `create-react-app` ejection — works with GitHub's "Use this template" button, no external tooling. Would be worth a follow-up if/when there's a `create-strands-*` CLI, but this is a sensible v1.
- **publish-typescript.yml "Did we test this workflow?"**: PR description's test plan checkbox `[ ] Verify TS workflow runs on a draft tag (typescript-v0.0.0-test) once NPM_TOKEN is configured` is still unchecked — would address this directly.

### Net

✅ **Direction is right and ship-ready** modulo the 2 small README bugs above (15-line fix in `stripMonorepoFromReadme` on each side). The hoist-to-root flow gets you to mkmeral's stated goal — "After the script is run, I want the repo to look beautiful" — which it now genuinely does. Verified the resulting repo has zero monorepo cruft, tests still pass, workflows still resolve, and `pyproject.toml`/`package.json` look like normal single-language packages.

**Suggested order:** land the 2 README fixes inline, then merge.

</details>

---
Repro artifacts at `/tmp/work/setup-test-v2` (TS post-hoist) and `/tmp/work/py-test-v2` (Python post-hoist).

@poshinchen poshinchen force-pushed the refactor/monorepo-python-typescript branch from e54a3be to 7257c03 Compare May 20, 2026 19:09
@poshinchen poshinchen force-pushed the refactor/monorepo-python-typescript branch from 7257c03 to da06ee1 Compare May 21, 2026 14:54
@poshinchen poshinchen merged commit afdae2a into strands-agents:main May 22, 2026
8 checks passed
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.

4 participants