Skip to content

Conversation

@TabishB
Copy link
Contributor

@TabishB TabishB commented Jan 20, 2026

Summary

  • Replace interactive config prompts with automatic config creation using default schema, with helpful comments in the generated config
  • Remove unused prompt functions from config-prompts.ts (168 lines deleted)
  • Add forceExit: true to vitest config to prevent worker processes from hanging

Test plan

  • Run openspec setup and verify config is created with default schema and comments
  • Run pnpm test and verify tests don't hang after completion

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Introduced schema aliasing support, allowing alternative schema names to resolve to the same configuration schema.
  • Refactor

    • Configuration creation is now automatic with defaults, streamlining the setup process.
  • Bug Fixes

    • Enhanced process cleanup mechanism to prevent process hangs on exit.

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

- Replace interactive config prompts with automatic config creation using
  default schema. The generated config includes helpful comments explaining
  context and rules options.
- Remove unused promptForConfig, promptForArtifactRules, and isExitPromptError
  functions from config-prompts.ts
- Add forceExit: true to vitest config to prevent worker processes from hanging
  after tests complete
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

Removes interactive config creation prompts from the artifact workflow and replaces them with automatic default configuration generation. Simplifies serializeConfig to build YAML strings directly rather than serializing objects. Introduces schema aliasing support documentation and adds process cleanup to test teardown.

Changes

Cohort / File(s) Summary
Config Prompt Removal
src/core/config-prompts.ts, src/commands/artifact-workflow.ts
Removes interactive prompt flow including promptForConfig, promptForArtifactRules, ConfigPromptResult, and isExitPromptError. Replaces with automatic config creation using serializeConfig({ schema: DEFAULT_SCHEMA }). Reworks serializeConfig implementation from YAML object serialization to direct string assembly with hard-coded sections.
Schema Aliasing
openspec/changes/schema-alias-support/proposal.md, openspec/changes/schema-alias-support/.openspec.yaml
Introduces schema aliasing to allow spec-driven and openspec-default to resolve to the same schema. Documents alias resolution approach and adds metadata file with spec-driven schema indicator.
Test Cleanup
vitest.setup.ts
Adds concrete process termination mechanism in teardown: delayed unref'ed timer calling process.exit(0) to forcibly close worker if child processes or open handles prevent clean exit.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

A rabbit hops with glee, no prompts to typecast—
Default schemas bloom, no more questions asked! 🐰✨
Config writes itself, with alias embrace,
Clean teardowns ensue, a tidy codebase.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: it mentions simplifying config creation and fixing test hanging, which directly correspond to the removal of interactive prompts and the teardown cleanup mechanism added.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

@greptile-apps
Copy link

greptile-apps bot commented Jan 20, 2026

Greptile Summary

Simplified config creation by removing interactive prompts and automatically generating config files with helpful comments. Also fixed test hanging issue by adding forceExit: true to vitest config.

Key Changes:

  • Removed 168 lines of interactive prompt logic from config-prompts.ts (promptForConfig, isExitPromptError, etc.)
  • Config creation now automatically generates openspec/config.yaml with default schema and helpful inline comments showing users how to customize it
  • Users can edit the generated file directly instead of answering prompts during setup
  • Added forceExit: true to vitest config to prevent worker processes from hanging after test completion

Benefits:

  • Faster, non-blocking setup experience
  • Users get a working config immediately with clear documentation via comments
  • Removes complexity from the setup flow
  • Fixes test hanging issue that required manual cleanup

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The changes are well-scoped refactoring that simplifies the setup flow. The removal of interactive prompts is replaced with clear commented templates. The vitest config change is a standard solution for preventing worker hanging. No logic bugs or security issues found.
  • No files require special attention

Important Files Changed

Filename Overview
vitest.config.ts Added forceExit: true to prevent worker processes from hanging after tests complete
src/core/config-prompts.ts Removed 168 lines of interactive prompt code, simplified serializeConfig to generate YAML with helpful comments instead
src/commands/artifact-workflow.ts Simplified config creation by removing interactive prompts and automatically creating config with default schema and helpful comments

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as openspec setup
    participant FileSystem
    participant ConfigPrompts as config-prompts.ts

    User->>CLI: Run `openspec setup`
    CLI->>CLI: Check if config exists
    
    alt Config already exists
        CLI->>User: Skip creation (already exists)
    else Non-interactive mode
        CLI->>User: Skip creation (non-interactive)
    else Interactive mode (new behavior)
        CLI->>ConfigPrompts: serializeConfig({schema: DEFAULT_SCHEMA})
        ConfigPrompts->>ConfigPrompts: Build YAML with comments
        ConfigPrompts-->>CLI: Return YAML string
        CLI->>FileSystem: Write config.yaml
        FileSystem-->>CLI: Success/Error
        CLI->>User: Display success message
    end
Loading

@vibe-kanban-cloud
Copy link

Review Complete

Your review story is ready!

View Story

Comment !reviewfast on this PR to re-generate the story.

Proposal to add schema alias support so `openspec-default` and `spec-driven`
can be used interchangeably, enabling a rename without breaking existing configs.
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: 2

🤖 Fix all issues with AI agents
In `@src/core/config-prompts.ts`:
- Around line 9-38: serializeConfig currently prints only comments and blindly
emits "schema: undefined" when config.schema is missing and drops
config.context/config.rules; update serializeConfig to validate that
config.schema is present (throw or return an error) and when config.context or
config.rules exist, render them into the output instead of discarding them:
render context as a YAML block scalar (e.g., "context: |" followed by each line
of config.context indented) and render rules as a proper YAML mapping/sequence
(preserve nested artifact keys and arrays) while keeping the comment sections
when those fields are absent; ensure the final output still ends with a newline.

In `@vitest.config.ts`:
- Around line 45-47: Remove the invalid Vitest config property forceExit from
the vitest.config.ts export (it’s a Jest CLI flag, not a Vitest option) and keep
teardownTimeout as-is; to handle hanging workers instead, add or wire up a
Vitest global teardown/globalSetup hook (e.g., implement a globalTeardown
function referenced by the config) or call project.exit(force) programmatically
in your teardown code so worker processes are explicitly terminated and
resources are cleaned up.

Comment on lines 9 to +38
export function serializeConfig(config: Partial<ProjectConfig>): string {
// Build clean config object (only include defined fields)
const cleanConfig: Record<string, unknown> = {
schema: config.schema,
};

if (config.context) {
cleanConfig.context = config.context;
}

if (config.rules && Object.keys(config.rules).length > 0) {
cleanConfig.rules = config.rules;
}

// Serialize to YAML with proper formatting
return stringifyYaml(cleanConfig, {
indent: 2,
lineWidth: 0, // Don't wrap long lines
defaultStringType: 'PLAIN',
defaultKeyType: 'PLAIN',
});
const lines: string[] = [];

// Schema (required)
lines.push(`schema: ${config.schema}`);
lines.push('');

// Context section with comments
lines.push('# Project context (optional)');
lines.push('# This is shown to AI when creating artifacts.');
lines.push('# Add your tech stack, conventions, style guides, domain knowledge, etc.');
lines.push('# Example:');
lines.push('# context: |');
lines.push('# Tech stack: TypeScript, React, Node.js');
lines.push('# We use conventional commits');
lines.push('# Domain: e-commerce platform');
lines.push('');

// Rules section with comments
lines.push('# Per-artifact rules (optional)');
lines.push('# Add custom rules for specific artifacts.');
lines.push('# Example:');
lines.push('# rules:');
lines.push('# proposal:');
lines.push('# - Keep proposals under 500 words');
lines.push('# - Always include a "Non-goals" section');
lines.push('# tasks:');
lines.push('# - Break tasks into chunks of max 2 hours');

return lines.join('\n') + '\n';
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

serializeConfig drops provided context/rules and can emit invalid schema.

This function accepts Partial<ProjectConfig> but only prints comments plus schema. If schema is missing, it writes schema: undefined, and any supplied context/rules are silently discarded. Either render actual values when present or narrow the API to a schema-only template and update the docs.

🛠️ One possible fix (validate schema, render context/rules when provided)
 export function serializeConfig(config: Partial<ProjectConfig>): string {
+  if (!config.schema) {
+    throw new Error('serializeConfig requires config.schema');
+  }
   const lines: string[] = [];

   // Schema (required)
   lines.push(`schema: ${config.schema}`);
   lines.push('');

-  // Context section with comments
-  lines.push('# Project context (optional)');
-  lines.push('# This is shown to AI when creating artifacts.');
-  lines.push('# Add your tech stack, conventions, style guides, domain knowledge, etc.');
-  lines.push('# Example:');
-  lines.push('#   context: |');
-  lines.push('#     Tech stack: TypeScript, React, Node.js');
-  lines.push('#     We use conventional commits');
-  lines.push('#     Domain: e-commerce platform');
-  lines.push('');
+  if (config.context) {
+    lines.push('context: |');
+    for (const line of config.context.split('\n')) {
+      lines.push(`  ${line}`);
+    }
+    lines.push('');
+  } else {
+    // Context section with comments
+    lines.push('# Project context (optional)');
+    lines.push('# This is shown to AI when creating artifacts.');
+    lines.push('# Add your tech stack, conventions, style guides, domain knowledge, etc.');
+    lines.push('# Example:');
+    lines.push('#   context: |');
+    lines.push('#     Tech stack: TypeScript, React, Node.js');
+    lines.push('#     We use conventional commits');
+    lines.push('#     Domain: e-commerce platform');
+    lines.push('');
+  }

-  // Rules section with comments
-  lines.push('# Per-artifact rules (optional)');
-  lines.push('# Add custom rules for specific artifacts.');
-  lines.push('# Example:');
-  lines.push('#   rules:');
-  lines.push('#     proposal:');
-  lines.push('#       - Keep proposals under 500 words');
-  lines.push('#       - Always include a "Non-goals" section');
-  lines.push('#     tasks:');
-  lines.push('#       - Break tasks into chunks of max 2 hours');
+  if (config.rules && Object.keys(config.rules).length > 0) {
+    lines.push('rules:');
+    for (const [artifactId, rules] of Object.entries(config.rules)) {
+      lines.push(`  ${artifactId}:`);
+      for (const rule of rules) {
+        lines.push(`    - ${rule}`);
+      }
+    }
+  } else {
+    // Rules section with comments
+    lines.push('# Per-artifact rules (optional)');
+    lines.push('# Add custom rules for specific artifacts.');
+    lines.push('# Example:');
+    lines.push('#   rules:');
+    lines.push('#     proposal:');
+    lines.push('#       - Keep proposals under 500 words');
+    lines.push('#       - Always include a "Non-goals" section');
+    lines.push('#     tasks:');
+    lines.push('#       - Break tasks into chunks of max 2 hours');
+  }

   return lines.join('\n') + '\n';
 }
📝 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 function serializeConfig(config: Partial<ProjectConfig>): string {
// Build clean config object (only include defined fields)
const cleanConfig: Record<string, unknown> = {
schema: config.schema,
};
if (config.context) {
cleanConfig.context = config.context;
}
if (config.rules && Object.keys(config.rules).length > 0) {
cleanConfig.rules = config.rules;
}
// Serialize to YAML with proper formatting
return stringifyYaml(cleanConfig, {
indent: 2,
lineWidth: 0, // Don't wrap long lines
defaultStringType: 'PLAIN',
defaultKeyType: 'PLAIN',
});
const lines: string[] = [];
// Schema (required)
lines.push(`schema: ${config.schema}`);
lines.push('');
// Context section with comments
lines.push('# Project context (optional)');
lines.push('# This is shown to AI when creating artifacts.');
lines.push('# Add your tech stack, conventions, style guides, domain knowledge, etc.');
lines.push('# Example:');
lines.push('# context: |');
lines.push('# Tech stack: TypeScript, React, Node.js');
lines.push('# We use conventional commits');
lines.push('# Domain: e-commerce platform');
lines.push('');
// Rules section with comments
lines.push('# Per-artifact rules (optional)');
lines.push('# Add custom rules for specific artifacts.');
lines.push('# Example:');
lines.push('# rules:');
lines.push('# proposal:');
lines.push('# - Keep proposals under 500 words');
lines.push('# - Always include a "Non-goals" section');
lines.push('# tasks:');
lines.push('# - Break tasks into chunks of max 2 hours');
return lines.join('\n') + '\n';
export function serializeConfig(config: Partial<ProjectConfig>): string {
if (!config.schema) {
throw new Error('serializeConfig requires config.schema');
}
const lines: string[] = [];
// Schema (required)
lines.push(`schema: ${config.schema}`);
lines.push('');
if (config.context) {
lines.push('context: |');
for (const line of config.context.split('\n')) {
lines.push(` ${line}`);
}
lines.push('');
} else {
// Context section with comments
lines.push('# Project context (optional)');
lines.push('# This is shown to AI when creating artifacts.');
lines.push('# Add your tech stack, conventions, style guides, domain knowledge, etc.');
lines.push('# Example:');
lines.push('# context: |');
lines.push('# Tech stack: TypeScript, React, Node.js');
lines.push('# We use conventional commits');
lines.push('# Domain: e-commerce platform');
lines.push('');
}
if (config.rules && Object.keys(config.rules).length > 0) {
lines.push('rules:');
for (const [artifactId, rules] of Object.entries(config.rules)) {
lines.push(` ${artifactId}:`);
for (const rule of rules) {
lines.push(` - ${rule}`);
}
}
} else {
// Rules section with comments
lines.push('# Per-artifact rules (optional)');
lines.push('# Add custom rules for specific artifacts.');
lines.push('# Example:');
lines.push('# rules:');
lines.push('# proposal:');
lines.push('# - Keep proposals under 500 words');
lines.push('# - Always include a "Non-goals" section');
lines.push('# tasks:');
lines.push('# - Break tasks into chunks of max 2 hours');
}
return lines.join('\n') + '\n';
}
🤖 Prompt for AI Agents
In `@src/core/config-prompts.ts` around lines 9 - 38, serializeConfig currently
prints only comments and blindly emits "schema: undefined" when config.schema is
missing and drops config.context/config.rules; update serializeConfig to
validate that config.schema is present (throw or return an error) and when
config.context or config.rules exist, render them into the output instead of
discarding them: render context as a YAML block scalar (e.g., "context: |"
followed by each line of config.context indented) and render rules as a proper
YAML mapping/sequence (preserve nested artifact keys and arrays) while keeping
the comment sections when those fields are absent; ensure the final output still
ends with a newline.

- Remove `forceExit: true` from vitest.config.ts (Jest option, not Vitest)
- Add actual teardown logic in vitest.setup.ts that forces exit after 1s
  grace period if processes are still hanging
@TabishB TabishB merged commit e073680 into main Jan 20, 2026
9 checks passed
@TabishB TabishB deleted the simplify-setup-config branch January 20, 2026 22:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants