Enhance project setup: add vitest for testing, update .gitignore to include .pnpm-store, and improve README with authentication options and usage instructions. Introduce new files for ESLint and PostCSS configurations, and create a template for a C1 App with Next.js.#7
Conversation
…nclude .pnpm-store, and improve README with authentication options and usage instructions. Introduce new files for ESLint and PostCSS configurations, and create a template for a C1 App with Next.js.
There was a problem hiding this comment.
Walkthrough
This PR introduces a comprehensive authentication refactoring and adds a new Next.js C1 chat application template. The main changes include: (1) replacing the --skip-auth flag with a more flexible --auth flag supporting OAuth, manual, and skip modes with proper deprecation handling, (2) migrating the testing framework from Jest to Vitest with full test coverage for authentication logic, (3) adding a complete Next.js 15 application template with AI-powered chat functionality using Thesys C1 and Crayon AI packages, (4) implementing GitHub Actions workflow for automated build verification, and (5) adding pnpm package manager support. The changes improve authentication flexibility, modernize testing infrastructure, and provide a production-ready template for developers.
Changes
| File(s) | Summary |
|---|---|
.gitignore |
Added .pnpm-store/ to exclude pnpm's local package store directory from version control. |
README.md |
Refactored CLI options table formatting, added new --auth flag with oauth/manual/skip options, deprecated --skip-auth, removed 'Non-Interactive / CI / Agent Usage' section, and expanded Authentication Options documentation with clearer workflow explanations. |
src/index.ts |
Introduced AuthMethod and AuthDecision types, added helper functions (resolveAuthMethod, resolveAuthDecision, shouldPromptForAuthMethod) for authentication flow logic, implemented new --auth CLI option, added placeholder API key constant for skip mode, and converted code formatting from double to single quotes. |
src/types/index.ts |
Added optional auth property to CLIOptions interface with union type 'oauth' | 'manual' | 'skip' for explicit authentication method specification. |
tests/auth-options.test.ts |
Added comprehensive test coverage for authentication functions including auth method resolution, deprecation warnings, decision logic, and prompt conditions across interactive and non-interactive modes. |
package.json |
|
pnpm-lock.yaml |
Migrated testing framework from Jest to Vitest, removed Jest dependencies, added Vitest v4.0.18 with associated dependencies (chai, tinybench, Vite v7.3.1, esbuild, rollup), and added test/test:watch npm scripts. |
vitest.config.ts |
Added Vitest configuration file specifying Node.js test environment and test file pattern tests/**/*.test.ts. |
my-c1-app/.github/workflows/build-check.yml |
Added GitHub Actions workflow for automated build verification across Node.js versions 20.9.0, 22, and 24 with dependency installation, build, and linting steps. |
my-c1-app/.gitignore |
Added comprehensive Next.js .gitignore file with exclusion rules for dependencies, build artifacts, environment files, debug logs, and platform-specific files. |
my-c1-app/README.md |
Added documentation for C1 App Template including setup instructions, API key configuration, development server commands, system prompt customization guidance, and Vercel deployment button. |
my-c1-app/eslint.config.mjs |
Added ESLint flat config extending 'next/core-web-vitals' and 'next/typescript' with FlatCompat for backward compatibility and ES module polyfills. |
my-c1-app/favicon.ico |
Added favicon.ico binary asset file for browser tab branding. |
my-c1-app/next.config.ts |
Added Next.js TypeScript configuration file with empty NextConfig object as foundational setup. |
my-c1-app/package.json |
|
my-c1-app/package-lock.json |
|
my-c1-app/pnpm-lock.yaml |
Created package configuration and lockfiles for Next.js 15.2.8 application with dependencies including @crayonai packages, @thesysai/genui-sdk, React 19, OpenAI SDK, Tailwind CSS v4, TypeScript 5, and ESLint 9, enforcing Node.js >=20.9.0. |
my-c1-app/postcss.config.mjs |
Added PostCSS configuration with Tailwind CSS plugin for CSS processing pipeline. |
my-c1-app/src/app/api/chat/messageStore.ts |
Implemented in-memory message store module with DBMessage type and getMessageStore() function providing thread-specific message management with add, list, and OpenAI-compatible retrieval capabilities. |
my-c1-app/src/app/api/chat/route.ts |
Added Next.js API route handler for chat functionality integrating OpenAI API through Thesys endpoint with streaming responses using Server-Sent Events and @crayonai/stream library. |
my-c1-app/src/app/globals.css |
Added global CSS with Tailwind integration, custom CSS variables for light/dark mode theming, and Geist font family configuration. |
my-c1-app/src/app/layout.tsx |
Added root layout component with Inter font configuration, metadata ('C1 Chat' title), and base HTML structure wrapper. |
my-c1-app/src/app/page.tsx |
Created client-side chat interface page component rendering C1Chat with dark theme and '/api/chat' endpoint configuration. |
🔗 Cross-Repository Impact Analysis
Enable automatic detection of breaking changes across your dependent repositories. → Set up now
Learn more about Cross-Repository Analysis
What It Does
- Automatically identifies repositories that depend on this code
- Analyzes potential breaking changes across your entire codebase
- Provides risk assessment before merging to prevent cross-repo issues
How to Enable
- Visit Settings → Code Management
- Configure repository dependencies
- Future PRs will automatically include cross-repo impact analysis!
Benefits
- 🛡️ Prevent breaking changes across repositories
- 🔍 Catch integration issues before they reach production
- 📊 Better visibility into your multi-repo architecture
Install the extension
Note for Windsurf
Please change the default marketplace provider to the following in the windsurf settings:Marketplace Extension Gallery Service URL: https://marketplace.visualstudio.com/_apis/public/gallery
Marketplace Gallery Item URL: https://marketplace.visualstudio.com/items
Entelligence.ai can learn from your feedback. Simply add 👍 / 👎 emojis to teach it your preferences. More shortcuts below
Emoji Descriptions:
⚠️ Potential Issue - May require further investigation.- 🔒 Security Vulnerability - Fix to ensure system safety.
- 💻 Code Improvement - Suggestions to enhance code quality.
- 🔨 Refactor Suggestion - Recommendations for restructuring code.
- ℹ️ Others - General comments and information.
Interact with the Bot:
- Send a message or request using the format:
@entelligenceai + *your message*
Example: @entelligenceai Can you suggest improvements for this code?
- Help the Bot learn by providing feedback on its responses.
@entelligenceai + *feedback*
Example: @entelligenceai Do not comment on `save_auth` function !
Also you can trigger various commands with the bot by doing
@entelligenceai command
The current supported commands are
config- shows the current configretrigger_review- retriggers the review
More commands to be added soon.
| const fetchOptions: RequestInit = { | ||
| method: data ? 'POST' : 'GET', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Accept': 'application/json', | ||
| ...headers | ||
| } | ||
| } | ||
|
|
||
| try { | ||
| const response = await fetch(url, fetchOptions); | ||
| const body = await response.text(); | ||
| if (data) { | ||
| fetchOptions.body = data | ||
| } | ||
|
|
||
| return { | ||
| statusCode: response.status, | ||
| body, | ||
| }; | ||
| } catch (error) { | ||
| throw new Error( | ||
| `HTTP request failed: ${error instanceof Error ? error.message : "Unknown error"}`, | ||
| ); | ||
| } | ||
| try { | ||
| const response = await fetch(url, fetchOptions) | ||
| const body = await response.text() | ||
|
|
||
| return { | ||
| statusCode: response.status, | ||
| body | ||
| } | ||
| } catch (error) { | ||
| throw new Error(`HTTP request failed: ${error instanceof Error ? error.message : 'Unknown error'}`) | ||
| } | ||
| } | ||
|
|
||
| // Check Node.js version before doing anything else |
There was a problem hiding this comment.
Correctness: In gatherProjectConfig, the removal of the this.nonInteractive check introduces a regression. In non-interactive environments (e.g., CI, non-TTY shells), calling input() or select() will cause the process to crash because @inquirer/prompts requires a TTY. Restore the logic to provide default values (e.g., projectName = 'my-c1-app' and template = 'template-c1-next') when this.nonInteractive is true.
Affected Locations:
- src/index.ts:26-52
- src/index.ts:75-90
| | { type: 'skip', apiKey: string } | ||
| | { type: 'manual' } | ||
| | { type: 'oauth' } | ||
| | { type: 'error', message: string } | ||
|
|
||
| // Step 2: Create project | ||
| await this.createProject(); | ||
| export function resolveAuthMethod(options: CLIOptions, authWasExplicitlyProvided = false): AuthMethod { | ||
| if (options.skipAuth === true) { | ||
| if (authWasExplicitlyProvided && options.auth !== undefined) { | ||
| logger.warning('Both --auth and deprecated --skip-auth were provided. Using --auth.') | ||
| return options.auth | ||
| } | ||
|
|
||
| // Step 3: Setup environment with dotenv | ||
| await this.setupEnvironment(authResult.apiKey); | ||
| logger.warning('The --skip-auth flag is deprecated. Use --auth skip instead.') | ||
| return 'skip' | ||
| } | ||
|
|
||
| // Track successful completion | ||
| await telemetry.track("completed_create_c1_app", { | ||
| template: this.config.template, | ||
| }); | ||
| if (options.auth !== undefined) { | ||
| return options.auth | ||
| } | ||
|
|
||
| // Success message | ||
| this.showSuccessMessage(); | ||
| return 'oauth' | ||
| } | ||
|
|
||
| // Flush and shutdown telemetry before exit | ||
| await telemetry.flush(); | ||
| await telemetry.shutdown(); | ||
| this.spinner.stop(); | ||
| } catch (error) { | ||
| // Track error | ||
| await telemetry.track("failed_create_c1_app"); | ||
| export function resolveAuthDecision( | ||
| options: CLIOptions, |
There was a problem hiding this comment.
Duplicate Code:
This function Authentication decision logic (resolveAuthDecision) duplicates existing code.
📍 Original Location:
src/index.ts:158-196
Function: Authentication decision logic (inline in main method)
💡 Recommendation:
The PR functions should REPLACE the inline authentication logic in the existing codebase. This is not a duplicate to consolidate, but rather a refactoring improvement. The new approach:
- Is more testable (as evidenced by /home/user/pr_files/tests/auth-options.test.ts)
- Separates concerns (decision logic vs execution)
- Adds new functionality (--auth flag with oauth/manual/skip options)
- Maintains backward compatibility (--skip-auth still works with deprecation warning)
Recommendation: Accept the PR changes as they improve code quality while maintaining the same business logic.
Consider importing and reusing the existing function instead of duplicating the logic.
| return { | ||
| statusCode: response.status, | ||
| body | ||
| } | ||
| } catch (error) { | ||
| throw new Error(`HTTP request failed: ${error instanceof Error ? error.message : 'Unknown error'}`) | ||
| } | ||
| } | ||
|
|
||
| // Check Node.js version before doing anything else | ||
| function checkNodeVersion(): void { | ||
| const nodeVersion = process.version; | ||
| const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0]); | ||
| const minorVersion = parseInt(nodeVersion.slice(1).split(".")[1]); | ||
|
|
||
| // Check if version is greater than 20.19.0 | ||
| const isVersionSupported = | ||
| majorVersion > 20 || (majorVersion === 20 && minorVersion >= 9); | ||
|
|
||
| if (!isVersionSupported) { | ||
| console.error(`❌ Node.js version ${nodeVersion} is not supported.`); | ||
| console.error(`📋 This package requires Node.js version >= 20.9.0`); | ||
| console.error(`🔄 Please upgrade your Node.js version and try again.`); | ||
| console.error(``); | ||
| console.error( | ||
| `💡 You can download the latest Node.js from: https://nodejs.org/`, | ||
| ); | ||
| process.exit(1); | ||
| } | ||
| const nodeVersion = process.version | ||
| const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]) | ||
| const minorVersion = parseInt(nodeVersion.slice(1).split('.')[1]) | ||
|
|
||
| // Check if version is greater than 20.19.0 | ||
| const isVersionSupported = |
There was a problem hiding this comment.
Duplicate Code:
This function checkNodeVersion (inlined in makeHttpRequest) duplicates existing code.
📍 Original Location:
src/index.ts:62-81
Function: checkNodeVersion
💡 Recommendation:
REJECT this change in the PR. This appears to be a critical bug:
- The checkNodeVersion logic should remain a standalone function as in the existing codebase
- The PR version has this code unreachably placed after a return statement in makeHttpRequest
- This would cause Node version checking to fail silently
Action needed:
- Keep the existing checkNodeVersion function (lines 62-81 in repo)
- Remove the incorrectly placed duplicate from makeHttpRequest in the PR
- Ensure checkNodeVersion() is called at the start of main() as in the existing code
Consider importing and reusing the existing function instead of duplicating the logic.
… improved testing coverage.
| await telemetry.track("skipped_authentication"); | ||
| break; | ||
| case "manual": { | ||
| const apiKey = await this.promptForApiKey(); |
There was a problem hiding this comment.
Duplicate Code:
This function promptForApiKey duplicates existing code.
📍 Original Location:
src/index.ts:301-345
Function: promptForApiKey
💡 Recommendation:
Complete duplicate - the PR should use the existing promptForApiKey method rather than redefining it. This is a private class method that already exists in the CreateC1App class with identical functionality.
Consider importing and reusing the existing function instead of duplicating the logic.
| await telemetry.track("oauth_authentication"); | ||
| case "oauth": | ||
| try { | ||
| authResult = await this.authenticateAndGenerateAPIKey(); |
There was a problem hiding this comment.
Duplicate Code:
This function authenticateAndGenerateAPIKey duplicates existing code.
📍 Original Location:
src/index.ts:347-438
Function: authenticateAndGenerateAPIKey
💡 Recommendation:
Complete duplicate - the PR should use the existing authenticateAndGenerateAPIKey method. This private method already implements the exact same OAuth flow and API key generation. No redefinition needed.
Consider importing and reusing the existing function instead of duplicating the logic.
| this.showWelcome(); | ||
|
|
||
| // Handle authentication flow | ||
| let authResult: AuthenticationResult; | ||
| let authWasExplicitlyProvided = hideBin(process.argv).some( | ||
| (arg) => arg === "--auth" || arg.startsWith("--auth="), | ||
| ); | ||
| let resolvedOptions: CLIOptions = options; | ||
|
|
||
| if ( | ||
| options.apiKey !== undefined && | ||
| options.apiKey !== null && | ||
| options.apiKey.trim().length > 0 | ||
| shouldPromptForAuthMethod( | ||
| options, | ||
| this.nonInteractive, | ||
| authWasExplicitlyProvided, | ||
| ) | ||
| ) { | ||
| // Use provided API key | ||
| const apiKey = options.apiKey.trim(); | ||
| logger.info(`🔑 Using provided API key: ${apiKey.substring(0, 8)}...`); | ||
| authResult = { apiKey }; | ||
| await telemetry.track("provided_api_key"); | ||
| } else if (this.nonInteractive) { | ||
| // In non-interactive mode, we cannot open a browser or prompt for input | ||
| throw new Error( | ||
| "An API key is required in non-interactive mode. " + | ||
| "Provide one with --api-key <key>.\n" + | ||
| " Example: npx create-c1-app my-project --template template-c1-next --api-key <your-key>\n" + | ||
| " Get a key at: https://console.thesys.dev/keys", | ||
| ); | ||
| } else { | ||
| // Perform OAuth authentication flow | ||
| try { | ||
| authResult = await this.authenticateAndGenerateAPIKey(); | ||
| } catch (error) { | ||
| console.log(error); | ||
| const errorMessage = | ||
| error instanceof Error ? error.message : "Unknown error"; | ||
| logger.error(`Authentication failed: ${errorMessage}`); | ||
| logger.newLine(); | ||
|
|
||
| // Fallback to manual API key input | ||
| logger.info("💡 Falling back to manual API key input..."); | ||
| const apiKey = await this.promptForApiKey(); | ||
| const { select } = await import("@inquirer/prompts"); | ||
| const selectedAuth = (await select({ | ||
| message: "How would you like to authenticate?", | ||
| choices: [ | ||
| { name: "OAuth (recommended)", value: "oauth" }, | ||
| { name: "Manual API key entry", value: "manual" }, | ||
| { name: "Skip authentication", value: "skip" }, | ||
| ], | ||
| default: "oauth", | ||
| })) as AuthMethod; | ||
|
|
||
| resolvedOptions = { | ||
| ...options, | ||
| auth: selectedAuth, | ||
| }; | ||
| authWasExplicitlyProvided = true; | ||
| } | ||
|
|
||
| const authDecision = resolveAuthDecision( | ||
| resolvedOptions, | ||
| this.nonInteractive, | ||
| authWasExplicitlyProvided, | ||
| ); | ||
| let authResult: AuthenticationResult; | ||
|
|
||
| switch (authDecision.type) { | ||
| case "provided-api-key": | ||
| logger.info( | ||
| `🔑 Using provided API key: ${authDecision.apiKey.substring(0, 8)}...`, | ||
| ); | ||
| authResult = { apiKey: authDecision.apiKey }; | ||
| await telemetry.track("provided_api_key"); | ||
| break; | ||
| case "skip": | ||
| logger.info( | ||
| "⏩ Skipping authentication and key generation as requested", | ||
| ); | ||
| logger.info( | ||
| "📝 A placeholder API key will be written to your .env file. Replace it before running your app.", | ||
| ); | ||
| authResult = { apiKey: authDecision.apiKey }; | ||
| await telemetry.track("skipped_authentication"); | ||
| break; | ||
| case "manual": { | ||
| const apiKey = await this.promptForApiKey(); | ||
| authResult = { apiKey }; | ||
| await telemetry.track("manual_api_key_entry"); | ||
| break; | ||
| } | ||
| await telemetry.track("oauth_authentication"); | ||
| case "oauth": | ||
| try { |
There was a problem hiding this comment.
Duplicate Code:
This function Authentication flow in main() method duplicates existing code.
📍 Original Location:
src/index.ts:158-196
Function: Authentication flow in main() method
💡 Recommendation:
This represents semantic duplication with enhancement. The PR refactors the inline authentication logic from the existing repo into separate, reusable functions (resolveAuthDecision, shouldPromptForAuthMethod, resolveAuthMethod) and adds new authentication options. The existing inline logic in repo/src/index.ts:158-196 should be replaced with calls to the new helper functions from pr_files/src/auth/resolve.ts to: (1) Eliminate duplication, (2) Improve maintainability and testability, (3) Support the new 'skip' and 'manual' auth modes, (4) Centralize auth decision logic. The switch statement approach is more extensible than the if-else chain.
Consider importing and reusing the existing function instead of duplicating the logic.
| } from "./auth/resolve.js"; | ||
|
|
||
| // Load package.json for version info (ESM workaround) | ||
| const require = createRequire(import.meta.url); | ||
| const packageJson = require("../package.json"); | ||
| const pkgRequire = createRequire(import.meta.url); | ||
| const packageJson = pkgRequire("../package.json"); | ||
|
|
||
| const THESYS_API_URL = "https://api.app.thesys.dev"; |
There was a problem hiding this comment.
Duplicate Code:
This function validate callback (inline arrow function) duplicates existing code.
📍 Original Location:
src/index.ts:466-472
Function: validate callback in gatherProjectConfig input()
💡 Recommendation:
Extract this validation logic into a reusable named function, either: 1) As a static method in the Validator module: "static getInputValidator(): (input: string) => string | boolean" which returns the callback function, or 2) As a module-level constant in index.ts: "const PROJECT_NAME_INPUT_VALIDATOR = (input: string) => { ... }", or 3) As a class method in CreateC1App: "private getProjectNameValidator()". This function can then be referenced by name in all input() prompts that need project name validation, ensuring consistency and reducing duplication.
Consider importing and reusing the existing function instead of duplicating the logic.
| const packageJson = pkgRequire("../package.json"); | ||
|
|
||
| const THESYS_API_URL = "https://api.app.thesys.dev"; | ||
| const THESYS_ISSUER_URL = "https://api.app.thesys.dev/oidc"; |
There was a problem hiding this comment.
Duplicate Code:
This function transformer callback (inline arrow function) duplicates existing code.
📍 Original Location:
src/index.ts:473
Function: transformer callback in gatherProjectConfig input()
💡 Recommendation:
Since this is a direct pass-through to Validator.sanitizeProjectName, consider: 1) If the function signatures match exactly, pass Validator.sanitizeProjectName directly as the transformer value without wrapping it, or 2) Create a named constant at the module level: "const PROJECT_NAME_TRANSFORMER = (input: string) => Validator.sanitizeProjectName(input)" and reference it in all input() prompts, or 3) Add a static helper to the Validator module: "static getProjectNameTransformer()" that returns this function. This eliminates the duplication of the wrapper function.
Consider importing and reusing the existing function instead of duplicating the logic.
| import SpinnerManager from "./utils/spinner.js"; | ||
| import * as Validator from "./utils/validation.js"; | ||
| import { | ||
| type AuthMethod, | ||
| type CLIOptions, | ||
| type CreateC1AppConfig, | ||
| type AuthenticationResult, |
There was a problem hiding this comment.
Duplicate Code:
This function epilogue help text duplicates existing code.
📍 Original Location:
src/index.ts:286-294
Function: epilogue in parseArguments()
💡 Recommendation:
Extract the epilogue text into a module-level constant: "const API_KEY_HELP_EPILOGUE = ..." at the top of the file. Then reference this constant in the .epilogue() call: ".epilogue(API_KEY_HELP_EPILOGUE)". This ensures: 1) The help text is defined once and can be easily updated, 2) Consistency across all locations where this help text is needed, 3) Better maintainability as any changes to URLs, steps, or examples only need to be made in one place. If the epilogue should only appear once in the yargs configuration, investigate why the PR is adding it again.
Consider importing and reusing the existing function instead of duplicating the logic.
EntelligenceAI PR Summary
This PR refactors authentication to support multiple methods via a new
--authflag and adds a complete Next.js C1 chat application template with modern tooling.--authflag with oauth/manual/skip options, deprecating--skip-authwith backward compatibilityresolveAuthMethod,resolveAuthDecision,shouldPromptForAuthMethodinsrc/index.tstests/auth-options.test.tsmy-c1-app/with Thesys C1 integration, React 19, TypeScript 5, and Tailwind CSS v4my-c1-app/src/app/api/chat/.pnpm-store/in.gitignoreand pnpm lockfileCLIOptionsinterface with optionalauthproperty insrc/types/index.ts