Skip to content

Feat/lightweight setup - #2

Merged
DoniLite merged 7 commits into
mainfrom
feat/lightweight-setup
Jul 13, 2026
Merged

Feat/lightweight setup#2
DoniLite merged 7 commits into
mainfrom
feat/lightweight-setup

Conversation

@DoniLite

@DoniLite DoniLite commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

Introduce a profile-based scaffolding system for @donilite/create-wrap with multiple no-DB and SSR/gateway templates, centralised project scaffolding logic, and improved Swagger/auth typing and tagging behaviour.

New Features:

  • Add interactive project profile selection and lightweight stdin-based prompts to the create-wrap CLI, including follow-up options for full-backend features like Redis cache and realtime.
  • Introduce multiple scaffold profiles (full-backend, lightweight API, API aggregator, fullstack SSR, gateway) implemented as diffs over a shared base template, each with tailored config, entrypoints, example features, README, and tests.
  • Provide a reusable filesystem-based scaffold helper that copies the base template, applies profile-specific overlays/removals, patches package metadata, and seeds env/gitignore files.
  • Add a best-effort WebSocket proxy helper and HTTP reverse-proxy controller for the gateway profile, plus SSR routing/rendering utilities using TanStack Router and React for the fullstack-SSR profile.

Enhancements:

  • Refine Swagger generation to support route-level tag overrides, merge configured tag metadata into generated tags, and clarify tag flattening behaviour.
  • Strengthen OpenAPI security scheme typing by replacing loose records with explicit OpenApiSecurityScheme/Flows types and re-exporting them from the auth middleware API surface.
  • Adjust auth-related tests and JWT cookie auth controller to use the new typed security scheme interface and validate presence of security schemes in specs.
  • Factor shared CLI utilities like logging and string case conversion into a dedicated utils module and streamline the create-wrap entry script around the new scaffold pipeline and profile selection.

Tests:

  • Add profile-specific test suites for lightweight-API, api-aggregator, fullstack-SSR, and gateway templates that exercise Wrap composition, routing behaviour, Swagger generation, and example feature flows.
  • Extend Swagger tests to cover tag description merging, controller vs. route-level tag precedence, and security scheme exposure from auth controllers.
  • Add SSR-focused tests for the fullstack-SSR profile that validate server-side rendering of defined routes.

DoniLite added 7 commits July 8, 2026 16:50
openApiSecurityScheme() was a concrete method defaulting to {} and typed
as Record<string, unknown> — subclasses could silently ship no security
scheme (producing a misleading/empty securitySchemes block in generated
docs) and implementers got no autocomplete/type-checking on the shape
OpenAPI actually expects.

Make it abstract (every auth paradigm has *some* client-facing scheme to
document) and replace the loose bag with OpenApiSecurityScheme/
OpenApiSecuritySchemes, modeled directly on the OpenAPI 3.0.3 Security
Scheme Object's four `type` variants (http, apiKey, oauth2,
openIdConnect). Breaking change, intentional per maintainer — this repo
is pre-1.0 and actively developed.
Follow-up to the abstract openApiSecurityScheme() change: update every
current implementer (JwtCookieAuthController, CombinedAuthController,
swagger/index.ts's DEFAULT_SECURITY_SCHEMES + securitySchemes local, and
the template's hand-rolled LegacyHeaderAuthController test double) to the
new strongly-typed return shape instead of Record<string, unknown>.
Two things bundled here since they're both about generateSpec()'s tag
handling:

1. Bug fix: the top-level `tags` array was built as
   `Array.from(tags).map(tag => ({ name: tag }))`, which silently
   discarded `SwaggerConfig.tags` — callers passing real descriptions for
   their tags never saw them in the generated spec. Now merges by name,
   falling back to a bare `{ name }` for tags with no config entry.

2. New `RouteOptions.tags`: a route can now declare its own tags,
   replacing (not merging with) its controller's tags for that operation.

The second change responds to a maintainer report of a parent/child
controller tag setup collapsing into "one summary" instead of a nested
grouping. Verified against Redocly's own vendor-extension docs:
`x-tagGroups` (visual tag nesting) is ReDoc-only, not part of the OpenAPI
spec, and not rendered by `@hono/swagger-ui` (stock swagger-ui-dist).
OpenAPI tags are a flat namespace — an operation with multiple tags
legitimately appears under every one of them, it does not nest. True
hierarchical grouping isn't achievable without swapping the UI renderer.
`RouteOptions.tags` is the closest spec-compliant lever: it lets a child
controller's routes take on their own tag identity (e.g. "Parent: Child"
by convention) instead of inheriting the parent's tag wholesale. Findings
also written up as a comment at the tag-handling site in swagger/index.ts.
The CLI only ever asked for a project name and copied one fixed template
— every project got the full stack (Postgres, Redis, realtime, auth)
whether it needed it or not. Rework it into a small interactive wizard
(src/prompts.ts: numbered-menu select/confirm/text over Bun's stdin,
hand-rolled rather than adding @clack/prompts as a dependency — this is a
one-shot bunx scaffolding tool, so a dependency-light prompt is a better
trade than a nicer TUI for a wizard with five questions) that asks what
kind of project this is (src/profiles.ts) and scaffolds accordingly
(src/scaffold.ts).

Every profile is generated as a diff against the existing full-backend
template (copy template/, delete what a profile doesn't need per
profiles/<id>/remove.txt, layer profiles/<id>/files/ on top) rather than
duplicated template trees — see the profiles/ commit that follows this
one, and packages/create-wrap/profiles/README.md, for the actual profile
content and mechanics writeup.

full-backend itself (still the default, first choice) keeps two new
yes/no follow-ups — Redis cache, realtime websockets — applied as small
text edits to the copied template rather than their own file set, since
each is a single conditional block.
Two DB-free profiles, both scaffolded via the diff mechanism the CLI
commit set up: lightweight-api (auth-only, a "greeting" feature slice)
and api-aggregator (services fronting an upstream API, an "aggregator"
feature slice with fetch injected for testability). Neither pulls in
Postgres/Redis config, drizzle-kit, or pglite — see the "growing into a
database" section of each profile's README for what to add back if a
project outgrows this shape.

Both example services follow the same @service()/@ValidateDTO()
convention an entity-backed BaseService uses (ServiceFactory singleton
lookup, request-body validation replacing the method argument before the
body runs) even though neither has a repository — both decorators are
fully generic in @donilite/wrap already, confirmed with a throwaway
script before writing this (WrapService + @service() + @ValidateDTO(),
zero framework changes needed).

api-aggregator's tests stub `globalThis.fetch` at module-eval time in
tests/swagger.test.ts, before that file's `new Wrap().register(...)`
call — the earliest point anything constructs the AggregatorService
singleton — so the whole suite never makes a real network call through
ServiceFactory's process-wide cache, regardless of which test file
happens to touch the aggregator routes first.

Verified by scaffolding both into the monorepo workspace (temporarily, so
hono/etc. hoist the same way packages/create-wrap/template's own
verification does) and running install/typecheck/lint/test against the
generated output, not just checking files got copied.
Backend framework stays TanStack Router/React-agnostic on purpose (that's
a frontend-ecosystem choice, not something @donilite/wrap should mandate)
— this profile is scaffolding only: it wires TanStack Router's route tree
(src/ssr/routes.tsx) to a server-side render pass (src/ssr/render.tsx,
react-dom/server's renderToString + a per-request router built with
createMemoryHistory) mounted as an ordinary Hono catch-all route in
src/index.ts. No new Wrap/RouterController primitive was needed — `.get()`
already covers it, confirming the mission brief's expectation that this
profile is mostly about scaffolding the right example code.

What ships is a genuine, tested server round trip (tests/ssr.test.ts hits
both routes and asserts on the rendered HTML, no framework changes
required). What does NOT ship, flagged in render.tsx's header comment and
the profile README rather than silently left out: client-side hydration —
no Vite/esbuild bundle, no hydrateRoot(). Pages are server-rendered HTML
only. Wiring a client bundle is real, separate work (pick a bundler, add
a client entry point, serve built assets) intentionally left as a
follow-up rather than guessed at unsupervised.

JSON API routes moved under /api (see index.controller.ts) so they don't
collide with SSR page paths at "/".
Proxy/gateway profile: an HTTP reverse-proxy example using Hono's
built-in proxy() helper (hono/proxy — the standard approach, checked it
exists in the installed hono version rather than assuming), and a
best-effort WebSocket proxy helper (src/gateway/ws-proxy.ts) relaying
frames both directions over a native WebSocket client connection to the
upstream, using the same hono/bun WebSocket primitive
@donilite/wrap/realtime is built on.

WS proxying is explicitly scoped as a starting point, not claimed as
production-grade — src/gateway/ws-proxy.ts's header comment lists exactly
what it does and doesn't handle (no backpressure propagation, no upstream
reconnection, no built-in auth on the upgrade), and the profile README
repeats the same warning rather than burying it.

Also adds profiles/README.md, the mechanics writeup promised by comments
in src/profiles.ts and src/scaffold.ts added earlier in this branch: how
the copy/remove/overlay diff works, how to add a new profile, and a table
of what each current profile drops/adds relative to the full-backend
base — including the finding that drizzle-orm/drizzle-zod/pg stay as
dependencies in every profile (even DB-free ones) because
@donilite/wrap's own barrel imports them unconditionally at the module
level (entity.ts, events.ts, dto.ts, database.ts), independent of whether
a DB connection is ever established. Decoupling that is flagged as a
separate follow-up, not attempted here.
@sourcery-ai

sourcery-ai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors the create-wrap CLI into a profile-driven scaffolder with reusable prompting/scaffolding utilities, introduces multiple non-DB project profiles, enhances Swagger tag handling, and tightens auth/OpenAPI typing and exports, plus adds targeted tests and docs for the new behaviors.

File-Level Changes

Change Details Files
Refactor CLI entrypoint to use shared prompt utilities and a profile-driven scaffolding pipeline instead of inline template copying logic.
  • Replace manual stdin prompt with text() helper for project name input.
  • Introduce promptForAnswers() and PROFILES for selecting project profiles and toggles (Redis, realtime).
  • Delegate all filesystem/template logic to scaffoldProject(config, answers, WRAP_VERSION).
  • Adjust logging and next-steps output to be profile-aware (conditional DB steps, init:env script).
packages/create-wrap/src/index.ts
packages/create-wrap/src/prompts.ts
packages/create-wrap/src/profiles.ts
packages/create-wrap/src/utils.ts
packages/create-wrap/src/scaffold.ts
packages/create-wrap/package.json
Add a profile overlay system that derives multiple project types from a single base template using remove.txt and files/ diffs.
  • Define Config, COPY_IGNORE, TEXT_EXTENSIONS, copyTree(), removePaths(), parseRemoveList() utilities.
  • Implement applyProfileOverlay() for non-full-backend profiles and applyFullBackendToggles() for Redis/realtime switches.
  • Modify package.json, .gitignore, and .env handling in scaffoldProject() for all profiles.
  • Create profile-specific directories with configs, bootstrap, factory, index/index.controller, feature slices, tests, README, and package.json for lightweight-api, api-aggregator, fullstack-ssr, and gateway.
packages/create-wrap/src/scaffold.ts
packages/create-wrap/src/profiles.ts
packages/create-wrap/template/**
packages/create-wrap/profiles/README.md
packages/create-wrap/profiles/lightweight-api/**
packages/create-wrap/profiles/api-aggregator/**
packages/create-wrap/profiles/fullstack-ssr/**
packages/create-wrap/profiles/gateway/**
Strengthen auth/OpenAPI typing and exports to provide strongly-typed security schemes and make auth controllers easier to consume.
  • Introduce OpenApiSecurityScheme, OpenApiOAuthFlow(s), and OpenApiSecuritySchemes types in AuthController.
  • Make AuthController.openApiSecurityScheme() abstract and typed, and update CombinedAuthController and JwtCookieAuthController to conform.
  • Export isAuthController and all OpenAPI-related types from auth middleware index.
  • Update tests to use OpenApiSecuritySchemes typing for custom auth controllers.
packages/wrap/src/middleware/auth/auth.controller.ts
packages/wrap/src/middleware/auth/auth.middleware.ts
packages/wrap/src/middleware/auth/jwt-cookie.controller.ts
packages/create-wrap/template/tests/auth.combine.test.ts
Enhance SwaggerGenerator to support per-route tags that override controller tags and to merge configured tag descriptions with generated tags.
  • Import OpenApiSecuritySchemes and use it for DEFAULT_SECURITY_SCHEMES and securitySchemes typing.
  • Add RouteOptions.tags with documentation explaining flat tag namespace semantics.
  • Compute operationTags per route (route.tags override controller tags) and attach them to operations while collecting tag names.
  • Modify tag generation to merge config.tags entries (preserving descriptions) and fall back to bare {name} for generated tags without config.
  • Extend Swagger tests to cover tag inheritance/override and tag description merging behavior.
packages/wrap/src/swagger/index.ts
packages/wrap/src/decorators/interfaces.ts
packages/create-wrap/template/tests/swagger.test.ts
Add profile-specific test suites and configuration files to validate composition roots, routing behavior, Swagger output, and auth scheme exposure for each new profile.
  • Add wrap.test.ts and swagger.test.ts per profile to exercise health routes, feature controllers, 404 shape, parent/child route ordering, and JwtCookieAuthController.openApiSecurityScheme().
  • Stub fetch globally in api-aggregator Swagger tests to avoid network calls while ensuring AggregatorService singleton behavior.
  • Add profile-specific app.config.ts, bootstrap.ts, web.factory.ts, and env examples tailored to non-DB setups and upstream APIs/proxy targets.
  • Add SSR-focused tests and routing/render helpers for fullstack-ssr profile (TanStack Router + React SSR).
packages/create-wrap/profiles/lightweight-api/files/tests/wrap.test.ts
packages/create-wrap/profiles/lightweight-api/files/tests/swagger.test.ts
packages/create-wrap/profiles/api-aggregator/files/tests/wrap.test.ts
packages/create-wrap/profiles/api-aggregator/files/tests/swagger.test.ts
packages/create-wrap/profiles/gateway/files/tests/wrap.test.ts
packages/create-wrap/profiles/gateway/files/tests/swagger.test.ts
packages/create-wrap/profiles/fullstack-ssr/files/tests/wrap.test.ts
packages/create-wrap/profiles/fullstack-ssr/files/tests/swagger.test.ts
packages/create-wrap/profiles/fullstack-ssr/files/tests/ssr.test.ts
packages/create-wrap/profiles/*/files/src/config/app.config.ts
packages/create-wrap/profiles/*/files/src/bootstrap.ts
packages/create-wrap/profiles/*/files/src/factory/web.factory.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The profile-specific scaffolding in scaffold.ts (e.g. applyFullBackendToggles, applyProfileOverlay) relies on fairly brittle string/regex replacements and hard-coded markers; consider factoring these into more structured transformations or shared constants so template changes don’t silently break the toggles/overlays.
  • The new wsProxy helper assumes Bun’s WebSocket client semantics (e.g. binaryType, close signatures) but doesn’t guard against unsupported environments or mismatched upstream implementations; it may be worth isolating those assumptions (and errors) behind a small adapter so future runtime changes don’t require touching the proxy logic directly.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The profile-specific scaffolding in `scaffold.ts` (e.g. `applyFullBackendToggles`, `applyProfileOverlay`) relies on fairly brittle string/regex replacements and hard-coded markers; consider factoring these into more structured transformations or shared constants so template changes don’t silently break the toggles/overlays.
- The new `wsProxy` helper assumes Bun’s `WebSocket` client semantics (e.g. `binaryType`, close signatures) but doesn’t guard against unsupported environments or mismatched upstream implementations; it may be worth isolating those assumptions (and errors) behind a small adapter so future runtime changes don’t require touching the proxy logic directly.

## Individual Comments

### Comment 1
<location path="packages/create-wrap/profiles/gateway/files/src/features/proxy/web/proxy.controller.ts" line_range="30-37" />
<code_context>
+  @Get({ path: "/*", description: "Proxy GET requests to the configured upstream" })
+  async proxyGet(c: Context) {
+    const upstreamPath = c.req.path.replace(/^\/proxy/, "");
+    return proxy(`${appConfig.upstream.baseUrl}${upstreamPath}`, {
+      headers: {
+        ...c.req.header(),
+        "X-Forwarded-For": c.req.header("x-forwarded-for") ?? "",
+        "X-Forwarded-Host": c.req.header("host"),
+        // Don't propagate this app's own auth to the upstream by default —
+        // opt back in per-route if the upstream expects it.
+        Authorization: undefined,
+      },
+    });
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid sending an `Authorization: undefined` header to the upstream when stripping auth.

Because `headers` is a plain object, setting `Authorization: undefined` is likely to result in an `Authorization: "undefined"` header being sent instead of omitting it. Please construct the headers so that `Authorization` is not present at all (e.g., build a `Headers` instance and `delete("Authorization")`, or avoid adding the property when spreading) to ensure the header is actually stripped.
</issue_to_address>

### Comment 2
<location path="packages/create-wrap/src/scaffold.ts" line_range="124-129" />
<code_context>
+}
+
+/** Remove a contiguous block of lines between (and including) two markers, if both are found. */
+function stripBlock(content: string, startMarker: string, endMarker: string): string {
+  const start = content.indexOf(startMarker);
+  if (start === -1) return content;
+  const end = content.indexOf(endMarker, start);
+  if (end === -1) return content;
+  return content.slice(0, start) + content.slice(end + endMarker.length);
+}
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Profile toggles rely on brittle marker-based string slicing that may break with small template edits.

`stripBlock` and the hard-coded `replace` calls in `applyFullBackendToggles()` depend on exact comment text and brace/newline layout in `bootstrap.ts`, `index.ts`, `compose.yml`, and `.env.example`. Small formatting changes could stop the edits from working or remove the wrong block. Consider targeting more structured delimiters (e.g. `// BEGIN REDIS BLOCK` / `// END REDIS BLOCK`) or simple AST-based transforms, and at least make the end markers more specific than a bare `}` to reduce accidental removals.

Suggested implementation:

```typescript
/**
 * Remove a contiguous block of lines between (and including) two markers, if both are found.
 *
 * Markers are intended to be structured delimiters (e.g. `// BEGIN REDIS BLOCK` / `// END REDIS BLOCK`)
 * that appear on their own lines in the template. Avoid using generic markers such as a bare `}`.
 */
function stripBlock(content: string, startMarker: string, endMarker: string): string {
  // Split into lines so we only ever remove whole lines between explicit delimiters.
  const lines = content.split(/\r?\n/);

  const startIndex = lines.findIndex((line) => line.includes(startMarker));
  if (startIndex === -1) return content;

  const endIndex = lines.findIndex(
    (line, idx) => idx >= startIndex && line.includes(endMarker),
  );
  if (endIndex === -1) return content;

  const keptLines = [
    ...lines.slice(0, startIndex),
    ...lines.slice(endIndex + 1),
  ];

  return keptLines.join("\n");
}

const TEMPLATE_DIR = join(import.meta.dir, "..", "template");

```

To fully address the brittleness mentioned in your comment, you should also:
1. Add explicit BEGIN/END markers (e.g. `// BEGIN REDIS BLOCK` / `// END REDIS BLOCK`) in the relevant template files (`bootstrap.ts`, `index.ts`, `compose.yml`, `.env.example`) wrapping each profile-specific block.
2. Update `applyFullBackendToggles()` (and any other callers of `stripBlock`) to use those structured markers instead of hard-coded brace or newline-based substrings (especially replacing any use of a bare `}` as the end marker).
3. Ensure any future profile blocks follow the same marker convention so `stripBlock` remains robust against formatting changes within the block.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +30 to +37
return proxy(`${appConfig.upstream.baseUrl}${upstreamPath}`, {
headers: {
...c.req.header(),
"X-Forwarded-For": c.req.header("x-forwarded-for") ?? "",
"X-Forwarded-Host": c.req.header("host"),
// Don't propagate this app's own auth to the upstream by default —
// opt back in per-route if the upstream expects it.
Authorization: undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Avoid sending an Authorization: undefined header to the upstream when stripping auth.

Because headers is a plain object, setting Authorization: undefined is likely to result in an Authorization: "undefined" header being sent instead of omitting it. Please construct the headers so that Authorization is not present at all (e.g., build a Headers instance and delete("Authorization"), or avoid adding the property when spreading) to ensure the header is actually stripped.

Comment on lines +124 to +129
function stripBlock(content: string, startMarker: string, endMarker: string): string {
const start = content.indexOf(startMarker);
if (start === -1) return content;
const end = content.indexOf(endMarker, start);
if (end === -1) return content;
return content.slice(0, start) + content.slice(end + endMarker.length);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): Profile toggles rely on brittle marker-based string slicing that may break with small template edits.

stripBlock and the hard-coded replace calls in applyFullBackendToggles() depend on exact comment text and brace/newline layout in bootstrap.ts, index.ts, compose.yml, and .env.example. Small formatting changes could stop the edits from working or remove the wrong block. Consider targeting more structured delimiters (e.g. // BEGIN REDIS BLOCK / // END REDIS BLOCK) or simple AST-based transforms, and at least make the end markers more specific than a bare } to reduce accidental removals.

Suggested implementation:

/**
 * Remove a contiguous block of lines between (and including) two markers, if both are found.
 *
 * Markers are intended to be structured delimiters (e.g. `// BEGIN REDIS BLOCK` / `// END REDIS BLOCK`)
 * that appear on their own lines in the template. Avoid using generic markers such as a bare `}`.
 */
function stripBlock(content: string, startMarker: string, endMarker: string): string {
  // Split into lines so we only ever remove whole lines between explicit delimiters.
  const lines = content.split(/\r?\n/);

  const startIndex = lines.findIndex((line) => line.includes(startMarker));
  if (startIndex === -1) return content;

  const endIndex = lines.findIndex(
    (line, idx) => idx >= startIndex && line.includes(endMarker),
  );
  if (endIndex === -1) return content;

  const keptLines = [
    ...lines.slice(0, startIndex),
    ...lines.slice(endIndex + 1),
  ];

  return keptLines.join("\n");
}

const TEMPLATE_DIR = join(import.meta.dir, "..", "template");

To fully address the brittleness mentioned in your comment, you should also:

  1. Add explicit BEGIN/END markers (e.g. // BEGIN REDIS BLOCK / // END REDIS BLOCK) in the relevant template files (bootstrap.ts, index.ts, compose.yml, .env.example) wrapping each profile-specific block.
  2. Update applyFullBackendToggles() (and any other callers of stripBlock) to use those structured markers instead of hard-coded brace or newline-based substrings (especially replacing any use of a bare } as the end marker).
  3. Ensure any future profile blocks follow the same marker convention so stripBlock remains robust against formatting changes within the block.

@DoniLite
DoniLite merged commit fc31642 into main Jul 13, 2026
2 checks passed
@DoniLite
DoniLite deleted the feat/lightweight-setup branch July 13, 2026 18:41
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.

The CLI is broken after the first question step Add CLAUDE.md to describe project for LLM

1 participant