Skip to content

Conversation

@abshnko
Copy link
Member

@abshnko abshnko commented Nov 10, 2025

image

todo:

  • fill out .gitmodules
  • fill out deploy worflows
  • tests in ci .github/workflows/nodejs.condo.ci.yml
  • fix uppercase variable in templates/000-services-urls.yaml
  • remove unused deps from server miniapp:
    Unused dependencies (4)
    dayjs apps/payments-gateway/package.json
    graphql-tag apps/payments-gateway/package.json
    react apps/payments-gateway/package.json
    zod apps/payments-gateway/package.json
    Unused devDependencies (2)
    @graphql-codegen/typescript apps/payments-gateway/package.json
    @types/react apps/payments-gateway/package.json

Summary by CodeRabbit

  • New Features

    • New CLI to create mini-app projects with an interactive setup wizard and templates for server, client, and full‑stack apps.
    • Automated scaffolding including optional dependency installation, code formatting, and post‑create guidance.
    • Helm chart generation and secret/values integration for deployment-ready projects.
  • Chores

    • Added top-level script to invoke the mini‑app creator from the repo.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 10, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a new create-miniapp CLI package and npm script; implements interactive scaffolding, templated client/server/fullstack projects, Helm chart generation and secret handling, OIDC middleware in templates, install/format/prepare helpers, utilities, and many template assets and configs.

Changes

Cohort / File(s) Summary
Root script
package.json
Adds create-miniapp npm script running the CLI.
CLI package manifest & build
packages/create-miniapp-cli/package.json, packages/create-miniapp-cli/.gitignore, packages/create-miniapp-cli/tsconfig.json, packages/create-miniapp-cli/tsup.config.ts
New package metadata, dependencies, build and TS config, and gitignore.
CLI core & orchestrator
packages/create-miniapp-cli/src/index.ts, packages/create-miniapp-cli/src/runCli.ts
Main CLI entrypoint and interactive prompt flow to collect options and drive scaffolding/preparation.
Helpers: project, format, install, scaffold
packages/create-miniapp-cli/src/helpers/createProject.ts, .../format.ts, .../installDependencies.ts, .../installPackages.ts, .../scaffoldProject.ts, .../logNextSteps.ts, .../setImportAlias.ts
Project creation, template copy/overwrite handling, package install/boilerplate installers, formatting, import alias replacement, and post-setup messaging.
Installers surface & dependency map
packages/create-miniapp-cli/src/installers/index.ts, .../dependencyVersionMap.ts
Installer types, available packages list, and static dependency version map.
Helm generation & utilities
packages/create-miniapp-cli/src/installers/helm/* (constants.ts, templates.ts, utils.ts, values.ts, servicesUrls.ts, reviewSecrets.ts, secretValues.ts, index.ts)
Helm template fragments, writers, prefix utilities, resource prompts/validation, values and secret management, and top-level setupHelm orchestration.
Prepare workflow
packages/create-miniapp-cli/src/installers/prepare.ts
Multi-step prepareApp orchestration (builds, migrations, retries, migrator fallback).
Utilities
packages/create-miniapp-cli/src/consts.ts, src/utils/* (addPackageDependency.ts, addPackageScript.ts, getHelmTemplates.ts, getUserPkgManager.ts, isAppType.ts, logger.ts, parseNameAndPath.ts, removeTrailingSlash.ts, renderTitle.ts, validateAppName.ts, validateImportAlias.ts)
Constants, logger, pkg manager detection, name/alias parsing and validation, package.json helpers, Helm template generators, and title rendering.
Template assets — client / server / fullstack
packages/create-miniapp-cli/template/{client,server,fullstack}/** (configs, scripts, pages, domains, middleware, public assets, tests, tsconfigs, package.json, gitignore, etc.)
New project templates: Next.js client, Keystone server, combined fullstack; includes OIDC middleware, domain schemas, test setup, extensive config files, pages, localization, email templates, Helm-compatible files, and helper bin scripts.
Other
apps/resident-app
Submodule pointer updated.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant npm as npm script
    participant CLI as create-miniapp CLI
    participant Prompts as Interactive prompts
    participant FS as File system
    participant Helm as Helm setup
    participant Installer as Dependency installer
    participant Prepare as prepareApp

    User->>npm: npm run create-miniapp
    npm->>CLI: invoke CLI entry
    CLI->>Prompts: ask for name, type, resources, flags
    Prompts->>CLI: return choices
    CLI->>FS: scaffold template -> copy files (handle overwrite)
    CLI->>FS: update package.json, replace aliases
    CLI->>Helm: setupHelm(app config)
    Helm->>FS: write templates, update services & secrets, update values
    CLI->>Installer: optionally install dependencies
    Installer->>FS: run package manager install in projectDir
    CLI->>Prepare: run prepareApp steps (build, migrations, types)
    Prepare->>FS: run build/migration scripts
    CLI->>User: log next steps and exit
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • Helm secret encryption and werf integration (secret-values.ts, reviewSecrets.ts).
  • OIDC middleware and session/nonce/token handling in template server middlewares.
  • Scaffold safety: overwrite/clear prompts and file rename logic (scaffoldProject.ts).
  • prepareApp orchestration: command ordering, retry/fallback behavior and subprocess log handling.
  • Large template additions: ensure consistency across package.json, tsconfig, and build scripts in templates.

Possibly related PRs

Suggested labels

✋🙂 Review please, 🐘 BIG

Suggested reviewers

  • abshnko

Poem

🐰 I hopped through folders, small and grand,
I seeded templates by gentle hand.
With Helm and prompts and OIDC cheer,
New miniapps now spring up here.
Thump-thump—deploy, the rabbit claps a hand!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(packages): added create miniapp CLI' accurately describes the main change: the addition of a new create-miniapp CLI tool to the packages directory.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/packages/add-condo-miniapp-cli

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +28 to +32
console.log('process.cwd(): ', process.cwd())
console.log('projectName: ', projectName)
const rootDir = path.resolve(process.cwd(), '../..') // relative to CLI package
const appsDir = path.join(rootDir, 'apps')
const projectDir = path.resolve(appsDir, projectName)

Choose a reason for hiding this comment

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

P1 Badge Scaffolding path escapes repository root

The project directory is derived from path.resolve(process.cwd(), '../..'), which assumes the process is launched from packages/create-miniapp-cli. The root package.json introduces a create-miniapp script that runs the CLI from the repo root, so executing yarn create-miniapp new-app now resolves ../../apps/new-app, i.e. two directories above the repo. This either fails with ENOENT or creates files outside the repository, making the CLI unusable from its advertised entry point.

Useful? React with 👍 / 👎.

Comment on lines +11 to +18
files.forEach((file) => {
const filePath = path.join(directoryPath, file)
if (fs.statSync(filePath).isDirectory()) {
replaceTextInFiles(filePath, search, replacement)
} else {
const data = fs.readFileSync(filePath, 'utf8')
const updatedData = data.replace(new RegExp(search, 'g'), replacement)
fs.writeFileSync(filePath, updatedData, 'utf8')

Choose a reason for hiding this comment

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

P1 Badge Import alias replacement corrupts binary assets

replaceTextInFiles recurses through every file in the scaffolded project and rewrites them with fs.readFileSync(filePath, 'utf8') / writeFileSync(..., 'utf8'). The CLI runs this over the entire template directory to swap aliases, which includes PNGs, PDFs and other binaries. Reading and writing those files as UTF‑8 will rewrite their bytes and break the assets even when no replacement occurs. The traversal should skip non‑text files or operate on buffered data without encoding.

Useful? React with 👍 / 👎.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 56

🧹 Nitpick comments (24)
packages/create-miniapp-cli/template/client/_gitignore (1)

1-37: Consider adding section headers for maintainability.

The AI summary mentions organized sections (DEPENDENCIES, TESTING, BUILD, ENV FILES, LOGS, MISC, MAC, TS/BUILD), but the file lacks explicit section headers or comments. Adding brief comments to group related patterns would improve maintainability and make it easier for developers to locate or add patterns in the future.

Consider adding section headers:

+# Dependencies
 node_modules
 bin/.local
+
+# Build artifacts
 dist
 out
 .next
+
+# Environment & Configuration
 .idea
 .env
 .kmigrator
+
+# OS & IDE
 .DS_Store
 .docker-cache-warming
 .expo
 .expo-shared
 web-build
+
+# Generated files
 coverage
 __generated__

This would improve readability and make the file easier to maintain as it grows.

packages/create-miniapp-cli/template/fullstack/pages/global.css (3)

1-1: Consider hosting fonts locally to reduce external dependencies.

Importing fonts from googleapis.com adds a runtime external dependency and introduces a potential single point of failure if Google's services are unavailable. For better reliability and performance, consider:

  1. Downloading the font files and bundling them locally within the template
  2. Implementing font fallback chains to gracefully degrade if the import fails
  3. Using the font-display: swap property to ensure text remains readable while fonts load

This is especially important for a CLI-generated template that should be as self-contained and resilient as possible.


9-9: Replace deprecated text-rendering: optimizelegibility property.

The text-rendering: optimizelegibility value is deprecated in modern CSS. Replace it with text-rendering: optimizeSpeed, which is the modern standard.

- text-rendering: optimizelegibility;
+ text-rendering: optimizeSpeed;

42-42: Reconsider !important flag in favor of specificity or a wrapper class.

While !important is pragmatic for overriding Ant Design styles in a template, it reduces maintainability. Consider alternative approaches:

  1. Wrap the component in a scoped class (e.g., .miniapp-form .react-tel-input .ant-input)
  2. Use CSS modules to isolate and scope styles naturally
  3. Document the rationale for !important with an inline comment if it must remain

This will make the template easier to customize and extend without cascading specificity issues.

packages/create-miniapp-cli/template/fullstack/_gitignore (1)

1-37: Well-structured gitignore template with minor enhancements recommended.

The ignore patterns are comprehensive and appropriate for a fullstack mini-app template, covering build outputs, IDE files, dependency directories, and security-sensitive files.

Recommended improvements:

  1. Expand environment file patterns (line 7): Consider using .env* or adding specific patterns like .env.local, .env.*.local to catch environment-specific files that developers might create.

  2. Add trailing newline: Git convention suggests files should end with a newline character for better compatibility with various tools.

Apply this diff:

-.env
+.env*
+!.env.example

And ensure the file ends with a newline:

 # Turborepo local cache
 .turbo
+

Note: The .env* pattern with !.env.example allows committing example environment files while protecting actual secrets.

packages/create-miniapp-cli/template/server/jest.setupSpec.js (1)

5-14: Consider logging specific test properties for clearer output.

The test lifecycle logging infrastructure is functional, but consider these improvements:

  1. Log specific properties: Logging the entire jasmine.currentTest object may produce verbose output. Consider logging jasmine.currentTest.fullName or jasmine.currentTest.description for cleaner, more readable test output.

  2. Inconsistent property access: Lines 8-9 use dot notation (jasmine.currentTest), while lines 12-13 use bracket notation (jasmine['currentTest']). Using consistent dot notation throughout would improve readability.

Apply this diff for clearer logging:

-    beforeEach(() => console.log('[BEGIN] TEST:', jasmine['currentTest'])) // eslint-disable-line
-    afterEach(() => console.log('[END] TEST:', jasmine['currentTest'])) // eslint-disable-line
+    beforeEach(() => console.log('[BEGIN] TEST:', jasmine.currentTest?.fullName)) // eslint-disable-line
+    afterEach(() => console.log('[END] TEST:', jasmine.currentTest?.fullName)) // eslint-disable-line
packages/create-miniapp-cli/template/server/domains/user/utils/clientSchema/User.ts (1)

16-30: Consider exporting all available hooks for completeness.

The generateReactHooks function returns 10 hooks, but only 5 are exported. The omitted hooks are:

  • useCreateMany - for batch creation
  • useUpdateMany - for batch updates
  • useSoftDeleteMany - for batch soft deletes
  • useCount - for counting records
  • useAllObjects - for loading all records with automatic pagination

While the selective export might be intentional to keep the API surface minimal, consider whether template users might need these additional hooks. If they're commonly needed, exporting all hooks would provide a more complete toolkit.

If you decide to export all hooks, apply this diff:

 const {
     useObject,
     useObjects,
     useCreate,
+    useCreateMany,
     useUpdate,
+    useUpdateMany,
     useSoftDelete,
+    useSoftDeleteMany,
+    useCount,
+    useAllObjects,
 } = generateReactHooks<User, UserCreateInput, UserUpdateInput, QueryAllUsersArgs>(UserGQL)

 export {
     useObject,
     useObjects,
     useCreate,
+    useCreateMany,
     useUpdate,
+    useUpdateMany,
     useSoftDelete,
+    useSoftDeleteMany,
+    useCount,
+    useAllObjects,
 }
packages/create-miniapp-cli/template/server/clients/condoClient.js (1)

5-8: Consider removing the no-op constructor or documenting its purpose.

The constructor only forwards arguments to the superclass without adding any behavior. If this is intentional scaffolding for future auto-generated methods (as suggested by the comment on line 10), consider adding a comment to clarify. Otherwise, you can remove the constructor entirely and rely on the inherited one.

 class CondoClient extends ApolloServerClient {
-    constructor ({ endpoint, authRequisites, opts }) {
-        super(endpoint, authRequisites, opts)
-    }
-
-    // AUTO_GENERATED: use needed methods here
+    // Note: Constructor inherited from ApolloServerClient
+    // AUTO_GENERATED: add custom methods here as needed
 }
packages/create-miniapp-cli/template/fullstack/jest.setupTest.js (1)

13-22: Consider extracting duplicated Jasmine patching logic.

This Jasmine reporter patching code (lines 13-22) is identical to jest.setupSpec.js lines 3-12. While the duplication is minimal, extracting it to a shared utility function would improve maintainability.

You could create a shared setupJasmineLogging.js utility:

// setupJasmineLogging.js
module.exports = function setupJasmineLogging() {
    const EXTRA_LOGGING = !process.env.DISABLE_LOGGING
    
    if (EXTRA_LOGGING) {
        jasmine.getEnv().addReporter({ // eslint-disable-line
            specStarted: result => jasmine.currentTest = result, // eslint-disable-line
            specDone: result => jasmine.currentTest = result, // eslint-disable-line
        })
        
        beforeEach(() => console.log('[BEGIN] TEST:', jasmine['currentTest'])) // eslint-disable-line
        afterEach(() => console.log('[END] TEST:', jasmine['currentTest'])) // eslint-disable-line
    }
}

Then import and call it in both setup files.

packages/create-miniapp-cli/.gitignore (1)

45-45: Remove duplicate *.tsbuildinfo entry.

The pattern *.tsbuildinfo appears twice in the gitignore file (lines 45 and 60). While this doesn't cause issues functionally, removing the duplicate improves maintainability.

 .docusaurus
-*.tsbuildinfo

 # MAC

Also applies to: 60-60

packages/create-miniapp-cli/src/utils/validateImportAlias.ts (1)

1-6: Consider adding explicit return type annotation.

The function implicitly returns string | undefined, but having an explicit return type annotation improves code clarity and type safety.

-export const validateImportAlias = (input: string) => {
+export const validateImportAlias = (input: string): string | undefined => {
     if (input.startsWith('.') || input.startsWith('/')) {
         return 'Import alias can\'t start with \'.\' or \'/\''
     }
-    return
 }

Note: You can also remove the explicit return on line 5 since TypeScript will infer undefined when no value is returned.

packages/create-miniapp-cli/src/utils/addPackageScript.ts (1)

7-26: Consider adding error handling for missing package.json.

The function assumes package.json exists and has a valid structure. If the file doesn't exist or is malformed, readJSONSync will throw an unclear error.

Consider adding a guard:

 export const addPackageScript = (opts: {
     scripts: Record<string, string>
     projectDir: string
 }) => {
     const { scripts, projectDir } = opts
 
     const packageJsonPath = path.join(projectDir, 'package.json')
+    if (!fs.existsSync(packageJsonPath)) {
+        throw new Error(`package.json not found at ${packageJsonPath}`)
+    }
     const packageJsonContent = fs.readJSONSync(packageJsonPath) as PackageJson
 
     packageJsonContent.scripts = {
-        ...packageJsonContent.scripts,
+        ...(packageJsonContent.scripts || {}),
         ...scripts,
     }
packages/create-miniapp-cli/template/fullstack/README.md (2)

8-48: Add language specifiers to code blocks.

The code blocks at lines 8-48, 52-61, and 65-68 are missing language specifiers, which reduces readability and syntax highlighting support.

Apply language specifiers to improve clarity:

-```
+```bash
 cat > .env << ENDOFFILE
 ...

And for the other blocks:

```diff
-```
+```bash
 # create migration script at migrations/20201212124723-00xx_name.js
 ...

```diff
-```
+```bash
 # create ./schema.d.ts file with all GQL types
 ...

---

`76-87`: **Fix list indentation.**

The list items have inconsistent indentation (1 space instead of 0), which doesn't follow markdown best practices.

Apply this diff to fix indentation:

```diff
 Main folders:
- - `./<domain>/constants` -- contains various constants. Used on the client and server side
- - `./<domain>/gql` -- contains all GraphQL queries. Used on the server and client side
+- `./<domain>/constants` -- contains various constants. Used on the client and server side
+- `./<domain>/gql` -- contains all GraphQL queries. Used on the server and client side
 ...
packages/create-miniapp-cli/template/server/package.json (2)

8-8: Fix script formatting.

Line 8 contains extra spaces and trailing whitespace that should be cleaned up.

Apply this diff to clean up the spacing:

-    "dev": "yarn build:deps && NODE_ENV=development && yarn dev:start  ",
+    "dev": "yarn build:deps && NODE_ENV=development && yarn dev:start",

35-35: Consider using caret range for openid-client.

The openid-client dependency is pinned to an exact version 5.7.1 without a caret, which prevents minor and patch updates. Unless there's a specific reason for pinning, consider using ^5.7.1 to allow compatible updates.

Apply this diff if no pinning is required:

-    "openid-client": "5.7.1",
+    "openid-client": "^5.7.1",
packages/create-miniapp-cli/src/utils/removeTrailingSlash.ts (1)

1-7: Avoid mutating the input parameter.

While this works correctly, reassigning the input parameter is unnecessary since strings are immutable in JavaScript.

Apply this diff for a cleaner implementation:

 export const removeTrailingSlash = (input: string) => {
-    if (input.length > 1 && input.endsWith('/')) {
-        input = input.slice(0, -1)
-    }
-
-    return input
+    return input.length > 1 && input.endsWith('/') ? input.slice(0, -1) : input
 }
packages/create-miniapp-cli/src/helpers/format.ts (1)

9-24: Add error handling for formatting failures.

The execa call can fail if the format:write script errors (e.g., prettier config issues, syntax errors in generated code). Without error handling, the spinner won't show failure state and the exception will propagate uncaught, potentially crashing the CLI or leaving the user confused.

Apply this diff to add error handling:

 export const formatProject = async ({
     pkgManager,
     projectDir,
 }: {
     pkgManager: PackageManager
     projectDir: string
 }) => {
     logger.info(`Formatting project with ${'prettier'}...`)
     const spinner = ora('Running format command\n').start()
 
-    await execa(pkgManager, ['run', 'format:write'], {
-        cwd: projectDir,
-    })
+    try {
+        await execa(pkgManager, ['run', 'format:write'], {
+            cwd: projectDir,
+        })
+        spinner.succeed(`${chalk.green('Successfully formatted project')}`)
+    } catch (error) {
+        spinner.warn(`${chalk.yellow('Formatting failed, but project was created')}`)
+        logger.warn('You may need to run formatting manually later.')
+    }
+}
  • spinner.succeed(${chalk.green('Successfully formatted project')})
    -}
packages/create-miniapp-cli/src/installers/helm/servicesUrls.ts (1)

10-25: Add error handling for file operations.

The file read/write operations lack error handling. If .helm/templates/000-services-urls.yaml doesn't exist, has permission issues, or write fails, the uncaught error will crash the installer or leave it in an inconsistent state.

Consider adding error handling:

 export async function updateServicesUrls (appName: string) {
-    const content = await fs.readFile(SERVICES_URLS, 'utf8')
+    try {
+        const content = await fs.readFile(SERVICES_URLS, 'utf8')
+    } catch (error) {
+        throw new Error(`Failed to read services URLs file: ${error.message}`)
+    }
+    
     const upperUnderscoredAppName = appName.replace(/-/g, '_').toUpperCase()
     const line = `${upperUnderscoredAppName}_DOMAIN: {{ ( printf "https://%s" $.Values.global.ci_${upperUnderscoredAppName}_url ) | b64enc }}`
     const idx = content.lastIndexOf('{{- end')
     
     let out = ''
     if (idx !== -1) {
         out = content.slice(0, idx).trimEnd() + '\n' + line + '\n\n' + content.slice(idx)
     } else {
         out = content.trimEnd() + '\n' + line + '\n'
     }
-    await fs.writeFile(SERVICES_URLS, out, 'utf8')
+    
+    try {
+        await fs.writeFile(SERVICES_URLS, out, 'utf8')
+    } catch (error) {
+        throw new Error(`Failed to write services URLs file: ${error.message}`)
+    }
 
     return SERVICES_URLS
 }
packages/create-miniapp-cli/src/helpers/createProject.ts (2)

30-32: Hard-coded path resolution is fragile.

The path resolution path.resolve(process.cwd(), '../..') assumes a specific directory structure relative to the CLI package. This could break if the CLI is invoked from different contexts or the project structure changes.

Consider using a more robust approach, such as:

  • Using PKG_ROOT constant if available
  • Using __dirname with a known offset
  • Adding a verification step to ensure the resolved path exists and is valid

44-67: Remove commented-out code or add TODO.

Large blocks of commented code reduce readability. If this code is intended for future implementation, add a TODO comment with context. Otherwise, remove it.

packages/create-miniapp-cli/src/runCli.ts (3)

113-114: Extract repeated default value logic.

The expression (results.name ? \@${results.name}` : `@${cliProvidedName}`) || defaultOptions.flags.importAliasis duplicated for bothdefaultValueandplaceholder`. Extract to a variable for better maintainability.

Example:

const defaultAlias = (results.name ? `@${results.name}` : `@${cliProvidedName}`) || defaultOptions.flags.importAlias
return p.text({
    message: 'What import alias would you like to use?',
    defaultValue: defaultAlias,
    placeholder: defaultAlias,
    validate: validateImportAlias,
})

161-161: Formatting inconsistency.

Missing space before opening brace: if (v){ should be if (v) { to match the style used elsewhere in the file (e.g., lines 25, 160).


206-206: Type cast followed by nullish coalescing is redundant.

The expression project.importAlias as string ?? cliResults.flags.importAlias casts to string but then uses nullish coalescing, suggesting importAlias might be undefined. The cast doesn't provide type safety here.

Consider removing the unnecessary cast:

-            importAlias: project.importAlias as string ?? cliResults.flags.importAlias,
+            importAlias: project.importAlias ?? cliResults.flags.importAlias,

"createapp": "node ./bin/createapp",
"prepare": "husky install"
"prepare": "husky install",
"create-miniapp": "node ./packages/create-miniapp-cli/dist/index.js"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the CLI package build step is documented or automated.

The script references dist/index.js, which is a build artifact that must exist before this command can run. Users attempting to run this script without first building the CLI package will encounter a runtime error.

Consider one of the following approaches:

  1. Add a build script that ensures the CLI is built before running (e.g., using a wrapper that checks for dist/index.js and builds if missing)
  2. Document the build requirement in the README or contributing guide
  3. Use a prepare script or hook to automatically build the CLI package

Run the following to verify if there's a build automation:


🏁 Script executed:

#!/bin/bash
# Check if there's any build automation for the CLI package
echo "=== Checking for build scripts in create-miniapp-cli package ==="
cat packages/create-miniapp-cli/package.json | jq -r '.scripts'

echo -e "\n=== Checking for prepare/postinstall hooks ==="
cat packages/create-miniapp-cli/package.json | jq -r '.scripts | with_entries(select(.key | test("prepare|postinstall")))'

echo -e "\n=== Checking turbo.json for build pipeline ==="
if [ -f turbo.json ]; then
  cat turbo.json | jq -r '.pipeline.build // "No build pipeline found"'
fi

Length of output: 328


I need to gather more information to understand the build automation at the root level.

I need to check the root turbo.json configuration and .gitignore to understand the full build orchestration:


🏁 Script executed:

#!/bin/bash
# Check root turbo.json pipeline
echo "=== Root turbo.json ==="
if [ -f turbo.json ]; then
  cat turbo.json | jq '.' 2>/dev/null || cat turbo.json
else
  echo "No turbo.json found"
fi

# Check if dist is in .gitignore
echo -e "\n=== Checking .gitignore for dist patterns ==="
grep -E 'dist|build' .gitignore 2>/dev/null || echo "No dist/build patterns found in .gitignore"

# Check root package.json scripts
echo -e "\n=== Root package.json scripts ==="
cat package.json | jq '.scripts' 2>/dev/null || echo "Could not parse scripts"

Length of output: 213


Let me locate the repository structure and check if dist artifacts are version controlled:


🏁 Script executed:

#!/bin/bash
# Find all package.json files
echo "=== Package.json files in repo ==="
fd -t f 'package\.json$' -x ls -la {} \;

# Check if dist files are tracked in git
echo -e "\n=== Git tracking of dist directories ==="
git ls-files | grep -E 'dist/' | head -20 || echo "No dist files tracked in git"

# Check .gitignore existence and content
echo -e "\n=== .gitignore content (first 50 lines) ==="
if [ -f .gitignore ]; then
  head -50 .gitignore
else
  echo "No .gitignore found"
fi

# Check for CI workflows
echo -e "\n=== CI workflows in .github/workflows ==="
ls -la .github/workflows/ 2>/dev/null || echo "No .github/workflows found"

Length of output: 5454


Now let me check the root package.json and relevant CI workflow:


🏁 Script executed:

#!/bin/bash
# Check root package.json content
echo "=== Root package.json ==="
cat ./package.json

# Check if create-miniapp script is used anywhere in CI
echo -e "\n=== Usage of 'create-miniapp' in CI workflows ==="
grep -r "create-miniapp" .github/workflows/ || echo "No 'create-miniapp' found in CI workflows"

# Check for build/prepare hooks in root package.json
echo -e "\n=== Looking for build/prepare pattern in workflows ==="
grep -E "(npm.*build|yarn.*build|pnpm.*build|prepare|postinstall)" .github/workflows/*.yml .github/workflows/*.yaml 2>/dev/null | head -20

Length of output: 5431


Add a prepare script to auto-build the CLI package before the create-miniapp command can run.

The script references a build artifact (dist/index.js) from a package that isn't automatically built. Users who clone the repository and run yarn create-miniapp without first building will encounter a missing file error.

While yarn build (which uses turbo) can build all packages including create-miniapp-cli, this dependency is implicit and undocumented. Add a prepare hook or modify the create-miniapp script to ensure the dist directory exists:

"prepare": "husky install && yarn build:packages",

Or create a wrapper script that checks for and builds the CLI if needed before delegating to the actual command.

🤖 Prompt for AI Agents
In package.json around line 25, the "create-miniapp" script points at a
non-built artifact (packages/create-miniapp-cli/dist/index.js) causing failures
for fresh clones; add a prepare script to build the CLI automatically (for
example run husky install && yarn build:packages) or replace the create-miniapp
script with a small wrapper that checks for the dist/index.js and runs the
package build if missing before executing the CLI; update the scripts section
accordingly so users can run yarn create-miniapp without manually building.

Comment on lines +11 to +18
interface CreateProjectOptions {
projectName: string
scopedAppName: string
noInstall: boolean
importAlias: string
appType: AppType
packages?: PkgInstallerMap
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Unused parameter: importAlias.

The CreateProjectOptions interface includes an importAlias parameter (line 15) that is never used in the function body. Either use it or remove it from the interface.

🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/helpers/createProject.ts around lines 11 to
18, the CreateProjectOptions interface declares an unused importAlias field;
either remove it or wire it into the function. Fix by deleting importAlias from
the interface and from any call sites/types that reference it (or if it was
intended to control imports, add a parameter to the createProject function and
use importAlias where imports/aliases are configured), then update tests/usage
to match.

Comment on lines +28 to +29
console.log('process.cwd(): ', process.cwd())
console.log('projectName: ', projectName)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove debug console.log statements.

Debug logging statements should be replaced with proper logging via the logger utility or removed before merging.

Apply this diff:

     const pkgManager = getUserPkgManager()
-    console.log('process.cwd(): ', process.cwd())
-    console.log('projectName: ', projectName)
     const rootDir = path.resolve(process.cwd(), '../..')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log('process.cwd(): ', process.cwd())
console.log('projectName: ', projectName)
const pkgManager = getUserPkgManager()
const rootDir = path.resolve(process.cwd(), '../..')
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/helpers/createProject.ts around lines 28 to
29, remove the two debug console.log statements (console.log('process.cwd(): ',
process.cwd()) and console.log('projectName: ', projectName')) or replace them
with the project's logger utility (e.g., logger.debug or logger.info) if that
context needs to be recorded; if you choose to log, import the logger at the top
of the file and use a single structured message rather than raw console.log
calls.

Comment on lines +39 to +47
export const installDependencies = async ({
projectDir,
}: {
projectDir: string
}) => {
const pkgManager = getUserPkgManager()

await runInstallCommand(pkgManager, projectDir)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing return value propagation.

The runInstallCommand returns a boolean indicating success/failure, but installDependencies doesn't return or propagate this value. Callers have no way to determine if installation succeeded.

Apply this diff:

-export const installDependencies = async ({
+export const installDependencies = async ({
     projectDir,
 }: {
     projectDir: string
-}) => {
+}): Promise<boolean> => {
     const pkgManager = getUserPkgManager()

-    await runInstallCommand(pkgManager, projectDir)
+    return await runInstallCommand(pkgManager, projectDir)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const installDependencies = async ({
projectDir,
}: {
projectDir: string
}) => {
const pkgManager = getUserPkgManager()
await runInstallCommand(pkgManager, projectDir)
}
export const installDependencies = async ({
projectDir,
}: {
projectDir: string
}): Promise<boolean> => {
const pkgManager = getUserPkgManager()
return await runInstallCommand(pkgManager, projectDir)
}
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/helpers/installDependencies.ts around lines
39 to 47, installDependencies currently calls runInstallCommand but does not
return its boolean result; update the function to propagate that value by
returning the awaited result (e.g., return await runInstallCommand(pkgManager,
projectDir)) and adjust the function signature to reflect Promise<boolean> if
needed so callers can determine success/failure.

Comment on lines +18 to +27
for (const [name, pkgOpts] of Object.entries(packages)) {
if (pkgOpts.inUse) {
const spinner = ora(`Boilerplating ${name}...`).start()
pkgOpts.installer(options)
spinner.succeed(
chalk.green(
`Successfully setup boilerplate for ${chalk.green.bold(name)}`,
),
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Await installers so boilerplate actually finishes.

Installer implementations are async; calling them without await resolves immediately, the spinner reports success, and any rejection becomes an unhandled promise. The boilerplate may still be writing files while the CLI moves on. Make the function async, await each installer, and fail the spinner on error so the CLI stops cleanly.

Apply this diff:

-export const installPackages = (options: InstallPackagesOptions) => {
+export const installPackages = async (options: InstallPackagesOptions) => {
     const { packages } = options
     logger.info('Adding boilerplate...')

     for (const [name, pkgOpts] of Object.entries(packages)) {
         if (pkgOpts.inUse) {
             const spinner = ora(`Boilerplating ${name}...`).start()
-            pkgOpts.installer(options)
-            spinner.succeed(
-                chalk.green(
-                    `Successfully setup boilerplate for ${chalk.green.bold(name)}`,
-                ),
-            )
+            try {
+                await pkgOpts.installer(options)
+                spinner.succeed(
+                    chalk.green(
+                        `Successfully setup boilerplate for ${chalk.green.bold(name)}`,
+                    ),
+                )
+            } catch (error) {
+                spinner.fail(`Failed to setup boilerplate for ${name}`)
+                throw error
+            }
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (const [name, pkgOpts] of Object.entries(packages)) {
if (pkgOpts.inUse) {
const spinner = ora(`Boilerplating ${name}...`).start()
pkgOpts.installer(options)
spinner.succeed(
chalk.green(
`Successfully setup boilerplate for ${chalk.green.bold(name)}`,
),
)
}
export const installPackages = async (options: InstallPackagesOptions) => {
const { packages } = options
logger.info('Adding boilerplate...')
for (const [name, pkgOpts] of Object.entries(packages)) {
if (pkgOpts.inUse) {
const spinner = ora(`Boilerplating ${name}...`).start()
try {
await pkgOpts.installer(options)
spinner.succeed(
chalk.green(
`Successfully setup boilerplate for ${chalk.green.bold(name)}`,
),
)
} catch (error) {
spinner.fail(`Failed to setup boilerplate for ${name}`)
throw error
}
}
}
}
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/helpers/installPackages.ts around lines 18 to
27, the installer calls are not awaited so async installers resolve immediately,
the spinner reports success prematurely, and rejections become unhandled; make
the surrounding function async, await pkgOpts.installer(options) for each
package, wrap each await in try/catch to call spinner.fail(...) on error and
rethrow or return a rejected promise so the CLI stops cleanly, and keep
spinner.succeed only after the awaited installer completes.


const conf = require('@open-condo/config')

const { createOrUpdateUser } = require('~/domains/user/utils/serverSchema/createOrUpdateUser')
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Swap the ~ alias for the real template path.

The scaffolded project does not define a ~ resolver, so this require fails at runtime and violates the repository’s no-restricted-modules rule. Please import via the relative path that exists within the template tree.

-const { createOrUpdateUser } = require('~/domains/user/utils/serverSchema/createOrUpdateUser')
+const { createOrUpdateUser } = require('../domains/user/utils/serverSchema/createOrUpdateUser')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { createOrUpdateUser } = require('~/domains/user/utils/serverSchema/createOrUpdateUser')
const { createOrUpdateUser } = require('../domains/user/utils/serverSchema/createOrUpdateUser')
🧰 Tools
🪛 ESLint

[error] 7-7: '~/domains/user/utils/serverSchema/createOrUpdateUser' module is restricted from being used by a pattern.

(no-restricted-modules)

🤖 Prompt for AI Agents
In packages/create-miniapp-cli/template/server/middlewares/oidc.js around line
7, the require uses the non-resolved '~' alias which breaks runtime imports and
violates the no-restricted-modules rule; replace the alias with the relative
template path by changing the import to
require('../domains/user/utils/serverSchema/createOrUpdateUser') (adjust the
relative traversal if your file location differs) so the module resolves within
the scaffolded template tree.

Comment on lines +70 to +107
req.session[redirectKey] = (req.query.next && typeof req.query.next === 'string') ? req.query.next : '/'
req.session[oidcSessionKey] = checks
await req.session.save()
try {
const redirectUrl = helper.authorizationUrlWithParams(checks)
return res.redirect(redirectUrl)
} catch (error) {
return next(error)
}
})
app.get('/oidc/callback', async (req, res, next) => {
try {
const checks = req.session[oidcSessionKey]
const redirect = req.session[redirectKey] || '/'

if (!isObject(checks) || !checks) {
return res.status(400).send('ERROR: Invalid nonce and state')
}
const { userInfo, token } = await helper.completeAuth(req, checks)
const user = await createOrUpdateUser(keystone, userInfo)
const appToken = await keystone._sessionManager.startAuthedSession(req, {
item: { id: user.id },
list: keystone.lists['User'],
meta: {
source: 'oidc',
provider: 'condo',
clientID: helper.clientID,
},
})

delete req.session[oidcSessionKey]
delete req.session[redirectKey]
req.session[CONDO_OIDC_TOKEN_KEY] = token
req.session[APP_TOKEN_KEY] = appToken
await req.session.save()

return res.redirect(redirect)
} catch (error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate the next redirect target to avoid open redirects.

Storing the raw req.query.next and later redirecting to it allows an attacker to send users to an arbitrary external site after login. That’s a classic open-redirect vulnerability. Clamp the value to same-origin paths (e.g. require it to start with /) before saving and before redirecting.

-            req.session[redirectKey] = (req.query.next && typeof req.query.next === 'string') ? req.query.next : '/'
+            const nextParam = typeof req.query.next === 'string' ? req.query.next : '/'
+            req.session[redirectKey] = nextParam.startsWith('/') ? nextParam : '/'
@@
-                const redirect = req.session[redirectKey] || '/'
+                const storedRedirect = req.session[redirectKey]
+                const redirect = (typeof storedRedirect === 'string' && storedRedirect.startsWith('/')) ? storedRedirect : '/'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
req.session[redirectKey] = (req.query.next && typeof req.query.next === 'string') ? req.query.next : '/'
req.session[oidcSessionKey] = checks
await req.session.save()
try {
const redirectUrl = helper.authorizationUrlWithParams(checks)
return res.redirect(redirectUrl)
} catch (error) {
return next(error)
}
})
app.get('/oidc/callback', async (req, res, next) => {
try {
const checks = req.session[oidcSessionKey]
const redirect = req.session[redirectKey] || '/'
if (!isObject(checks) || !checks) {
return res.status(400).send('ERROR: Invalid nonce and state')
}
const { userInfo, token } = await helper.completeAuth(req, checks)
const user = await createOrUpdateUser(keystone, userInfo)
const appToken = await keystone._sessionManager.startAuthedSession(req, {
item: { id: user.id },
list: keystone.lists['User'],
meta: {
source: 'oidc',
provider: 'condo',
clientID: helper.clientID,
},
})
delete req.session[oidcSessionKey]
delete req.session[redirectKey]
req.session[CONDO_OIDC_TOKEN_KEY] = token
req.session[APP_TOKEN_KEY] = appToken
await req.session.save()
return res.redirect(redirect)
} catch (error) {
const nextParam = typeof req.query.next === 'string' ? req.query.next : '/'
req.session[redirectKey] = nextParam.startsWith('/') ? nextParam : '/'
req.session[oidcSessionKey] = checks
await req.session.save()
try {
const redirectUrl = helper.authorizationUrlWithParams(checks)
return res.redirect(redirectUrl)
} catch (error) {
return next(error)
}
})
app.get('/oidc/callback', async (req, res, next) => {
try {
const checks = req.session[oidcSessionKey]
const storedRedirect = req.session[redirectKey]
const redirect = (typeof storedRedirect === 'string' && storedRedirect.startsWith('/')) ? storedRedirect : '/'
if (!isObject(checks) || !checks) {
return res.status(400).send('ERROR: Invalid nonce and state')
}
const { userInfo, token } = await helper.completeAuth(req, checks)
const user = await createOrUpdateUser(keystone, userInfo)
const appToken = await keystone._sessionManager.startAuthedSession(req, {
item: { id: user.id },
list: keystone.lists['User'],
meta: {
source: 'oidc',
provider: 'condo',
clientID: helper.clientID,
},
})
delete req.session[oidcSessionKey]
delete req.session[redirectKey]
req.session[CONDO_OIDC_TOKEN_KEY] = token
req.session[APP_TOKEN_KEY] = appToken
await req.session.save()
return res.redirect(redirect)
} catch (error) {
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/template/server/middlewares/oidc.js around lines
70 to 107, the code saves req.query.next directly to session and later redirects
to it which permits open-redirects; validate and normalize the next target
before saving and before redirecting by only accepting same-origin paths
(require it to be a non-empty string that starts with a single '/' and does not
start with '//' or contain a full origin), otherwise fall back to '/' or a
configured safe default; implement this check where you set
req.session[redirectKey] and again before res.redirect(redirect), ensuring you
canonicalize the value (trim and remove query fragments if desired) and only
assign the validated value to the session/redirect variable.

"mkdirp": "^3.0.1",
"openid-client": "5.7.1",
"react": "^18.3.1",
"zod": "^4.0.8"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify zod version.

The specified version ^4.0.8 doesn't exist. The latest stable version of zod is 3.x.


🌐 Web query:

What is the latest stable version of zod npm package?

💡 Result:

The latest stable published version on the npm package root is 3.25.20. [1][2]

Sources:
[1] npm listing for zod v3.25.20. (npmjs.com)
[2] Zod maintainer note: Zod v4 is published under the subpath "zod/v4" while the package root continues to publish v3. (github.com)

Citations:


Correct the zod version specification.

The latest stable published version on the npm package root is 3.25.20, and zod v4 is published under the subpath "zod/v4" while the package root continues to publish v3. The current specification "zod": "^4.0.8" will fail because zod v4 is not available at the main package root.

Update to either "zod": "^3.25.20" or, if v4 is required, use "zod/v4": "^4.0.8".

🤖 Prompt for AI Agents
In packages/create-miniapp-cli/template/server/package.json around line 37, the
dependency is incorrectly set to "zod": "^4.0.8" which will fail because zod v4
is published under the "zod/v4" subpath while the package root publishes v3;
update the dependency to either "zod": "^3.25.20" to use the package root v3, or
if you need v4 change the spec to "zod/v4": "^4.0.8" so npm resolves the correct
package path.

"./src/*"
]
},
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Remove trailing comma.

JSON does not allow trailing commas. This will cause a syntax error when parsing the tsconfig.json file.

Apply this diff to remove the trailing comma:

       "@cli/*": [
         "./src/*"
       ]
     },
-  },
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
},
}
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/tsconfig.json around line 23, remove the trailing
comma after the closing brace on that line so the JSON is valid; update the file
to end the object/array without a comma, save, and re-validate the JSON (e.g.,
run a JSON linter or tsc) to ensure no syntax errors remain.

minify: true,
target: 'esnext',
outDir: 'dist',
onSuccess: 'node dist/index.js', // TODO: remove for prod
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove onSuccess hook before production release.

The onSuccess hook automatically executes the built CLI, which is useful during development but should not be included in the production build.

Apply this diff to remove the onSuccess hook:

 export default defineConfig({
     clean: true,
     entry: ['src/index.ts'],
     format: ['esm'],
     minify: true,
     target: 'esnext',
     outDir: 'dist',
-    onSuccess: 'node dist/index.js', // TODO: remove for prod
 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onSuccess: 'node dist/index.js', // TODO: remove for prod
export default defineConfig({
clean: true,
entry: ['src/index.ts'],
format: ['esm'],
minify: true,
target: 'esnext',
outDir: 'dist',
})
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/tsup.config.ts around line 10, the tsup
configuration includes an onSuccess: 'node dist/index.js' hook used for
development; remove that onSuccess entry from the exported config (delete the
onSuccess property and trailing comma if required) so the production build does
not automatically execute the built CLI.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 22

♻️ Duplicate comments (38)
packages/create-miniapp-cli/template/server/domains/common/schema/fields.js (1)

10-10: Typo already flagged: "devise" should be "device".

This typo has already been identified in a previous review. Please apply the suggested fix to correct "devise" to "device" in the schemaDoc string.

packages/create-miniapp-cli/tsconfig.json (1)

23-25: Remove trailing commas to fix JSON syntax errors.

JSON does not allow trailing commas. The file has two trailing comma violations that will prevent it from being parsed as valid JSON:

  1. Line 23: Trailing comma after the compilerOptions object (already flagged in a previous review)
  2. Line 24: Trailing comma after the include array before the final closing brace

Apply this diff to remove both trailing commas:

     },
-  },
+  }
   "include": ["src/**/*"],
-}
+  ]
+}

Actually, let me provide the correct fix:

     },
-  },
+  }
   "include": ["src/**/*"]
-}
+}
packages/create-miniapp-cli/tsup.config.ts (1)

3-11: Remove dev-only onSuccess hook from tsup config before merge.

Auto-running node dist/index.js on every build is useful locally but unexpected/undesirable in CI and production builds (side effects, slower builds, surprise executions). The existing // TODO: remove for prod indicates this is temporary; it should be cleaned up now.

Consider applying:

 export default defineConfig({
     clean: true,
     entry: ['src/index.ts'],
     format: ['esm'],
     minify: true,
     target: 'esnext',
     outDir: 'dist',
-    onSuccess: 'node dist/index.js', // TODO: remove for prod
 })

The rest of the config looks reasonable for a small ESM CLI bundle.

Please double-check that your local/dev workflow doesn’t depend on this hook anymore (e.g. switch to npm run dev or similar) before removing it.

packages/create-miniapp-cli/template/fullstack/tsconfig.json (2)

22-26: Remove trailing comma to fix JSON syntax error.

Line 25 has a trailing comma after the last array element, which is invalid JSON syntax and will cause parsing errors when tsconfig.json is loaded.

Apply this diff:

   "include": [
     "next-env.d.ts",
     "**/*.ts",
-    "**/*.tsx",
+    "**/*.tsx"
   ],

11-11: Enable strict mode for better type safety.

Setting "strict": false in a new project template disables all strict type-checking options, which can lead to runtime errors that TypeScript could catch. New projects should enable strict mode by default to benefit from TypeScript's full type safety.

Apply this diff:

-    "strict": false,
+    "strict": true,
packages/create-miniapp-cli/template/config/next-config-appdir.js (1)

5-5: Remove trailing semicolons to match project style.

The project follows a no-semicolon convention. Remove semicolons from lines 5, 8, and 10.

Apply this diff:

-import "./src/env.js";
+import "./src/env.js"

 /** @type {import("next").NextConfig} */
-const config = {};
+const config = {}

-export default config;
+export default config

Also applies to: 8-8, 10-10

packages/create-miniapp-cli/template/fullstack/lang/en/messages/1S_INSTRUCTION/email.html.njk (1)

12-48: Russian text in EN locale template (previously flagged).

The localization mismatch identified in the previous review remains unresolved. This EN template still contains Russian text in the CTA (line 12: "В Дома →"), header (line 14: "Оферта принята"), and footer (line 48), while the body text (line 16) is correctly in English. This inconsistency will confuse English-speaking recipients.

packages/create-miniapp-cli/template/server/clients/index.spec.js (1)

32-39: Mock external dependencies to prevent real network calls.

This test calls await getCondoClient() which instantiates a CondoClient, connects to CONDO_DOMAIN, and calls signIn(). Without mocking, the test makes real network requests and will fail if the server is unavailable or environment variables are not set.

Mock the CondoClient constructor and signIn method to isolate the test:

     test('should return the same instance on second call (singleton)', async () => {
+        // Mock CondoClient to prevent real network calls
+        jest.mock('./condoClient', () => ({
+            CondoClient: jest.fn().mockImplementation(() => ({
+                signIn: jest.fn().mockResolvedValue(undefined),
+            })),
+        }))
+        
         const { getCondoClient } = require('./index')
         
         const firstCall = await getCondoClient()
         const secondCall = await getCondoClient()
         
         expect(firstCall).toBe(secondCall)
     })
packages/create-miniapp-cli/template/server/clients/index.js (3)

5-9: Fix the API_TARGET validation logic.

The current approach sets a placeholder default and then throws an error if that placeholder is detected. This is a trap pattern. If API_TARGET is required, validate it directly without a misleading fallback. Also use strict equality (===) instead of loose equality (==).

Apply this diff:

-// TODO: (AUTO_GENERATED): provide a fallback option for API_TARGET
-const API_TARGET = conf['API_TARGET'] || 'API TARGET OF YOUR MINIAPP'
-if (API_TARGET == 'API TARGET OF YOUR MINIAPP') {
-    throw new Error('You need to provide API_TARGET environment variable and provide a fallback value')
-}
+const API_TARGET = conf['API_TARGET']
+if (!API_TARGET) {
+    throw new Error('API_TARGET environment variable is required')
+}

18-36: The clientName parameter is ignored after the first initialization.

The singleton pattern caches a single instance, meaning only the first clientName value is used. Subsequent calls with different clientName values return the same cached instance, which may lead to unexpected behavior.

Consider one of these solutions:

Option 1: Remove the parameter if all clients should share the same name:

-async function getCondoClient (clientName = 'template') {
+async function getCondoClient () {
     if (!condoClient) {
         const condoAuthConfig = JSON.parse(conf['CONDO_CLIENT_AUTH_CONFIG'] || '{}')
         const condoServerUrl = conf['CONDO_DOMAIN']
 
         condoClient = new CondoClient({
             endpoint: `${condoServerUrl}/admin/api`,
             authRequisites: condoAuthConfig,
             opts: {
-                clientName,
+                clientName: 'template',
                 customHeaders: { 'x-target': API_TARGET },
             },
         })

Option 2: Cache per clientName if different names need different instances:

-let condoClient
+const condoClients = {}

 async function getCondoClient (clientName = 'template') {
-    if (!condoClient) {
+    if (!condoClients[clientName]) {
         const condoAuthConfig = JSON.parse(conf['CONDO_CLIENT_AUTH_CONFIG'] || '{}')
         const condoServerUrl = conf['CONDO_DOMAIN']
 
-        condoClient = new CondoClient({
+        condoClients[clientName] = new CondoClient({
             endpoint: `${condoServerUrl}/admin/api`,
             authRequisites: condoAuthConfig,
             opts: {
                 clientName,
                 customHeaders: { 'x-target': API_TARGET },
             },
         })
 
-        await condoClient.signIn()
+        await condoClients[clientName].signIn()
     }
 
-    return condoClient
+    return condoClients[clientName]
 }

20-24: Validate CONDO_DOMAIN before constructing the endpoint.

If CONDO_DOMAIN is undefined or empty, the endpoint will be malformed (e.g., undefined/admin/api), causing connection errors with unclear messages.

Apply this diff:

         const condoAuthConfig = JSON.parse(conf['CONDO_CLIENT_AUTH_CONFIG'] || '{}')
         const condoServerUrl = conf['CONDO_DOMAIN']
+        if (!condoServerUrl) {
+            throw new Error('CONDO_DOMAIN environment variable is required')
+        }
 
         condoClient = new CondoClient({
             endpoint: `${condoServerUrl}/admin/api`,
packages/create-miniapp-cli/template/server/jest.setupTest.js (1)

1-22: Template still uses restricted @app/* and repo‑internal modules

This template depends on @app/~/index and @open-condo/* modules, which (a) violate the no-restricted-modules rule for files under packages/, and (b) are tightly coupled to the main Condo repo layout, so a generated mini‑app will not have these modules available by default.

For a template intended to scaffold projects, consider either:

  • Rewriting these imports to template‑local paths/utilities (or injecting them via the CLI at generation time), and/or
  • Adding explicit ESLint overrides that exempt packages/create-miniapp-cli/template/** from the @app/* restriction, if coupling to the monorepo is intentional.

Until this is addressed, lint or runtime will fail in generated projects.

packages/create-miniapp-cli/template/fullstack/jest.setupTest.js (1)

1-22: Repo‑specific imports and inconsistent logging flag in fullstack test setup

Two points here:

  1. Repo‑specific / restricted imports
    The template relies on @app/billing-connector/index and @open-condo/* modules. These are specific to the main repo and, together with the @app/* restriction under packages/, will cause lint and runtime failures in generated mini‑apps unless they live inside this monorepo with matching aliases. Either decouple the template from these imports (e.g. local stubs, CLI‑injected paths, or published packages added to the generated package.json), or explicitly relax ESLint for the template directory.

  2. EXTRA_LOGGING semantics differ from server templates
    Here EXTRA_LOGGING = !process.env.DISABLE_LOGGING, while server templates use process.env.DISABLE_LOGGING !== 'true'. That changes how values like 'false' or '0' behave. For consistency across templates, I’d reuse the explicit 'true' check:

-const EXTRA_LOGGING = !process.env.DISABLE_LOGGING
+const EXTRA_LOGGING = process.env.DISABLE_LOGGING !== 'true'


</blockquote></details>
<details>
<summary>packages/create-miniapp-cli/template/server/jest.config.js (1)</summary><blockquote>

`1-25`: **Fix uuid transform regex and make reporter path template‑safe**

Two issues here:

1. **`transformIgnorePatterns` typo in `spec` project**  
The `spec` project config has a typo and an extra space:

```js
// NOTE: need to pass uid export syntax through babel
transformIgnorePatterns: ['/node_modules/(?! (uuid)/)'],

That space means uuid is still ignored by Babel, leading back to Unexpected token 'export' errors. Align this block with the working schema project configuration and fix the comment:

-            // NOTE: need to pass uid export syntax through babel
-            transformIgnorePatterns: ['/node_modules/(?! (uuid)/)'],
+            // NOTE: need to pass uuid export syntax through babel
+            transformIgnorePatterns: ['/node_modules/(?!(uuid)/)'],
  1. Reporter path assumes monorepo structure
    reporters: ['default', '<rootDir>/../../bin/report-failed-tests.js'] assumes the generated project sits inside the Condo monorepo with that exact bin/ layout. In a standalone mini‑app this file won’t exist, and Jest will fail to start.

    For a safe default in templates, I’d drop the custom reporter (or copy a reporter implementation into the template and point to it):

  • reporters: ['default', '/../../bin/report-failed-tests.js'],
  • reporters: ['default'],

</blockquote></details>
<details>
<summary>packages/create-miniapp-cli/template/fullstack/jest.config.js (1)</summary><blockquote>

`1-36`: **Reporter path is not safe for generated fullstack projects**

The custom reporter `<rootDir>/../../bin/report-failed-tests.js` depends on the Condo monorepo’s directory layout. A mini‑app generated from this template won’t have that file, so Jest will error out when loading the config.

For template safety, I’d either ship a reporter inside the template and reference it via a project‑local path, or just rely on the default reporter:

```diff
-    reporters: ['default', '<rootDir>/../../bin/report-failed-tests.js'],
+    reporters: ['default'],
packages/create-miniapp-cli/template/config/postcss.config.js (1)

1-5: Fix ESLint indent and trailing semicolon in PostCSS config.

ESLint is still flagging this file for 2‑space indent and the extra semicolon after the exported object; this will break lint/CI. Reindent to 4 spaces and drop the final semicolon.

-export default {
-  plugins: {
-    "@tailwindcss/postcss": {},
-  },
-};
+export default {
+    plugins: {
+        "@tailwindcss/postcss": {},
+    },
+}
package.json (1)

24-25: Ensure the create-miniapp CLI is built before running dist/index.js.

create-miniapp points directly to ./packages/create-miniapp-cli/dist/index.js. On a fresh clone, if the CLI package hasn’t been built (and dist isn’t checked in), this command will fail. Either make the build explicit here or in a hook so users can safely run yarn create-miniapp without prior manual build, for example:

-    "prepare": "husky install",
-    "create-miniapp": "node ./packages/create-miniapp-cli/dist/index.js"
+    "prepare": "husky install && yarn build:packages",
+    "create-miniapp": "node ./packages/create-miniapp-cli/dist/index.js"

(or an equivalent wrapper that builds the CLI if dist is missing).

packages/create-miniapp-cli/template/fullstack/public/robots.txt (1)

1-5: Consolidate User-agent: * rules into a single robots.txt block.

The two User-agent: * sections conflict; better to group them so intent is unambiguous (allow /auth/, block everything else):

-User-agent: *
-Allow: /auth/
-
-User-agent: *
-Disallow: *
+User-agent: *
+Allow: /auth/
+Disallow: /
packages/create-miniapp-cli/template/config/_eslint.drizzle.js (1)

1-58: Fix flat ESLint config (remove extends property, clean up imports/formatting)

This config still has the same structural issues called out earlier:

  • In flat config, extends inside a config object is not supported; you need to spread the recommended presets at the top level.
  • Static analysis also flags import order, extra semicolons, and inconsistent indentation, which will cause lint failures in scaffolded projects.

You can apply essentially the same restructuring that was previously suggested:

-import { FlatCompat } from "@eslint/eslintrc";
-import tseslint from 'typescript-eslint';
-// @ts-ignore -- no types for this plugin
-import drizzle from "eslint-plugin-drizzle";
-
-const compat = new FlatCompat({
-  baseDirectory: import.meta.dirname,
-});
-
-export default tseslint.config(
-  {
-		ignores: ['.next']
-	},
-  ...compat.extends("next/core-web-vitals"),
-  {
-    files: ['**/*.ts', '**/*.tsx'],
-    plugins: {
-      drizzle,
-    },
-		extends: [
-			...tseslint.configs.recommended,
-			...tseslint.configs.recommendedTypeChecked,
-			...tseslint.configs.stylisticTypeChecked
-		],
-      rules: {
-    "@typescript-eslint/array-type": "off",
-    "@typescript-eslint/consistent-type-definitions": "off",
-    "@typescript-eslint/consistent-type-imports": [
-      "warn",
-      { prefer: "type-imports", fixStyle: "inline-type-imports" },
-    ],
-    "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
-    "@typescript-eslint/require-await": "off",
-    "@typescript-eslint/no-misused-promises": [
-      "error",
-      { checksVoidReturn: { attributes: false } },
-    ],
-    "drizzle/enforce-delete-with-where": [
-      "error",
-      { drizzleObjectName: ["db", "ctx.db"] },
-    ],
-    "drizzle/enforce-update-with-where": [
-      "error",
-      { drizzleObjectName: ["db", "ctx.db"] },
-    ],
-  },
-  },
-  {
-		linterOptions: {
-			reportUnusedDisableDirectives: true
-		},
-		languageOptions: {
-			parserOptions: {
-				projectService: true
-			}
-		}
-	}
-)
+import { FlatCompat } from "@eslint/eslintrc"
+// @ts-ignore -- no types for this plugin
+import drizzle from "eslint-plugin-drizzle"
+import tseslint from "typescript-eslint"
+
+const compat = new FlatCompat({
+    baseDirectory: import.meta.dirname,
+})
+
+export default tseslint.config(
+    {
+        ignores: [".next"],
+    },
+    ...compat.extends("next/core-web-vitals"),
+    ...tseslint.configs.recommended,
+    ...tseslint.configs.recommendedTypeChecked,
+    ...tseslint.configs.stylisticTypeChecked,
+    {
+        files: ["**/*.ts", "**/*.tsx"],
+        plugins: {
+            drizzle,
+        },
+        rules: {
+            "@typescript-eslint/array-type": "off",
+            "@typescript-eslint/consistent-type-definitions": "off",
+            "@typescript-eslint/consistent-type-imports": [
+                "warn",
+                { prefer: "type-imports", fixStyle: "inline-type-imports" },
+            ],
+            "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
+            "@typescript-eslint/require-await": "off",
+            "@typescript-eslint/no-misused-promises": [
+                "error",
+                { checksVoidReturn: { attributes: false } },
+            ],
+            "drizzle/enforce-delete-with-where": [
+                "error",
+                { drizzleObjectName: ["db", "ctx.db"] },
+            ],
+            "drizzle/enforce-update-with-where": [
+                "error",
+                { drizzleObjectName: ["db", "ctx.db"] },
+            ],
+        },
+    },
+    {
+        linterOptions: {
+            reportUnusedDisableDirectives: true,
+        },
+        languageOptions: {
+            parserOptions: {
+                projectService: true,
+            },
+        },
+    },
+)

This keeps the drizzle rules as‑is while giving you a valid flat config and satisfying the lint hints.

packages/create-miniapp-cli/template/fullstack/package.json (1)

25-58: Re‑evaluate the exact openid-client pin for this new template.

openid-client is pinned to 4.7.4 while most other deps use ranges; for a fresh template that will be cloned over time, it’s safer to either (a) move to the latest compatible 4.x patch or (b) clearly justify/document why 4.7.4 must stay pinned (e.g. known regressions in later 4.x or 6.x). Please re-run your SCA / npm view openid-client checks and adjust or document accordingly.

packages/create-miniapp-cli/src/helpers/createProject.ts (1)

11-70: Fix projectDir resolution and clean up unused/temporary bits.

Three issues here:

  1. Path resolution escapes the repo root (functional/ safety issue).
    rootDir = path.resolve(process.cwd(), '../..') assumes the process is started in packages/create-miniapp-cli. From the advertised root script (yarn create-miniapp ...), process.cwd() will be the repo root, so ../.. points outside the repo and appsDir ends up two levels above it. That can either fail or scaffold into the wrong location. Derive appsDir from a stable anchor (e.g. a PKG_ROOT constant or __dirname) instead of process.cwd(). For example (adjust paths to your existing consts):
-import path from 'path'
+import path from 'path'
+import { PKG_ROOT } from '@cli/consts.js'
@@
-    const rootDir = path.resolve(process.cwd(), '../..') // relative to CLI package
-    const appsDir = path.join(rootDir, 'apps')
+    // Anchor to the repo/apps directory regardless of where the CLI is invoked from
+    const appsDir = path.join(PKG_ROOT, '..', '..', 'apps')
     const projectDir = path.resolve(appsDir, projectName)

Please adapt the relative segments to match how PKG_ROOT is defined and verify that projectDir resolves to <repo>/apps/<projectName>.

  1. Unused option: importAlias.
    CreateProjectOptions.importAlias is never read. Either wire it into the scaffolding flow or remove it from the options type (and any call sites) to avoid a misleading API surface.

  2. Leftover debug logging.
    The console.log calls for process.cwd() and projectName are noisy for an end‑user CLI and should be removed or routed through your logger at an appropriate level if you genuinely need them.

packages/create-miniapp-cli/template/server/bin/generate-condo-schema.js (1)

10-11: Replace ~ alias import with a real relative path so the bin script can run.

This Node script runs outside of the webpack/Next alias setup, so require('~/domains/condo/gql') will fail to resolve and is also blocked by ESLint’s no-restricted-modules rule. Use the actual relative location from bin/ instead:

-const { CONDO_CONFIG } = require('~/domains/condo/gql')
+const { CONDO_CONFIG } = require('../domains/condo/gql')

This keeps the template self-contained and makes the script executable immediately after scaffold.

packages/create-miniapp-cli/template/config/_prettier.config.js (1)

2-2: Remove the trailing semicolon.

The trailing semicolon violates the project's semi: "never" lint rule, as confirmed by static analysis. This causes generated projects to start with a lint error.

Apply this diff:

-export default {};
+export default {}
packages/create-miniapp-cli/src/helpers/installPackages.ts (1)

14-27: Installer calls must be awaited to prevent premature success reporting and unhandled rejections.

The pkgOpts.installer(options) call on line 21 is not awaited, causing the spinner to report success immediately while the installer may still be running or could fail silently. This matches the concern raised in the previous review.

Apply this diff to fix:

-export const installPackages = (options: InstallPackagesOptions) => {
+export const installPackages = async (options: InstallPackagesOptions) => {
     const { packages } = options
     logger.info('Adding boilerplate...')
 
     for (const [name, pkgOpts] of Object.entries(packages)) {
         if (pkgOpts.inUse) {
             const spinner = ora(`Boilerplating ${name}...`).start()
-            pkgOpts.installer(options)
-            spinner.succeed(
-                chalk.green(
-                    `Successfully setup boilerplate for ${chalk.green.bold(name)}`,
-                ),
-            )
+            try {
+                await pkgOpts.installer(options)
+                spinner.succeed(
+                    chalk.green(
+                        `Successfully setup boilerplate for ${chalk.green.bold(name)}`,
+                    ),
+                )
+            } catch (error) {
+                spinner.fail(`Failed to setup boilerplate for ${name}`)
+                throw error
+            }
         }
     }
packages/create-miniapp-cli/template/config/_tailwind.prettier.config.js (1)

2-4: Fix formatting to align with project lint rules.

The file uses 2-space indentation and trailing semicolons, which conflict with the project's ESLint rules (@typescript-eslint/indent expects 4 spaces, @typescript-eslint/semi disallows semicolons). This matches the concern raised in the previous review.

Apply this diff:

 export default {
-  plugins: ["prettier-plugin-tailwindcss"],
-};
+    plugins: ["prettier-plugin-tailwindcss"],
+}
packages/create-miniapp-cli/src/utils/validateAppName.ts (2)

12-17: Remove duplicate findIndex call.

Line 15 repeats the same findIndex operation already computed on line 12.

Apply this diff:

     const indexOfDelimiter = paths.findIndex((p) => p.startsWith('@'))
 
     let appName = paths[paths.length - 1]
-    if (paths.findIndex((p) => p.startsWith('@')) !== -1) {
+    if (indexOfDelimiter !== -1) {
         appName = paths.slice(indexOfDelimiter).join('/')
     }

4-4: Align error message with regex or tighten regex.

The regex permits ., ~, and * but the error message claims only lowercase alphanumeric, -, and _ are allowed.

Either update the error message:

-        return 'App name must consist of only lowercase alphanumeric characters, \'-\', and \'_\''
+        return 'App name must consist of only lowercase alphanumeric characters, \'-\', \'_\', \'.\', \'~\', and \'*\''

Or tighten the regex:

 const validationRegExp =
-  /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/
+  /^(?:@[a-z0-9-_][a-z0-9-_]*\/)?[a-z0-9-_][a-z0-9-_]*$/

Also applies to: 22-22

packages/create-miniapp-cli/src/utils/addPackageDependency.ts (1)

25-29: Dependencies silently fail to install if package.json objects don't exist.

If pkgJson.devDependencies or pkgJson.dependencies is undefined, the assignment on lines 26 or 28 silently fails, resulting in missing dependencies in the generated package.json.

Apply this diff to initialize missing objects:

 dependencies.forEach((pkgName) => {
     const version = dependencyVersionMap[pkgName]

     if (devMode && pkgJson.devDependencies) {
         pkgJson.devDependencies[pkgName] = version
+    } else if (devMode) {
+        pkgJson.devDependencies = { [pkgName]: version }
     } else if (pkgJson.dependencies) {
         pkgJson.dependencies[pkgName] = version
+    } else {
+        pkgJson.dependencies = { [pkgName]: version }
     }
 })
packages/create-miniapp-cli/template/config/_eslint.base.js (1)

13-34: Flat config cannot use extends inside config objects.

ESLint's flat config mode doesn't recognize the extends key inside configuration objects (line 15). This will cause "Key 'extends' was unexpected" errors when running eslint in scaffolded projects, breaking the generated codebase.

Spread the TypeScript-ESLint presets at the top level instead:

 export default tseslint.config(
     {
-        ignores: ['.next']
+        ignores: [".next"],
     },
     ...compat.extends("next/core-web-vitals"),
+    ...tseslint.configs.recommended,
+    ...tseslint.configs.recommendedTypeChecked,
+    ...tseslint.configs.stylisticTypeChecked,
     {
-        files: ['**/*.ts', '**/*.tsx'],
-        extends: [
-            ...tseslint.configs.recommended,
-            ...tseslint.configs.recommendedTypeChecked,
-            ...tseslint.configs.stylisticTypeChecked
-        ],
-        rules: {
-            "@typescript-eslint/array-type": "off",
-            "@typescript-eslint/consistent-type-definitions": "off",
-            "@typescript-eslint/consistent-type-imports": [
-                "warn",
-                { prefer: "type-imports", fixStyle: "inline-type-imports" },
-            ],
-            "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
-            "@typescript-eslint/require-await": "off",
-            "@typescript-eslint/no-misused-promises": [
-                "error",
-                { checksVoidReturn: { attributes: false } },
-            ],
-        },
+        files: ["**/*.ts", "**/*.tsx"],
+        rules: {
+            "@typescript-eslint/array-type": "off",
+            "@typescript-eslint/consistent-type-definitions": "off",
+            "@typescript-eslint/consistent-type-imports": [
+                "warn",
+                { prefer: "type-imports", fixStyle: "inline-type-imports" },
+            ],
+            "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
+            "@typescript-eslint/require-await": "off",
+            "@typescript-eslint/no-misused-promises": [
+                "error",
+                { checksVoidReturn: { attributes: false } },
+            ],
+        },
     },
     {
         linterOptions: {
-            reportUnusedDisableDirectives: true
+            reportUnusedDisableDirectives: true,
         },
         languageOptions: {
             parserOptions: {
-                projectService: true
-            }
-        }
-    }
+                projectService: true,
+            },
+        },
+    },
 )
packages/create-miniapp-cli/src/installers/helm/reviewSecrets.ts (1)

17-17: All three secret values reference the same field.

Line 17 uses .Values.review.pg_${appUnderscore}._default for USER, PASS, and DB, which will result in identical base64-encoded values for all three secrets. This is incorrect and will break database connectivity.

Reference distinct fields for each secret:

-const insert = `  PG_REVIEW_${upperUnderscore}_USER: {{ .Values.review.pg_${appUnderscore}._default | b64enc }}\n  PG_REVIEW_${upperUnderscore}_PASS: {{ .Values.review.pg_${appUnderscore}._default | b64enc }}\n  PG_REVIEW_${upperUnderscore}_DB: {{ .Values.review.pg_${appUnderscore}._default | b64enc }}`
+const insert = `  PG_REVIEW_${upperUnderscore}_USER: {{ .Values.review.pg_${appUnderscore}.user | b64enc }}\n  PG_REVIEW_${upperUnderscore}_PASS: {{ .Values.review.pg_${appUnderscore}.password | b64enc }}\n  PG_REVIEW_${upperUnderscore}_DB: {{ .Values.review.pg_${appUnderscore}.database | b64enc }}`
packages/create-miniapp-cli/src/helpers/installDependencies.ts (1)

39-47: Missing return value propagation.

The runInstallCommand returns a boolean indicating success/failure, but installDependencies doesn't return or propagate this value. Callers have no way to determine if installation succeeded.

Apply this diff:

-export const installDependencies = async ({
+export const installDependencies = async ({
     projectDir,
 }: {
     projectDir: string
-}) => {
+}): Promise<boolean> => {
     const pkgManager = getUserPkgManager()
 
-    await runInstallCommand(pkgManager, projectDir)
+    return await runInstallCommand(pkgManager, projectDir)
 }
packages/create-miniapp-cli/src/installers/prepare.ts (1)

159-165: Run the missing prepare steps.

You define BUILD_DEPS, LOCAL_PREPARE, and BUILD, but never execute them; instead GLOBAL_PREPARE runs twice. As written, the CLI skips dependency builds, local prepare, and the final build entirely. Add those scripts to the execution order (and drop the duplicate global run) so every step actually runs.

Apply this diff:

         SCRIPTS.BUILD_PACKAGES,
-        SCRIPTS.GLOBAL_PREPARE, // base env vars
+        SCRIPTS.GLOBAL_PREPARE,
+        SCRIPTS.BUILD_DEPS,
         SCRIPTS.MAKE_MIGRATIONS,
-        SCRIPTS.GLOBAL_PREPARE, // migrate and run local prepare
+        SCRIPTS.LOCAL_PREPARE,
         SCRIPTS.MAKE_TYPES,
+        SCRIPTS.BUILD,
     ]
packages/create-miniapp-cli/src/helpers/setImportAlias.ts (1)

11-18: Import alias replacement corrupts binary assets.

replaceTextInFiles recurses through every file in the scaffolded project and rewrites them with fs.readFileSync(filePath, 'utf8') / writeFileSync(..., 'utf8'). The CLI runs this over the entire template directory to swap aliases, which includes PNGs, PDFs and other binaries. Reading and writing those files as UTF‑8 will rewrite their bytes and break the assets even when no replacement occurs.

The traversal should skip non‑text files. Apply this diff to filter by extension:

+const TEXT_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.json', '.md', '.txt', '.css', '.scss', '.html', '.yml', '.yaml'])
+
 export function replaceTextInFiles (
     directoryPath: string,
     search: string,
     replacement: string,
 ): void {
     const files = fs.readdirSync(directoryPath)
 
     files.forEach((file) => {
         const filePath = path.join(directoryPath, file)
         if (fs.statSync(filePath).isDirectory()) {
             replaceTextInFiles(filePath, search, replacement)
         } else {
+            const ext = path.extname(filePath)
+            if (!TEXT_EXTENSIONS.has(ext)) return
             const data = fs.readFileSync(filePath, 'utf8')
             const updatedData = data.replace(new RegExp(search, 'g'), replacement)
             fs.writeFileSync(filePath, updatedData, 'utf8')
         }
     })
 }
packages/create-miniapp-cli/template/fullstack/pages/1S/connected.tsx (1)

86-89: Redundant loading check (duplicate).

The conditional on line 87 duplicates the isLoading variable defined on line 86.

Apply this diff:

     const isLoading = launchParamsAreLoading || billingContextLoading || !billingContext
-    if (launchParamsAreLoading || billingContextLoading || !billingContext) {
+    if (isLoading) {
         return <Loader loading={isLoading} size='large' />
     }
packages/create-miniapp-cli/template/server/middlewares/oidc.js (2)

7-7: Swap the ~ alias for the real template path (duplicate).

The scaffolded project does not define a ~ resolver, so this require fails at runtime.

-const { createOrUpdateUser } = require('~/domains/user/utils/serverSchema/createOrUpdateUser')
+const { createOrUpdateUser } = require('../domains/user/utils/serverSchema/createOrUpdateUser')

69-106: Validate the next redirect target to avoid open redirects (duplicate).

Storing raw req.query.next and redirecting to it allows an attacker to send users to arbitrary external sites after login.

-            req.session[redirectKey] = (req.query.next && typeof req.query.next === 'string') ? req.query.next : '/'
+            const nextParam = typeof req.query.next === 'string' ? req.query.next : '/'
+            req.session[redirectKey] = nextParam.startsWith('/') && !nextParam.startsWith('//') ? nextParam : '/'
@@
-                const redirect = req.session[redirectKey] || '/'
+                const storedRedirect = req.session[redirectKey]
+                const redirect = (typeof storedRedirect === 'string' && storedRedirect.startsWith('/') && !storedRedirect.startsWith('//')) ? storedRedirect : '/'
packages/create-miniapp-cli/template/server/package.json (1)

37-37: Correct the zod version specification (duplicate).

The specified version ^4.0.8 doesn't exist at the package root. Zod v4 is published under the subpath "zod/v4" while the package root publishes v3.

Update to either:

-    "zod": "^4.0.8"
+    "zod": "^3.25.20"

Or if v4 is required:

-    "zod": "^4.0.8"
+    "zod/v4": "^4.0.8"
packages/create-miniapp-cli/src/index.ts (1)

60-64: Use the sanitized app name when updating @app references (duplicate).

Line 61 passes the raw CLI input (which may include path segments) into setImportAlias. Pass scopedAppName instead to ensure consistent generated code.

-        setImportAlias(projectDir, importAlias, appName)
+        setImportAlias(projectDir, importAlias, scopedAppName)
🧹 Nitpick comments (34)
packages/create-miniapp-cli/template/fullstack/pages/global.css (3)

1-14: Generally sound global styles; consider modernizing CSS practices.

The body resets provide reasonable defaults for the fullstack template. However, a few practices warrant attention:

  • Line 9 (text-rendering: optimizelegibility): This property is deprecated and no longer recommended for modern browsers. Consider removing it, as modern browsers handle text rendering automatically.
  • Line 5 (overflow-x: hidden): This could hide content that legitimately overflows horizontally. Verify this is intentional and won't cause usability issues on smaller viewports or with dynamic content.

Consider removing the deprecated text-rendering property:

  text-decoration-skip: ink;
-  text-rendering: optimizelegibility;
  -ms-overflow-style: -ms-autohiding-scrollbar;

40-43: Avoid !important; refactor CSS specificity instead.

The !important flag on line 42 is a code smell indicating a CSS specificity conflict with react-tel-input. Rather than using !important, increase selector specificity or remove conflicting styles.

-.react-tel-input .ant-input {
+.ant-input.react-tel-input-input {
  width: 100%;
- background-color: rgb(255 255 255 / 90%) !important;
+ background-color: rgb(255 255 255 / 90%);
}

Alternatively, if this selector is genuinely necessary, document why !important is needed.

Verify that the react-tel-input selector aligns with the library's actual DOM structure to avoid overly broad rules.


24-26: Verify color palette against design system.

The color values (#82879f for labels, #222 for disabled text) should be documented or aligned with your design system tokens. Using semantic color variables (e.g., --color-text-muted, --color-text-disabled) would improve maintainability.

Also applies to: 45-50

packages/create-miniapp-cli/template/fullstack/pages/_document.tsx (1)

7-7: Add lang attribute to <Html> for accessibility.

The <Html> component should include a lang attribute to specify the document's language. This is important for accessibility (screen readers) and SEO best practices.

Apply this diff to add the language attribute:

-            <Html>
+            <Html lang="en">
packages/create-miniapp-cli/template/fullstack/public/500-error.html (1)

4-7: Remove duplicate charset meta tag.

The charset is declared twice (line 4 and line 7), which is redundant. The first declaration is sufficient.

Apply this diff to clean up:

 <head>
     <meta charset="UTF-8">
-    <title>Улучшаем сервис</title>
+    <title>Улучшаем сервис</title>
     <meta name="viewport" content="width=device-width"/>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
 </head>
packages/create-miniapp-cli/template/server/clients/condoClient.js (1)

5-11: Consider removing the redundant constructor.

The constructor only passes arguments to the superclass without additional logic. In ES6 classes, this constructor can be omitted entirely—JavaScript will automatically call the parent constructor with the provided arguments.

Apply this diff:

 class CondoClient extends ApolloServerClient {
-    constructor ({ endpoint, authRequisites, opts }) {
-        super(endpoint, authRequisites, opts)
-    }
-
     // AUTO_GENERATED: use needed methods here
 }
packages/create-miniapp-cli/template/fullstack/pages/_app.tsx (3)

50-51: Simplify redundant fallback logic.

Line 50 already provides a fallback to defaultLocale, making the || intl.locale fallback on line 51 redundant in normal cases. However, this does protect against undefined defaultLocale. Once line 24 has a proper fallback, this can be simplified.

 const locale = get(launchContext, 'condoLocale', defaultLocale)
-localeContext.setLocale(locale || intl.locale)
+localeContext.setLocale(locale)

86-99: Consider using proper GraphQL fragment composition.

The current approach embeds userFragment as a template string, which works but limits IDE/tooling support for GraphQL. For better type safety and tooling, consider using proper fragment composition.

-const userFragment = `
-    id
-    name
-    isAdmin
-    isSupport
-`
-
-export const USER_QUERY = gql`
+export const USER_FRAGMENT = gql`
+    fragment UserFragment on User {
+        id
+        name
+        isAdmin
+        isSupport
+    }
+`
+
+export const USER_QUERY = gql`
+    ${USER_FRAGMENT}
     query {
         authenticatedUser {
-            ${userFragment}
+            ...UserFragment
         }
     }
 `

81-84: Consider validating locale before dynamic import.

While the locale appears to be application-controlled, defensive validation would prevent potential path traversal if the source is ever compromised or changes.

 async function messagesImporter (locale) {
+    // Validate locale to prevent path traversal
+    const validLocales = ['en', 'ru']
+    const safeLocale = validLocales.includes(locale) ? locale : 'en'
+    
-    const localeData = await import(`../lang/${locale}/${locale}`)
+    const localeData = await import(`../lang/${safeLocale}/${safeLocale}`)
     return { ...localeData.default }
 }
packages/create-miniapp-cli/template/fullstack/jest.setupSpec.js (1)

1-12: Align EXTRA_LOGGING flag semantics with server templates

Here EXTRA_LOGGING is !process.env.DISABLE_LOGGING, while server templates use process.env.DISABLE_LOGGING !== 'true'. That difference can make logging toggle behave inconsistently across templates (e.g. DISABLE_LOGGING='false' disables logging here but not on the server side). I’d align on the explicit 'true' check for predictability.

-const EXTRA_LOGGING = !process.env.DISABLE_LOGGING
+const EXTRA_LOGGING = process.env.DISABLE_LOGGING !== 'true'
packages/create-miniapp-cli/template/server/jest.setupSpec.js (1)

1-14: LGTM; this can be the canonical EXTRA_LOGGING pattern

This setup looks good: explicit DISABLE_LOGGING !== 'true' check, timeout, and Jasmine reporter wiring are coherent. I’d use this 'true'-based predicate as the reference for other templates so the logging toggle behaves consistently everywhere.

packages/create-miniapp-cli/src/utils/validateImportAlias.ts (1)

1-6: Make validator’s return type explicit (string | undefined).

To make the contract clearer for callers (and avoid a string | void inference), consider annotating the return type and returning undefined explicitly on success:

-export const validateImportAlias = (input: string) => {
-    if (input.startsWith('.') || input.startsWith('/')) {
-        return 'Import alias can\'t start with \'.\' or \'/\''
-    }
-    return
-}
+export const validateImportAlias = (input: string): string | undefined => {
+    if (input.startsWith('.') || input.startsWith('/')) {
+        return "Import alias can't start with '.' or '/'"
+    }
+    return undefined
+}
packages/create-miniapp-cli/.gitignore (1)

36-60: Remove duplicate *.tsbuildinfo entry (optional cleanup).

*.tsbuildinfo is listed twice; it’s harmless but a bit noisy. You can drop one of the entries to keep the .gitignore DRY.

packages/create-miniapp-cli/template/fullstack/README.md (1)

1-87: Tidy up markdownlint and minor wording issues

A few small tweaks will keep markdown linting clean and improve readability:

  • Add languages to fenced code blocks (for example ```bash around shell snippets and ```sh or ```bash for docker-compose commands at Lines 8–28, 52–61, 65–68).
  • Use “billing self-service” (hyphenated) in the heading / text (Lines 6, 20).
  • Replace the generic link text “here” with something descriptive like “Domain README” (Line 73).
  • Normalize list indentation so list items are not indented with an extra leading space (Lines 76–87).
  • Optional: use “client‑side”, “server‑side”, “test‑side” as compound adjectives in bullet descriptions (Lines 82–84).
packages/create-miniapp-cli/src/utils/isAppType.ts (1)

1-5: Align AppType definition with what isAppType actually checks

Right now AppType is defined as keyof typeof APP_TYPES, but isAppType checks against Object.values(APP_TYPES). This is safe only as long as keys and values stay identical; if they ever diverge, the type guard becomes misleading.

Two cleaner options:

  • If you intend AppType to represent the values, change the type alias:
// in consts.ts
export type AppType = (typeof APP_TYPES)[keyof typeof APP_TYPES]
  • If you intend AppType to represent the keys, change the check:
export function isAppType (value: string): value is AppType {
    return Object.hasOwn(APP_TYPES, value)
}

Either way, keeping the runtime check and the type alias in sync will avoid subtle bugs later.

packages/create-miniapp-cli/src/consts.ts (1)

19-30: Fix formatting inconsistencies.

Minor formatting issues found:

  • Line 23: Space before colon in production :'500m'
  • Line 29: Extra space after closing brace

Apply this diff:

     cpu: {
         default: '250m',
         development: '250m',
-        production :'500m',
+        production: '500m',
     },
     memory: {
         default: '512Mi',
         development: '1024Mi',
         production: '1024Mi',
-    }, 
+    },
 }
packages/create-miniapp-cli/src/installers/helm/utils.ts (2)

77-94: Consider explicit undefined return.

Line 91 has a bare return statement. For clarity in TypeScript, consider return undefined or omitting the return entirely, letting the function fall through.

                     if (res !== true) return res
                 }
             }
-            
-            return
+            return undefined
         },

117-132: Replace any with proper types.

Lines 120-121 use any type, which bypasses TypeScript's type safety. Since the return type is ResourceSettings, consider using Partial<ResourceSettings['cpu']> and Partial<ResourceSettings['memory']> or explicitly typing the objects.

-    const cpuObj: any = { default: cpuParts[0] }
-    const memObj: any = { default: memParts[0] }
+    const cpuObj: Partial<ResourceSettings['cpu']> = { default: cpuParts[0] }
+    const memObj: Partial<ResourceSettings['memory']> = { default: memParts[0] }
packages/create-miniapp-cli/src/utils/removeTrailingSlash.ts (1)

1-7: Consider avoiding parameter reassignment.

Line 3 mutates the input parameter. While functional, avoiding parameter reassignment improves code clarity and follows immutability patterns.

 export const removeTrailingSlash = (input: string) => {
-    if (input.length > 1 && input.endsWith('/')) {
-        input = input.slice(0, -1)
-    }
-
-    return input
+    return input.length > 1 && input.endsWith('/')
+        ? input.slice(0, -1)
+        : input
 }
packages/create-miniapp-cli/template/server/domains/condo/constants.js (1)

1-6: Remove extra blank line.

Line 3 has an unnecessary extra blank line before the exports. While this doesn't affect functionality, removing it improves consistency.

 const CONDO_ORGANIZATION_ID_KEY = 'condoOrganizationId'

-
 module.exports = {
     CONDO_ORGANIZATION_ID_KEY,
 }

Otherwise, the constant definition is clean and well-named.

packages/create-miniapp-cli/src/utils/renderTitle.ts (2)

7-14: Inconsistent color sourcing.

The themeColors object mixes colors from @open-condo/ui/colors (blue, green) with hardcoded hex strings (cyan, magenta, red, yellow). For maintainability and consistency, consider either:

  • Using all colors from the @open-condo/ui/colors palette, or
  • Documenting why certain colors must be hardcoded

19-23: Clarify the workaround comment.

The comment "resolves weird behavior where the ascii is offset" is vague. Consider documenting the specific issue more precisely, for example:

-    // resolves weird behavior where the ascii is offset
+    // Compensates for extra line consumed by yarn/pnpm output during package manager detection

This helps future maintainers understand why this workaround exists.

packages/create-miniapp-cli/src/helpers/format.ts (1)

16-16: Remove unnecessary quotes around 'prettier'.

The string 'prettier' is quoted as if it's a variable placeholder, but it's just a literal string. Consider either removing the quotes or using template literal interpolation if this is meant to be dynamic.

-    logger.info(`Formatting project with ${'prettier'}...`)
+    logger.info('Formatting project with prettier...')
packages/create-miniapp-cli/template/fullstack/pages/_error.tsx (1)

19-19: Fix formatting: add space before opening brace.

-    if (statusCode === 404){
+    if (statusCode === 404) {
packages/create-miniapp-cli/template/server/domains/user/schema/User.js (1)

14-14: Simplify labelResolver: filter is redundant.

The filter(Boolean) is unnecessary since the default values 'noname' and 'noemail' are always truthy strings.

Apply this diff:

-    labelResolver: ({ name = 'noname', email = 'noemail' }) => [name, email].filter(Boolean).join(', '),
+    labelResolver: ({ name = 'noname', email = 'noemail' }) => [name, email].join(', '),
packages/create-miniapp-cli/src/installers/helm/servicesUrls.ts (1)

15-15: Remove unnecessary empty line.

     const idx = content.lastIndexOf('{{- end')
-    
+
     let out = ''
packages/create-miniapp-cli/src/runCli.ts (2)

113-114: Simplify default value expression.

The conditional logic (results.name ? @${results.name} : @${cliProvidedName})is duplicated for bothdefaultValueandplaceholder. Since cliProvidedNameis already defined andresults.namewould only exist ifcliProvidedName` was not provided (line 81-88), this can be simplified.

Consider simplifying:

-defaultValue: (results.name ? `@${results.name}` : `@${cliProvidedName}`) || defaultOptions.flags.importAlias,
-placeholder: (results.name ? `@${results.name}` : `@${cliProvidedName}`) || defaultOptions.flags.importAlias,
+defaultValue: `@${results.name ?? cliProvidedName}` || defaultOptions.flags.importAlias,
+placeholder: `@${results.name ?? cliProvidedName}` || defaultOptions.flags.importAlias,

206-212: Unnecessary nullish coalescing operators.

The ?? operators on lines 206 and 210-212 are unnecessary because the Clack prompts always return the specified type (string, ResourceSettings, MaxOldSpace) and cannot return undefined when the user completes them. The fallback to cliResults.flags.* will never be used.

Consider removing the unnecessary fallbacks for clarity:

-importAlias: project.importAlias as string ?? cliResults.flags.importAlias,
+importAlias: project.importAlias as string,
 appType: project.appType ?? cliResults.flags.appType,
 hasReview: project.hasReview ?? cliResults.flags.hasReview,
 hasWorker: project.hasWorker ?? cliResults.flags.hasWorker,
-appResources: project.appResources as ResourceSettings ?? cliResults.flags.appResources,
-workerResources: project.workerResources as ResourceSettings ?? cliResults.flags.workerResources,
-maxOldSpace: project.maxOldSpace as MaxOldSpace ?? cliResults.flags.maxOldSpace,
+appResources: project.appResources as ResourceSettings,
+workerResources: project.workerResources as ResourceSettings | undefined,
+maxOldSpace: project.maxOldSpace as MaxOldSpace,
packages/create-miniapp-cli/src/installers/helm/index.ts (1)

44-49: Use logger instead of console.error for consistency.

The error handling block uses console.error directly, but the module imports logger and uses it elsewhere. For consistency and centralized logging, use logger.error instead.

Apply this diff:

-        console.error('\n[error]')
+        logger.error('\n[error]')
         if (err.stack) {
-            console.error(err.stack)
+            logger.error(err.stack)
         } else {
-            console.error(err.message || err)
+            logger.error(err.message || err)
         }
packages/create-miniapp-cli/src/helpers/setImportAlias.ts (1)

17-17: Consider escaping the search string for RegExp safety.

While the current callers in setImportAlias use hardcoded patterns, constructing a RegExp from a variable without escaping can be fragile if the function is reused elsewhere. Consider escaping special regex characters.

Apply this diff:

+            const escapedSearch = search.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
-            const updatedData = data.replace(new RegExp(search, 'g'), replacement)
+            const updatedData = data.replace(new RegExp(escapedSearch, 'g'), replacement)
packages/create-miniapp-cli/src/installers/helm/templates.ts (1)

29-30: Consider implementing template filtering based on app configuration.

The TODO indicates templates should be filtered by appType and user input, but currently all 5 templates (including worker) are always generated. Looking at the broader PR context, setupHelm in helm/index.ts receives a hasWorker flag. Consider passing this flag to writeHelmTemplates and conditionally generating the worker template only when needed.

Example approach:

export async function writeHelmTemplates (
    appName: string, 
    nextPrefix: number, 
    reviewEnabled: boolean,
    hasWorker: boolean = true
) {
    const created: string[] = []
    const names = ['app', 'secrets', 'worker', 'migrations', 'ingress'] as const
    for (let i = 0; i < names.length; i++) {
        const role = names[i]
        if (role === 'worker' && !hasWorker) continue
        // ... rest of the logic
    }
    return created
}
packages/create-miniapp-cli/src/installers/index.ts (1)

16-27: Make Installer async‑friendly to match IO‑heavy installers

Given installers like Helm will likely be async (fs, execa, etc.), typing Installer as returning void is restrictive and will either cause type friction or encourage unsafe casts. Consider allowing both sync and async installers:

-export type Installer = (opts: InstallerOptions) => void
+export type Installer = (opts: InstallerOptions) => void | Promise<void>

This keeps current synchronous callers valid while allowing installers that naturally return a Promise<void>.

packages/create-miniapp-cli/src/installers/helm/secretValues.ts (2)

16-18: Reuse shared HELM_DIR constant instead of recomputing here

HELM_DIR is computed here and also exported from packages/create-miniapp-cli/src/installers/helm/constants.ts. Duplicating the path logic risks subtle drift if the base path changes later. Prefer importing the shared constant:

-import { CONDO_ROOT } from '@cli/consts'
+import { HELM_DIR } from '@cli/installers/helm/constants'
@@
-const HELM_DIR = path.resolve(CONDO_ROOT, './.helm')
-const SECRET_VALUES = path.join(HELM_DIR, 'secret-values.yaml')
+const SECRET_VALUES = path.join(HELM_DIR, 'secret-values.yaml')

(or the equivalent import path that matches your alias setup).


88-96: Ensure temporary Werf YAML is always cleaned up on failure

tmpNewAppFile is only unlinked after a successful werf ... encrypt call. If encryption throws, the tmp file is left behind in os.tmpdir(). Not critical per‑run, but over time this can accumulate.

Wrap the unlink in a finally or guard it in the catch:

-    const tmpNewAppFile = path.join(os.tmpdir(), `werf-newapp-${crypto.randomBytes(6).toString('hex')}.yaml`)
-    await fs.writeFile(tmpNewAppFile, yaml.stringify({ [appUnderscore]: newAppData }, { indent: 2 }), 'utf8')
-
-    const { stdout: encryptedNewApp } = await execa('werf', [
-        'helm', 'secret', 'values', 'encrypt', tmpNewAppFile,
-    ], { cwd: CONDO_ROOT })
-
-    await fs.unlink(tmpNewAppFile)
+    const tmpNewAppFile = path.join(os.tmpdir(), `werf-newapp-${crypto.randomBytes(6).toString('hex')}.yaml`)
+    try {
+        await fs.writeFile(tmpNewAppFile, yaml.stringify({ [appUnderscore]: newAppData }, { indent: 2 }), 'utf8')
+
+        const { stdout: encryptedNewApp } = await execa('werf', [
+            'helm', 'secret', 'values', 'encrypt', tmpNewAppFile,
+        ], { cwd: CONDO_ROOT })
+
+        // ... use encryptedNewApp as before
+    } finally {
+        await fs.unlink(tmpNewAppFile).catch(() => {})
+    }

This keeps the tmp directory tidy even when Werf fails.

@@ -1 +1 @@
Subproject commit 1690ce1cb1cf3834ac98c5545179b26b476699b5
Subproject commit 1d86075f9213f0c24a1053c4b8759793b97bb2ae
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify that this submodule update is intentional.

The apps/resident-app submodule pointer has been updated, but the PR objectives focus on adding a create-miniapp CLI tool. Submodule updates in unrelated PRs are often unintentional (e.g., from local development changes).

Please confirm:

  1. Is this submodule update intentional and necessary for this PR?
  2. If so, what changes in the resident-app submodule are required and why?
  3. If not, please revert this change to avoid unrelated modifications.

🏁 Script executed:

#!/bin/bash
# Description: Check if there are references to resident-app in the create-miniapp-cli code

# Search for any references to resident-app in the new CLI package
rg -n "resident-app" --type ts --type js --type json -g '!node_modules' -g '!.git'

Length of output: 1186


I need to gather more context to determine if the submodule update is justified. Let me search for information about the create-miniapp CLI changes and any dependencies.


---

🏁 Script executed:

```shell
#!/bin/bash
# Check what files are in the create-miniapp-cli package
fd -t f "package\.json" | grep -i "miniapp\|create-miniapp"

# Also check if there are any new files or significant changes in packages
fd -t f ".*\.json$" | head -20

Length of output: 1011


🏁 Script executed:

#!/bin/bash
# Look for any references to resident-app in package.json or dependency files that might explain the update
rg -n "resident-app" --type json -A2 -B2

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for any comments or documentation about why resident-app is being updated
rg -n "resident-app.*update|submodule.*resident-app|resident-app.*change" -i -C2

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check git log or commit messages for context (if available in PR description)
# This would help understand if there's a documented reason
echo "Checking if there's documented context about the submodule update..."

Length of output: 68


🏁 Script executed:

#!/bin/bash
# Check the actual dependencies in create-miniapp-cli package.json
cat packages/create-miniapp-cli/package.json | head -50

Length of output: 1204


🏁 Script executed:

#!/bin/bash
# Search for any postinstall or setup scripts that might involve resident-app
rg -n "postinstall|setup|resident-app" packages/create-miniapp-cli/ -C2

Length of output: 23947


🏁 Script executed:

#!/bin/bash
# Check if resident-app is a workspace dependency or referenced anywhere in the root
rg -n "resident-app" -g "package.json" -g "pnpm-workspace.yaml" -g "tsconfig.json" -C2

Length of output: 0


Revert the apps/resident-app submodule update—it's unrelated to this PR.

The create-miniapp-cli package has no dependencies on or references to resident-app, and there is no documented reason for the submodule pointer change. This appears to be an accidental inclusion of local development changes. Please revert the commit hash update to keep this PR focused on adding the create-miniapp CLI tool.

🤖 Prompt for AI Agents
In apps/resident-app around line 1, the PR accidentally updated the submodule
commit pointer; revert that submodule update by checking out the original commit
and committing the revert: find the intended commit hash from the target branch
(e.g., origin/main), then either run git submodule update --init --recursive to
sync or cd into apps/resident-app, git fetch && git checkout <original-hash>, cd
.. && git add apps/resident-app && commit the reversion with a message like
"revert accidental resident-app submodule update" so the PR no longer contains
unrelated submodule pointer changes.

Comment on lines +1 to +51
{
"name": "@open-condo/create-miniapp-cli",
"version": "1.0.0",
"description": "Create Condo-compatible miniapp",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/open-condo-software/condo.git",
"directory": "packages/create-miniapp-cli"
},
"author": {
"name": "Daria Abushenko",
"email": "dabushenko@doma.ai",
"url": "https://github.com/abshnko"
},
"type": "module",
"exports": "./dist/index.js",
"bin": "./dist/index.js",
"files": [
"dist",
"template",
"README.md",
"package.json"
],
"scripts": {
"typecheck": "tsc",
"build": "tsup",
"dev": "tsup --watch",
"clean": "rm -rf dist node_modules",
"start": "node dist/index.js"
},
"dependencies": {
"@clack/prompts": "^0.6.3",
"chalk": "5.4.0",
"commander": "14.0.1",
"execa": "^7.2.0",
"fs-extra": "^11.2.0",
"gradient-string": "^2.0.2",
"ora": "6.3.1",
"yaml": "^2.8.1"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/gradient-string": "^1.1.6",
"@types/node": "^22",
"tsup": "^8.4.0",
"type-fest": "^3.13.1",
"typescript": "^5.8.3",
"zod": "^4.0.8"
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Add missing runtime dependency and double‑check zod placement

Two manifest nits that can cause runtime issues:

  1. sort-package-json is not declared
    addPackageScript.ts imports sort-package-json, but it’s missing from both dependencies and devDependencies here. In a published package or in an isolated install, this will throw Cannot find module 'sort-package-json'.

    Add it under dependencies (it’s used at runtime by the CLI):

   "dependencies": {
     "@clack/prompts": "^0.6.3",
     "chalk": "5.4.0",
     "commander": "14.0.1",
     "execa": "^7.2.0",
     "fs-extra": "^11.2.0",
     "gradient-string": "^2.0.2",
     "ora": "6.3.1",
-    "yaml": "^2.8.1"
+    "yaml": "^2.8.1",
+    "sort-package-json": "^X.Y.Z"
   },

(Pick whatever version you’re targeting for sort-package-json.)

  1. Verify whether zod is runtime or dev‑only
    zod is currently in devDependencies. If it’s imported by CLI runtime code (not just tests or type‑level tooling), it should move to dependencies so consumers don’t hit missing‑module errors when this package is published or used outside the monorepo.

🏁 Script executed:

#!/bin/bash
# Search for imports/usages of sort-package-json and zod in the codebase
echo "=== Searching for sort-package-json imports ==="
rg -n 'sort-package-json|sort_package_json' --type=ts --type=js -B2 -A2

echo ""
echo "=== Searching for zod imports ==="
rg -n 'import.*zod|from.*zod' --type=ts --type=js -B2 -A2

echo ""
echo "=== Checking package.json structure ==="
cat packages/create-miniapp-cli/package.json | grep -A 20 '"dependencies"'

Length of output: 4298


🏁 Script executed:

#!/bin/bash
# Final check: search specifically for zod usage anywhere in create-miniapp-cli package
echo "=== Searching for zod in create-miniapp-cli package only ==="
rg -n 'zod' packages/create-miniapp-cli/ --type=ts --type=js --type=json -B1 -A1

echo ""
echo "=== Checking if zod is referenced in any config or test files ==="
find packages/create-miniapp-cli -type f \( -name '*.ts' -o -name '*.tsx' -o -name '*.js' -o -name '*.json' \) -exec grep -l 'zod' {} \;

Length of output: 935


Add missing runtime dependency sort-package-json and remove unused zod

Two manifest issues found:

  1. sort-package-json is not declared but is used at runtime
    Confirmed in packages/create-miniapp-cli/src/utils/addPackageScript.ts:4 and packages/create-miniapp-cli/src/utils/addPackageDependency.ts:8. This will break the CLI when packaged or run outside the monorepo. Add to dependencies:
   "dependencies": {
     "@clack/prompts": "^0.6.3",
     "chalk": "5.4.0",
     "commander": "14.0.1",
     "execa": "^7.2.0",
     "fs-extra": "^11.2.0",
     "gradient-string": "^2.0.2",
     "ora": "6.3.1",
-    "yaml": "^2.8.1"
+    "yaml": "^2.8.1",
+    "sort-package-json": "^2.8.2"
   },
  1. zod in devDependencies is unused
    Zod appears only in template subdirectories and the manifest itself, never imported by CLI code. Remove it from devDependencies.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"name": "@open-condo/create-miniapp-cli",
"version": "1.0.0",
"description": "Create Condo-compatible miniapp",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/open-condo-software/condo.git",
"directory": "packages/create-miniapp-cli"
},
"author": {
"name": "Daria Abushenko",
"email": "dabushenko@doma.ai",
"url": "https://github.com/abshnko"
},
"type": "module",
"exports": "./dist/index.js",
"bin": "./dist/index.js",
"files": [
"dist",
"template",
"README.md",
"package.json"
],
"scripts": {
"typecheck": "tsc",
"build": "tsup",
"dev": "tsup --watch",
"clean": "rm -rf dist node_modules",
"start": "node dist/index.js"
},
"dependencies": {
"@clack/prompts": "^0.6.3",
"chalk": "5.4.0",
"commander": "14.0.1",
"execa": "^7.2.0",
"fs-extra": "^11.2.0",
"gradient-string": "^2.0.2",
"ora": "6.3.1",
"yaml": "^2.8.1"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/gradient-string": "^1.1.6",
"@types/node": "^22",
"tsup": "^8.4.0",
"type-fest": "^3.13.1",
"typescript": "^5.8.3",
"zod": "^4.0.8"
}
}
{
"name": "@open-condo/create-miniapp-cli",
"version": "1.0.0",
"description": "Create Condo-compatible miniapp",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/open-condo-software/condo.git",
"directory": "packages/create-miniapp-cli"
},
"author": {
"name": "Daria Abushenko",
"email": "dabushenko@doma.ai",
"url": "https://github.com/abshnko"
},
"type": "module",
"exports": "./dist/index.js",
"bin": "./dist/index.js",
"files": [
"dist",
"template",
"README.md",
"package.json"
],
"scripts": {
"typecheck": "tsc",
"build": "tsup",
"dev": "tsup --watch",
"clean": "rm -rf dist node_modules",
"start": "node dist/index.js"
},
"dependencies": {
"@clack/prompts": "^0.6.3",
"chalk": "5.4.0",
"commander": "14.0.1",
"execa": "^7.2.0",
"fs-extra": "^11.2.0",
"gradient-string": "^2.0.2",
"ora": "6.3.1",
"yaml": "^2.8.1",
"sort-package-json": "^2.8.2"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/gradient-string": "^1.1.6",
"@types/node": "^22",
"tsup": "^8.4.0",
"type-fest": "^3.13.1",
"typescript": "^5.8.3",
"zod": "^4.0.8"
}
}
🤖 Prompt for AI Agents
packages/create-miniapp-cli/package.json lines 1-51: the package.json is missing
the runtime dependency "sort-package-json" which is required by runtime files
(src/utils/addPackageScript.ts and src/utils/addPackageDependency.ts), and it
still lists "zod" in devDependencies though unused by the CLI; add
"sort-package-json" to the "dependencies" section (pick a compatible semver,
e.g. ^1.x) and remove "zod" from "devDependencies", then run npm/yarn install
and rebuild to ensure the CLI bundles the new dependency.

Comment on lines +17 to +23
const spinner = ora('Running format command\n').start()

await execa(pkgManager, ['run', 'format:write'], {
cwd: projectDir,
})

spinner.succeed(`${chalk.green('Successfully formatted project')}`)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add error handling for format command failures.

If the execa call fails (e.g., missing dependencies, syntax errors in generated code), the spinner will hang and the error will propagate unhandled. Add try-catch to fail the spinner gracefully and provide a clear error message.

     const spinner = ora('Running format command\n').start()
 
-    await execa(pkgManager, ['run', 'format:write'], {
-        cwd: projectDir,
-    })
-
-    spinner.succeed(`${chalk.green('Successfully formatted project')}`)
+    try {
+        await execa(pkgManager, ['run', 'format:write'], {
+            cwd: projectDir,
+        })
+        spinner.succeed(`${chalk.green('Successfully formatted project')}`)
+    } catch (error) {
+        spinner.fail('Failed to format project')
+        throw error
+    }
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/helpers/format.ts around lines 17 to 23, the
execa call that runs the formatter is not wrapped in error handling, so a failed
format will leave the spinner spinning and the error unhandled; wrap the await
execa(...) call in a try-catch, on success call spinner.succeed as now, and in
the catch call spinner.fail with a clear message that includes the caught error
message (or error.toString()), then rethrow the error (or exit with non-zero) so
the failure surfaces to the caller; ensure the spinner is always stopped by
using the try-catch (no additional finally needed if you handle both branches).

Comment on lines +5 to +14
export const dependencyVersionMap = {
// eslint / prettier
prettier: '^3.5.3',
'@eslint/eslintrc': '^3.3.1',
'prettier-plugin-tailwindcss': '^0.6.11',
eslint: '^9.23.0',
'eslint-config-next': '^15.2.3',
'eslint-plugin-drizzle': '^0.2.3',
'typescript-eslint': '^8.27.0',
} as const
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Verify dependency versions are current and secure.

Since these versions are pinned and my knowledge may be outdated, ensure all dependencies are up-to-date and free from known vulnerabilities.


🌐 Web query:

Are there any known security vulnerabilities in prettier version 3.5.3, eslint version 9.23.0, eslint-config-next version 15.2.3, eslint-plugin-drizzle version 0.2.3, or typescript-eslint version 8.27.0?

💡 Result:

Short answer:

  • Prettier 3.5.3 — Known issue: a ReDoS (regular‑expression DoS) reported as CVE‑2025‑8263 affects Prettier up through 3.6.2, so 3.5.3 is within the affected range. Patch/mitigation info is in the Prettier issue and CVE entries. [1][2][3]
  • ESLint 9.23.0 — I found no public CVE or supply‑chain advisory specifically affecting eslint v9.23.0. (No direct vulnerabilities listed in major vulnerability databases for that release.) [4]
  • eslint-config-next 15.2.3 — No reported vulnerabilities for that package/version in Snyk/GitHub advisory databases. [5]
  • eslint-plugin-drizzle 0.2.3 — No known vulnerabilities reported for that version in Snyk. [6]
  • @typescript-eslint / typescript‑eslint (8.27.0) — No direct vulnerabilities reported for the 8.27.0 line in Snyk/GitHub advisory listings. [7]

Notes / context:

  • There was a separate high‑severity supply‑chain compromise (CVE‑2025‑54313) that injected malicious postinstall scripts into certain eslint‑config‑prettier and eslint‑plugin‑prettier releases (not the packages/versions you listed). If you use any Prettier/ESLint ecosystem packages, verify you are not pulling the specific compromised versions and upgrade to the patched releases. [8][9]

Sources:

  1. NVD / CVE-2025-8263 and Prettier issue tracking (ReDoS affecting Prettier ≤ 3.6.2). [1][2]
  2. Prettier GitHub issue / discussion for the regex/parseNestedCSS problem. [3]
  3. Snyk / package advisories for eslint, eslint-config-next, eslint-plugin-drizzle, @typescript-eslint (no direct vulnerabilities listed). [4][5][6][7]
  4. GitHub Advisory / Snyk / industry coverage for the supply‑chain compromise CVE‑2025‑54313 (eslint-config-prettier / eslint-plugin‑prettier). [8][9]

If you want, I can fetch the exact CVE/NVD and Prettier issue URLs and the recommended patched versions for each affected package.


Upgrade Prettier to address CVE-2025-8263 (ReDoS vulnerability).

Prettier 3.5.3 is affected by CVE-2025-8263, a ReDoS vulnerability affecting Prettier versions up through 3.6.2. Upgrade to a patched version above 3.6.2. The other dependencies (ESLint 9.23.0, eslint-config-next 15.2.3, eslint-plugin-drizzle 0.2.3, and typescript-eslint 8.27.0) have no reported vulnerabilities.

🤖 Prompt for AI Agents
In packages/create-miniapp-cli/src/installers/dependencyVersionMap.ts around
lines 5 to 14, Prettier is pinned to 3.5.3 which is vulnerable (CVE-2025-8263);
update the Prettier entry to a patched version greater than 3.6.2 (for example
'^3.6.3'), update package lock / yarn lock by reinstalling dependencies, and run
the project's lint/tests to verify nothing breaks.

Comment on lines +12 to +201
export const HELM_TEMPLATES = {
app: `\${REVIEW_NE_WRAPPER_START}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-\${APP}-app
labels:
service: {{ .Chart.Name }}-\${APP}-app
spec:
replicas: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.replicas | first | default .Values.\${APP_UNDERSCORE}.app.replicas._default }}
selector:
matchLabels:
service: {{ .Chart.Name }}-\${APP}-app
template:
metadata:
labels:
service: {{ .Chart.Name }}-\${APP}-app
annotations:
checksum/secret: {{ include (print $.Template.BasePath "/271-\${APP}-secret.yaml") . | sha256sum }}
spec:
nodeSelector:
nodepool: application-node-pool
imagePullSecrets:
- name: {{ required ".Values.registry.secret_name" .Values.registry.secret_name }}
securityContext:
fsGroup: 999
runAsUser: 999
containers:
- name: app
image: {{ .Values.werf.image.\${APP_UNDERSCORE} }}
workingDir: /app
resources:
requests:
cpu: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.requests.cpu | first | default .Values.\${APP_UNDERSCORE}.app.requests.cpu._default | quote }}
memory: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.requests.memory | first | default .Values.\${APP_UNDERSCORE}.app.requests.memory._default | quote }}
limits:
memory: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.requests.memory | first | default .Values.\${APP_UNDERSCORE}.app.requests.memory._default | quote }}
command: ['yarn', 'workspace', '@app/\${APP}', 'start']
ports:
- containerPort: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.port | first | default .Values.\${APP_UNDERSCORE}.app.port._default }}
name: app
protocol: TCP
envFrom:
- secretRef:
name: \${APP}-secrets
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-\${APP}
spec:
type: ClusterIP
selector:
service: {{ .Chart.Name }}-\${APP}-app
ports:
- name: {{ .Chart.Name }}-\${APP}-app
port: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.port | first | default .Values.\${APP_UNDERSCORE}.app.port._default }}
protocol: TCP
targetPort: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.port | first | default .Values.\${APP_UNDERSCORE}.app.port._default }}
\${REVIEW_NE_WRAPPER_END}`,

secrets: `\${REVIEW_NE_WRAPPER_START}
---
apiVersion: v1
kind: Secret
metadata:
name: \${APP}-secrets
annotations:
"helm.sh/hook": pre-install, pre-upgrade
"helm.sh/hook-weight": "0"
type: Opaque
data:
{{- include "services_urls" . | indent 2 }}
{{- range $env, $val := .Values.envs.\${APP_UNDERSCORE} }}
{{- if and (eq $.Values.werf.env "review") (eq $env "server_url") }}
{{ $env | upper }}: {{ ( printf "https://%s" $.Values.global.ci_\${APP_UNDERSCORE}_url ) | b64enc }}
{{- else }}
{{ $env | upper }}: {{ pluck $.Values.werf.env $val | first | default $val._default | b64enc }}
{{- end }}
{{- end }}
\${REVIEW_NE_WRAPPER_END}`,

worker: `\${REVIEW_NE_WRAPPER_START}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-\${APP}-worker
labels:
service: {{ .Chart.Name }}-\${APP}-worker
spec:
replicas: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.worker.replicas | first | default .Values.\${APP_UNDERSCORE}.worker.replicas._default }}
selector:
matchLabels:
service: {{ .Chart.Name }}-\${APP}-worker
template:
metadata:
labels:
service: {{ .Chart.Name }}-\${APP}-worker
annotations:
checksum/secret: {{ include (print $.Template.BasePath "/271-\${APP}-secret.yaml") . | sha256sum }}
spec:
nodeSelector:
nodepool: application-node-pool
imagePullSecrets:
- name: {{ required ".Values.registry.secret_name" .Values.registry.secret_name }}
containers:
- name: \${APP}-worker
image: {{ .Values.werf.image.\${APP_UNDERSCORE} }}
workingDir: /app
resources:
requests:
cpu: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.worker.requests.cpu | first | default .Values.\${APP_UNDERSCORE}.worker.requests.cpu._default | quote }}
memory: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.worker.requests.memory | first | default .Values.\${APP_UNDERSCORE}.worker.requests.memory._default | quote }}
limits:
memory: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.worker.requests.memory | first | default .Values.\${APP_UNDERSCORE}.worker.requests.memory._default | quote }}
command: ['yarn', 'workspace', '@app/\${APP}', 'worker']
ports:
- containerPort: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.worker.port | first | default .Values.\${APP_UNDERSCORE}.worker.port._default }}
name: worker
protocol: TCP
envFrom:
- secretRef:
name: \${APP}-secrets
\${REVIEW_NE_WRAPPER_END}`,

migrations: `\${REVIEW_NE_WRAPPER_START}
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ printf "%s-\${APP}-migrations-%s" .Chart.Name (now | date "2006-01-02-15-04-05")}}
labels:
app: {{ printf "%s-\${APP}-migrations" .Chart.Name }}
annotations:
"helm.sh/hook": pre-install, pre-upgrade
"helm.sh/hook-weight": "2"
spec:
ttlSecondsAfterFinished: 100
template:
metadata:
annotations:
checksum/secret: {{ include (print $.Template.BasePath "/271-\${APP}-secret.yaml") . | sha256sum }}
spec:
nodeSelector:
nodepool: application-node-pool
imagePullSecrets:
- name: {{ required ".Values.registry.secret_name" .Values.registry.secret_name }}
restartPolicy: Never
containers:
- name: migrate-condo-\${APP}-app
image: {{ .Values.werf.image.\${APP_UNDERSCORE} }}
command: ['yarn', 'workspace', '@app/\${APP}', 'migrate']
workingDir: /app
envFrom:
- secretRef:
name: \${APP}-secrets
\${REVIEW_NE_WRAPPER_END}`,

ingress: `\${REVIEW_NE_WRAPPER_START}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Chart.Name }}-\${APP}
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "Strict-Transport-Security: max-age=31536000; preload";
more_set_headers "X-Request-Id: $request_id";
spec:
tls:
- hosts:
- {{ .Values.global.ci_\${APP_UNDERSCORE}_url }}
secretName: {{ if hasPrefix "production" .Values.werf.env }}doma-wildcard{{ else }}doma-development-wildcard{{ end }}
rules:
- host: {{ .Values.global.ci_\${APP_UNDERSCORE}_url }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Chart.Name }}-\${APP}
port:
number: {{ pluck .Values.werf.env .Values.\${APP_UNDERSCORE}.app.port | first | default .Values.\${APP_UNDERSCORE}.app.port._default }}
\${REVIEW_NE_WRAPPER_END}`,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Avoid having two divergent sources of truth for Helm templates

This HELM_TEMPLATES object defines app/secrets/worker/migrations/ingress YAML with APP/APP_UNDERSCORE placeholders, while packages/create-miniapp-cli/src/utils/getHelmTemplates.ts now generates very similar templates programmatically.

Maintaining both will be brittle:

  • There are already behavioral deltas (e.g. secrets handling of server_url for review env; ingress TLS secretName logic), so which behavior is authoritative depends on which path is used.
  • Any future tweak to resource shape, labels, or env wiring must be duplicated in two places.

To keep behavior predictable and maintainable, consider:

  • Either:
    • Make getHelmTemplates.ts simply wrap HELM_TEMPLATES (performing placeholder substitution like APP/APP_UNDERSCORE → concrete names), and treat HELM_TEMPLATES as the single YAML source of truth.
  • Or:
    • Move the canonical YAML definitions into getHelmTemplates.ts and deprecate/remove the overlapping entries from HELM_TEMPLATES, leaving this file only for path constants (HELM_DIR, TEMPLATES_DIR, REVIEW_DIR, SERVICES_URLS) and any shared non‑duplicated fragments.

This will reduce the chance of subtle environment‑specific or review‑mode regressions when templates evolve.

Comment on lines +125 to +133
<svg width="121px" height="24px" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo">
<path d="M2.49023 1.99988V22.9999H9.87023C17.1002 22.9999 21.9002 19.5499 21.9002 12.0799C21.9002 5.62988 18.1202 1.99988 9.60023 1.99988H2.49023ZM7.29023 18.4999V6.49988H9.96024C15.0302 6.49988 16.8902 8.53988 16.8902 12.4399C16.8902 16.0999 14.7902 18.4999 10.0202 18.4999H7.29023Z"/>
<path d="M32.0611 7.63988C26.9311 7.66988 23.8111 10.3099 23.8111 15.4699C23.8111 20.7199 27.1711 23.3599 31.7911 23.3599C36.8911 23.3299 40.0411 20.7199 40.0411 15.5299C40.0411 10.2799 36.6811 7.63988 32.0611 7.63988ZM28.4611 15.4399C28.4611 12.7399 29.7211 11.5999 31.9111 11.5999C33.9511 11.6299 35.3911 12.7699 35.3911 15.4699V15.5599C35.3911 18.2599 34.1311 19.3999 31.9411 19.3999C29.8711 19.3999 28.4611 18.2599 28.4611 15.5299V15.4399Z"/>
<path d="M52.0266 22.9999H56.5566V14.3899C56.5266 12.4999 57.5166 11.6599 59.1966 11.6599H59.2566C60.6366 11.6899 61.2366 12.4699 61.2366 14.1199V22.9999H65.7666V12.8299C65.7666 9.46988 63.9366 7.72988 60.9066 7.69988H60.8766C58.4466 7.69988 56.8266 9.16988 56.3166 10.9699C55.7166 8.83988 54.0666 7.72988 51.7266 7.69988H51.6666C49.2666 7.69988 47.6166 9.25988 47.1066 10.9099V7.99988H42.8766V22.9999H47.3766V14.3899C47.2866 12.3799 48.2466 11.6899 49.9566 11.6599C51.4566 11.6599 52.0266 12.4399 52.0266 14.1199V22.9999Z"/>
<path d="M69.8109 14.2099C70.8309 12.7099 72.2409 11.6299 75.4209 11.6299C77.2809 11.6299 77.8509 12.2599 77.8509 13.9399V14.0599H74.6109C71.2209 14.0599 68.5809 15.0199 68.5809 18.5899C68.5809 21.3799 70.2009 23.2399 73.3209 23.2399C75.6609 23.2399 77.3709 22.0399 78.0909 20.2999V22.9999H82.3509V13.0999C82.3509 9.37988 80.3709 7.69988 75.4209 7.69988C72.6009 7.69988 70.8009 8.23988 69.8109 8.98988V14.2099ZM74.8809 19.2799C73.7409 19.2799 73.1109 18.7999 73.1109 17.9899C73.1109 17.0299 73.9209 16.6699 75.3609 16.6699H77.8509V17.5399C77.7009 18.5599 76.5909 19.2799 74.8809 19.2799Z"/>
<path d="M86.753 20.6299C86.753 22.0999 87.383 23.2999 89.453 23.2999C91.613 23.2999 92.243 22.0999 92.243 20.6299C92.243 19.1299 91.613 17.8999 89.453 17.8999C87.383 17.8999 86.753 19.1299 86.753 20.6299Z"/>
<path d="M97.2914 14.2099C98.3114 12.7099 99.7214 11.6299 102.901 11.6299C104.761 11.6299 105.331 12.2599 105.331 13.9399V14.0599H102.091C98.7014 14.0599 96.0614 15.0199 96.0614 18.5899C96.0614 21.3799 97.6814 23.2399 100.801 23.2399C103.141 23.2399 104.851 22.0399 105.571 20.2999V22.9999H109.831V13.0999C109.831 9.37988 107.851 7.69988 102.901 7.69988C100.081 7.69988 98.2814 8.23988 97.2914 8.98988V14.2099ZM102.361 19.2799C101.221 19.2799 100.591 18.7999 100.591 17.9899C100.591 17.0299 101.401 16.6699 102.841 16.6699H105.331V17.5399C105.181 18.5599 104.071 19.2799 102.361 19.2799Z"/>
<path d="M113.213 2.68988C113.213 4.12988 114.203 5.05988 115.733 5.05988C117.203 5.05988 118.193 4.12988 118.193 2.68988C118.193 1.27988 117.203 0.379883 115.733 0.379883C114.203 0.379883 113.213 1.27988 113.213 2.68988ZM113.453 22.9999H117.983V7.99988H113.453V22.9999Z"/>
</svg>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add semantic markup or ARIA labels to the SVG logo for accessibility.

The SVG logo lacks an alt text, title element, or ARIA attributes, making it inaccessible to screen readers. Assistive technology will skip over this element entirely.

Apply this diff to add proper semantic markup:

-    <svg width="121px" height="24px" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo">
+    <svg width="121px" height="24px" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo" role="img" aria-label="Logo">
         <path d="M2.49023 1.99988V22.9999H9.87023C17.1002 22.9999 21.9002 19.5499 21.9002 12.0799C21.9002 5.62988 18.1202 1.99988 9.60023 1.99988H2.49023ZM7.29023 18.4999V6.49988H9.96024C15.0302 6.49988 16.8902 8.53988 16.8902 12.4399C16.8902 16.0999 14.7902 18.4999 10.0202 18.4999H7.29023Z"/>

Alternatively, add a <title> child element inside the SVG for better semantics.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<svg width="121px" height="24px" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo">
<path d="M2.49023 1.99988V22.9999H9.87023C17.1002 22.9999 21.9002 19.5499 21.9002 12.0799C21.9002 5.62988 18.1202 1.99988 9.60023 1.99988H2.49023ZM7.29023 18.4999V6.49988H9.96024C15.0302 6.49988 16.8902 8.53988 16.8902 12.4399C16.8902 16.0999 14.7902 18.4999 10.0202 18.4999H7.29023Z"/>
<path d="M32.0611 7.63988C26.9311 7.66988 23.8111 10.3099 23.8111 15.4699C23.8111 20.7199 27.1711 23.3599 31.7911 23.3599C36.8911 23.3299 40.0411 20.7199 40.0411 15.5299C40.0411 10.2799 36.6811 7.63988 32.0611 7.63988ZM28.4611 15.4399C28.4611 12.7399 29.7211 11.5999 31.9111 11.5999C33.9511 11.6299 35.3911 12.7699 35.3911 15.4699V15.5599C35.3911 18.2599 34.1311 19.3999 31.9411 19.3999C29.8711 19.3999 28.4611 18.2599 28.4611 15.5299V15.4399Z"/>
<path d="M52.0266 22.9999H56.5566V14.3899C56.5266 12.4999 57.5166 11.6599 59.1966 11.6599H59.2566C60.6366 11.6899 61.2366 12.4699 61.2366 14.1199V22.9999H65.7666V12.8299C65.7666 9.46988 63.9366 7.72988 60.9066 7.69988H60.8766C58.4466 7.69988 56.8266 9.16988 56.3166 10.9699C55.7166 8.83988 54.0666 7.72988 51.7266 7.69988H51.6666C49.2666 7.69988 47.6166 9.25988 47.1066 10.9099V7.99988H42.8766V22.9999H47.3766V14.3899C47.2866 12.3799 48.2466 11.6899 49.9566 11.6599C51.4566 11.6599 52.0266 12.4399 52.0266 14.1199V22.9999Z"/>
<path d="M69.8109 14.2099C70.8309 12.7099 72.2409 11.6299 75.4209 11.6299C77.2809 11.6299 77.8509 12.2599 77.8509 13.9399V14.0599H74.6109C71.2209 14.0599 68.5809 15.0199 68.5809 18.5899C68.5809 21.3799 70.2009 23.2399 73.3209 23.2399C75.6609 23.2399 77.3709 22.0399 78.0909 20.2999V22.9999H82.3509V13.0999C82.3509 9.37988 80.3709 7.69988 75.4209 7.69988C72.6009 7.69988 70.8009 8.23988 69.8109 8.98988V14.2099ZM74.8809 19.2799C73.7409 19.2799 73.1109 18.7999 73.1109 17.9899C73.1109 17.0299 73.9209 16.6699 75.3609 16.6699H77.8509V17.5399C77.7009 18.5599 76.5909 19.2799 74.8809 19.2799Z"/>
<path d="M86.753 20.6299C86.753 22.0999 87.383 23.2999 89.453 23.2999C91.613 23.2999 92.243 22.0999 92.243 20.6299C92.243 19.1299 91.613 17.8999 89.453 17.8999C87.383 17.8999 86.753 19.1299 86.753 20.6299Z"/>
<path d="M97.2914 14.2099C98.3114 12.7099 99.7214 11.6299 102.901 11.6299C104.761 11.6299 105.331 12.2599 105.331 13.9399V14.0599H102.091C98.7014 14.0599 96.0614 15.0199 96.0614 18.5899C96.0614 21.3799 97.6814 23.2399 100.801 23.2399C103.141 23.2399 104.851 22.0399 105.571 20.2999V22.9999H109.831V13.0999C109.831 9.37988 107.851 7.69988 102.901 7.69988C100.081 7.69988 98.2814 8.23988 97.2914 8.98988V14.2099ZM102.361 19.2799C101.221 19.2799 100.591 18.7999 100.591 17.9899C100.591 17.0299 101.401 16.6699 102.841 16.6699H105.331V17.5399C105.181 18.5599 104.071 19.2799 102.361 19.2799Z"/>
<path d="M113.213 2.68988C113.213 4.12988 114.203 5.05988 115.733 5.05988C117.203 5.05988 118.193 4.12988 118.193 2.68988C118.193 1.27988 117.203 0.379883 115.733 0.379883C114.203 0.379883 113.213 1.27988 113.213 2.68988ZM113.453 22.9999H117.983V7.99988H113.453V22.9999Z"/>
</svg>
<svg width="121px" height="24px" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo" role="img" aria-label="Logo">
<path d="M2.49023 1.99988V22.9999H9.87023C17.1002 22.9999 21.9002 19.5499 21.9002 12.0799C21.9002 5.62988 18.1202 1.99988 9.60023 1.99988H2.49023ZM7.29023 18.4999V6.49988H9.96024C15.0302 6.49988 16.8902 8.53988 16.8902 12.4399C16.8902 16.0999 14.7902 18.4999 10.0202 18.4999H7.29023Z"/>
<path d="M32.0611 7.63988C26.9311 7.66988 23.8111 10.3099 23.8111 15.4699C23.8111 20.7199 27.1711 23.3599 31.7911 23.3599C36.8911 23.3299 40.0411 20.7199 40.0411 15.5299C40.0411 10.2799 36.6811 7.63988 32.0611 7.63988ZM28.4611 15.4399C28.4611 12.7399 29.7211 11.5999 31.9111 11.5999C33.9511 11.6299 35.3911 12.7699 35.3911 15.4699V15.5599C35.3911 18.2599 34.1311 19.3999 31.9411 19.3999C29.8711 19.3999 28.4611 18.2599 28.4611 15.5299V15.4399Z"/>
<path d="M52.0266 22.9999H56.5566V14.3899C56.5266 12.4999 57.5166 11.6599 59.1966 11.6599H59.2566C60.6366 11.6899 61.2366 12.4699 61.2366 14.1199V22.9999H65.7666V12.8299C65.7666 9.46988 63.9366 7.72988 60.9066 7.69988H60.8766C58.4466 7.69988 56.8266 9.16988 56.3166 10.9699C55.7166 8.83988 54.0666 7.72988 51.7266 7.69988H51.6666C49.2666 7.69988 47.6166 9.25988 47.1066 10.9099V7.99988H42.8766V22.9999H47.3766V14.3899C47.2866 12.3799 48.2466 11.6899 49.9566 11.6599C51.4566 11.6599 52.0266 12.4399 52.0266 14.1199V22.9999Z"/>
<path d="M69.8109 14.2099C70.8309 12.7099 72.2409 11.6299 75.4209 11.6299C77.2809 11.6299 77.8509 12.2599 77.8509 13.9399V14.0599H74.6109C71.2209 14.0599 68.5809 15.0199 68.5809 18.5899C68.5809 21.3799 70.2009 23.2399 73.3209 23.2399C75.6609 23.2399 77.3709 22.0399 78.0909 20.2999V22.9999H82.3509V13.0999C82.3509 9.37988 80.3709 7.69988 75.4209 7.69988C72.6009 7.69988 70.8009 8.23988 69.8109 8.98988V14.2099ZM74.8809 19.2799C73.7409 19.2799 73.1109 18.7999 73.1109 17.9899C73.1109 17.0299 73.9209 16.6699 75.3609 16.6699H77.8509V17.5399C77.7009 18.5599 76.5909 19.2799 74.8809 19.2799Z"/>
<path d="M86.753 20.6299C86.753 22.0999 87.383 23.2999 89.453 23.2999C91.613 23.2999 92.243 22.0999 92.243 20.6299C92.243 19.1299 91.613 17.8999 89.453 17.8999C87.383 17.8999 86.753 19.1299 86.753 20.6299Z"/>
<path d="M97.2914 14.2099C98.3114 12.7099 99.7214 11.6299 102.901 11.6299C104.761 11.6299 105.331 12.2599 105.331 13.9399V14.0599H102.091C98.7014 14.0599 96.0614 15.0199 96.0614 18.5899C96.0614 21.3799 97.6814 23.2399 100.801 23.2399C103.141 23.2399 104.851 22.0399 105.571 20.2999V22.9999H109.831V13.0999C109.831 9.37988 107.851 7.69988 102.901 7.69988C100.081 7.69988 98.2814 8.23988 97.2914 8.98988V14.2099ZM102.361 19.2799C101.221 19.2799 100.591 18.7999 100.591 17.9899C100.591 17.0299 101.401 16.6699 102.841 16.6699H105.331V17.5399C105.181 18.5599 104.071 19.2799 102.361 19.2799Z"/>
<path d="M113.213 2.68988C113.213 4.12988 114.203 5.05988 115.733 5.05988C117.203 5.05988 118.193 4.12988 118.193 2.68988C118.193 1.27988 117.203 0.379883 115.733 0.379883C114.203 0.379883 113.213 1.27988 113.213 2.68988ZM113.453 22.9999H117.983V7.99988H113.453V22.9999Z"/>
</svg>
🤖 Prompt for AI Agents
packages/create-miniapp-cli/template/fullstack/public/500-error.html around
lines 125 to 133: the inline SVG logo is missing accessible semantics; add a
<title> element inside the SVG (with a unique id) and set role="img" plus
aria-labelledby="that-id" (or aria-label="Descriptive text") so screen readers
can announce it; if the logo is purely decorative instead, set
aria-hidden="true" and keep it out of the accessibility tree.

Comment on lines +7 to +10
async function setupBaseConfigs () {
const condoUrl = await getAppEnvValue(APP_NAME, 'CONDO_DOMAIN')
await updateAppEnvFile(APP_NAME, 'CONDO_URL', JSON.stringify(condoUrl))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify CONDO_DOMAIN → CONDO_URL variable name transformation.

Line 8 reads CONDO_DOMAIN but line 9 writes it as CONDO_URL. Confirm this variable name transformation is intentional and that downstream code expects CONDO_URL rather than CONDO_DOMAIN.


🏁 Script executed:

#!/bin/bash
# Check if CONDO_URL is referenced elsewhere in the template
rg -n --type=js --type=ts 'CONDO_URL' packages/create-miniapp-cli/template/

Length of output: 136


I need to verify if this transformation is intentional by checking for CONDO_DOMAIN references and looking for broader context about these environment variables.


---

🏁 Script executed:

```shell
#!/bin/bash
# Search for CONDO_DOMAIN across entire codebase
echo "=== CONDO_DOMAIN references ==="
rg -n 'CONDO_DOMAIN' --type=js --type=ts

echo -e "\n=== CONDO_URL references outside template ==="
rg -n 'CONDO_URL' --type=js --type=ts -g '!packages/create-miniapp-cli/template/**'

echo -e "\n=== Check for env config or documentation files ==="
fd -e md -e json -e txt 'config|env|README' packages/create-miniapp-cli/ | head -20

Length of output: 2243


Fix incorrect environment variable name on line 9: change CONDO_URL to CONDO_DOMAIN.

The variable name transformation is a bug, not intentional. Throughout the codebase, all downstream code (clients/index.js, middlewares/oidc.js, next.config.js) and similar prepare.js scripts consistently read and write CONDO_DOMAIN, never CONDO_URL. Line 9 should write to CONDO_DOMAIN to match what line 8 reads and what the rest of the template expects.

# Current (incorrect):
await updateAppEnvFile(APP_NAME, 'CONDO_URL', JSON.stringify(condoUrl))

# Should be:
await updateAppEnvFile(APP_NAME, 'CONDO_DOMAIN', JSON.stringify(condoUrl))
🤖 Prompt for AI Agents
In packages/create-miniapp-cli/template/server/bin/prepare.js around lines 7 to
10, the code writes the environment variable under the wrong name; change the
call that currently updates CONDO_URL to instead update CONDO_DOMAIN so the
written key matches the value retrieved from CONDO_DOMAIN and the rest of the
codebase; modify the updateAppEnvFile call to use 'CONDO_DOMAIN' (and leave the
JSON.stringify(condoUrl) as-is).

Comment on lines +51 to +60
const createdUser = await User.create(context, {
sender: OIDC_SENDER_FIELD_VALUE,
name: userInfo.name,
dv: userInfo.dv,
isSupport: userInfo.isSupport,
isAdmin: userInfo.isAdmin,
})
// UserCreateInput does not have `id` field, so, update it manually
return await keystone.lists.User.adapter.update(createdUser.id, { id: userInfo.sub })
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add error handling for the adapter.update call.

The direct adapter.update at line 59 sets a custom id from userInfo.sub. If this operation fails (e.g., due to a constraint violation or database error), the newly created user will remain with an auto-generated id instead of the OIDC sub, causing authentication issues. Consider wrapping this in a try-catch and either rolling back the user creation or logging a critical error.

Example approach:

     } else {
         const createdUser = await User.create(context, {
             sender: OIDC_SENDER_FIELD_VALUE,
             name: userInfo.name,
             dv: userInfo.dv,
             isSupport: userInfo.isSupport,
             isAdmin: userInfo.isAdmin,
         })
-        // UserCreateInput does not have `id` field, so, update it manually
-        return await keystone.lists.User.adapter.update(createdUser.id, { id: userInfo.sub })
+        try {
+            // UserCreateInput does not have `id` field, so, update it manually
+            return await keystone.lists.User.adapter.update(createdUser.id, { id: userInfo.sub })
+        } catch (err) {
+            // Clean up the user or log critical error
+            await User.delete(context, createdUser.id)
+            throw new Error(`Failed to set OIDC sub as user id: ${err.message}`)
+        }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const createdUser = await User.create(context, {
sender: OIDC_SENDER_FIELD_VALUE,
name: userInfo.name,
dv: userInfo.dv,
isSupport: userInfo.isSupport,
isAdmin: userInfo.isAdmin,
})
// UserCreateInput does not have `id` field, so, update it manually
return await keystone.lists.User.adapter.update(createdUser.id, { id: userInfo.sub })
}
const createdUser = await User.create(context, {
sender: OIDC_SENDER_FIELD_VALUE,
name: userInfo.name,
dv: userInfo.dv,
isSupport: userInfo.isSupport,
isAdmin: userInfo.isAdmin,
})
try {
// UserCreateInput does not have `id` field, so, update it manually
return await keystone.lists.User.adapter.update(createdUser.id, { id: userInfo.sub })
} catch (err) {
// Clean up the user or log critical error
await User.delete(context, createdUser.id)
throw new Error(`Failed to set OIDC sub as user id: ${err.message}`)
}
}
🤖 Prompt for AI Agents
In
packages/create-miniapp-cli/template/server/domains/user/utils/serverSchema/createOrUpdateUser.js
around lines 51 to 60, the call to keystone.lists.User.adapter.update that sets
the custom id can fail and currently has no error handling; wrap the
adapter.update call in a try-catch, and on failure delete the just-created user
(use keystone.lists.User.adapter.delete with createdUser.id) or otherwise roll
back the creation, log a clear error (including the error object and context:
createdUser.id and userInfo.sub), and rethrow the error so callers know the
operation failed; ensure the function returns the updated record only on
successful update.

Comment on lines +17 to +19
const schemas = () => [
require('~/domains/user/schema'),
]
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Resolve ESLint no-restricted-modules on ~/domains/user/schema.

ESLint is complaining that '~/domains/user/schema' is restricted by no-restricted-modules; as-is, this will cause lint failures for the template. Either:

  • Change this to an allowed schema import path that fits your boundaries rules, or
  • Explicitly exempt this template file from that rule (e.g., via an override in the ESLint config or a targeted // eslint-disable-next-line no-restricted-modules if you’re comfortable with that here).
🧰 Tools
🪛 ESLint

[error] 18-18: '~/domains/user/schema' module is restricted from being used by a pattern.

(no-restricted-modules)

🤖 Prompt for AI Agents
In packages/create-miniapp-cli/template/server/index.js around lines 17 to 19,
ESLint flags require('~/domains/user/schema') as a restricted module; fix by
replacing the tilde import with an allowed path (preferably a relative path like
'./domains/user/schema' or the correct package-scoped path used in this
template) so it conforms to your boundaries, or if that’s not possible, add a
targeted exemption (either a single-line comment // eslint-disable-next-line
no-restricted-modules immediately above the require or an ESLint override for
this template file) to silence the rule.

@vovaaxeapolla vovaaxeapolla force-pushed the feat/packages/add-condo-miniapp-cli branch from 2645500 to 8dfff98 Compare November 20, 2025 11:47
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@abshnko abshnko force-pushed the feat/packages/add-condo-miniapp-cli branch from 8dfff98 to 685b875 Compare December 22, 2025 11:26
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants