-
Notifications
You must be signed in to change notification settings - Fork 7
feat(copilot): enrich installer with addition of tool selection #76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
bab5f96
67d84b8
467cae4
5d835c2
ae25ba7
9720673
b2fa278
166e604
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| name: architecture | ||
| description: Defines the System Architecture - stack, DBs, infra | ||
| tools: | ||
| - codebase | ||
| - editFiles | ||
| --- | ||
|
|
||
| {{INLINE:commands/architecture.md}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| name: implement | ||
| description: Runs tasks - delegates coding to sub-agents, tracks progress | ||
| tools: | ||
| - codebase | ||
| - editFiles | ||
| - runInTerminal | ||
| --- | ||
|
|
||
| {{INLINE:commands/implement.md}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| name: product | ||
| description: Defines the Product - what, why, and for who | ||
| tools: | ||
| - codebase | ||
| - editFiles | ||
| --- | ||
|
|
||
| {{INLINE:commands/product.md}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| name: roadmap | ||
| description: Builds the Product Roadmap - features and their order | ||
| tools: | ||
| - codebase | ||
| - editFiles | ||
| --- | ||
|
|
||
| {{INLINE:commands/roadmap.md}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| name: spec | ||
| description: Creates the Functional Spec - what the feature does for the user | ||
| tools: | ||
| - codebase | ||
| - editFiles | ||
| --- | ||
|
|
||
| {{INLINE:commands/spec.md}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| name: tasks | ||
| description: Breaks the Tech Spec into a task list for engineers | ||
| tools: | ||
| - codebase | ||
| - editFiles | ||
| --- | ||
|
|
||
| {{INLINE:commands/tasks.md}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| name: tech | ||
| description: Creates the Technical Spec - how the feature will be built | ||
| tools: | ||
| - codebase | ||
| - editFiles | ||
| --- | ||
|
|
||
| {{INLINE:commands/tech.md}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| { | ||
| "chat.promptFilesRecommendations": { | ||
| "product": true, | ||
| "roadmap": true, | ||
| "architecture": true, | ||
| "spec": true, | ||
| "tech": true, | ||
| "tasks": true, | ||
| "implement": true | ||
| }, | ||
| "chat.tools.terminal.autoApprove": { | ||
| ".awos/scripts/": true | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,62 +5,118 @@ | |
|
|
||
| /** | ||
| * Directories to create during setup | ||
| * tools: array of tool names this directory applies to ('claude', 'copilot') | ||
| * When tool='all' is selected, all directories are included automatically | ||
| */ | ||
| const directories = [ | ||
| { | ||
| path: '.awos', | ||
| description: 'AWOS configuration directory', | ||
| tools: ['claude', 'copilot'], | ||
| }, | ||
| { | ||
| path: '.claude', | ||
| description: 'Claude configuration directory', | ||
| tools: ['claude'], | ||
| }, | ||
| { | ||
| path: '.awos', | ||
| description: 'awos configuration directory', | ||
| path: '.github/prompts', | ||
| description: 'Copilot prompts directory', | ||
| tools: ['copilot'], | ||
| }, | ||
| { | ||
| path: '.vscode', | ||
| description: 'VS Code configuration directory', | ||
| tools: ['copilot'], | ||
| }, | ||
| { | ||
| path: 'context', | ||
| description: 'A home for project documentation', | ||
| tools: ['claude', 'copilot'], | ||
| }, | ||
| { | ||
| path: 'context/product', | ||
| description: 'Global product definitions', | ||
| tools: ['claude', 'copilot'], | ||
| }, | ||
| { | ||
| path: 'context/spec', | ||
| description: 'A home for specifications', | ||
| tools: ['claude', 'copilot'], | ||
| }, | ||
| ]; | ||
|
|
||
| /** | ||
| * File copy operations to perform during setup | ||
| * Each operation defines what to copy from source to destination | ||
| * tools: array of tool names this operation applies to ('claude', 'copilot') | ||
| * When tool='all' is selected, all operations are included automatically | ||
| */ | ||
| const copyOperations = [ | ||
| // Shared AWOS core (always installed for references) | ||
| { | ||
| source: 'commands', | ||
| destination: '.awos/commands', | ||
| patterns: ['*'], | ||
| description: 'AWOS command prompts', | ||
| tools: ['claude', 'copilot'], | ||
| }, | ||
| { | ||
| source: 'templates', | ||
| destination: '.awos/templates', | ||
| patterns: ['*'], | ||
| description: 'AWOS templates', | ||
| tools: ['claude', 'copilot'], | ||
| }, | ||
| { | ||
| source: 'scripts', | ||
| destination: '.awos/scripts', | ||
| patterns: ['*'], | ||
| description: 'AWOS scripts', | ||
| tools: ['claude', 'copilot'], | ||
| }, | ||
| { | ||
| source: 'claude/commands', | ||
| destination: '.claude/commands/awos', | ||
| patterns: ['*'], | ||
| description: 'Claude Code commands', | ||
| tools: ['claude'], | ||
| }, | ||
| // Note: Copilot prompts are generated by prompt-generator.js (not copied directly) | ||
| // because Copilot doesn't resolve file references - content must be inlined | ||
| ]; | ||
|
|
||
| /** | ||
| * Filter copy operations by selected tool | ||
| * @param {Array} operations - Copy operations array | ||
| * @param {string} tool - Selected tool ('claude', 'copilot', 'all') | ||
| * @returns {Array} Filtered operations (all operations when tool='all') | ||
| */ | ||
| function filterOperationsByTool(operations, tool) { | ||
| if (tool === 'all') return operations; | ||
| return operations.filter((op) => { | ||
| if (!op.tools) return true; | ||
| return op.tools.includes(tool); | ||
| }); | ||
|
Comment on lines
95
to
100
|
||
| } | ||
|
|
||
| /** | ||
| * Filter directories by selected tool | ||
| * @param {Array} dirs - Directories array | ||
| * @param {string} tool - Selected tool ('claude', 'copilot', 'all') | ||
| * @returns {Array} Filtered directories (all directories when tool='all') | ||
| */ | ||
| function filterDirectoriesByTool(dirs, tool) { | ||
| if (tool === 'all') return dirs; | ||
| return dirs.filter((dir) => { | ||
| if (!dir.tools) return true; | ||
| return dir.tools.includes(tool); | ||
| }); | ||
| } | ||
|
|
||
| module.exports = { | ||
| directories, | ||
| copyOperations, | ||
| filterOperationsByTool, | ||
| filterDirectoriesByTool, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| /** | ||
| * Copilot Services | ||
| * Exports all Copilot-specific installer functionality | ||
| */ | ||
| const { generatePrompts } = require('./prompt-generator'); | ||
|
|
||
| module.exports = { generatePrompts }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,128 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Copilot File Generator Service | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Generates Copilot prompt and agent files by inlining referenced content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Copilot doesn't resolve file references in prompts, so we inline | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * the full content at install time. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fs = require('fs').promises; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const path = require('path'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { log } = require('../utils/logger'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const INLINE_PATTERN = /\{\{INLINE:([^}]+)\}\}/g; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Remove YAML frontmatter from markdown content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string} content - Markdown content with potential frontmatter | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @returns {string} Content without frontmatter | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function removeFrontmatter(content) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Handle both Unix (\n) and Windows (\r\n) line endings | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (frontmatterMatch) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return content.slice(frontmatterMatch[0].length); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return content; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Generate files by inlining {{INLINE:path}} markers with file content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {Object} config - Generation configuration | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string} config.packageRoot - Root directory of the AWOS package | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string} config.sourceDir - Source directory relative to packageRoot | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string} config.outputDir - Output directory (absolute path) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {string} config.filePattern - File extension pattern to match (e.g., '.prompt.md') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {boolean} config.dryRun - Whether to run in dry-run mode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @returns {Promise<Object>} Statistics: { generated: files successfully processed, skipped: files with failed inlines } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async function generateFiles({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| packageRoot, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sourceDir, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| outputDir, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| filePattern, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dryRun = false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fullSourceDir = path.join(packageRoot, sourceDir); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const stats = { generated: 0, skipped: 0 }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Ensure output directory exists | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!dryRun) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fs.mkdir(outputDir, { recursive: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+48
to
+51
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Read all template files | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let files; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| files = await fs.readdir(fullSourceDir); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log(`Source directory not found: ${fullSourceDir}`, 'error'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return stats; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const templateFiles = files.filter((f) => f.endsWith(filePattern)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const file of templateFiles) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const templatePath = path.join(fullSourceDir, file); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let content = await fs.readFile(templatePath, 'utf8'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Replace all {{INLINE:path}} markers with file content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const matches = [...content.matchAll(INLINE_PATTERN)]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let inlineFailed = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const match of matches) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const relativePath = match[1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const inlinePath = path.join(packageRoot, relativePath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const inlineContent = await fs.readFile(inlinePath, 'utf8'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Remove frontmatter from inlined content (keep only body) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const bodyContent = removeFrontmatter(inlineContent); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Use replaceAll to handle multiple occurrences of the same marker | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content = content.replaceAll(match[0], bodyContent); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log(`Failed to inline ${relativePath}: ${err.message}`, 'error'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| inlineFailed = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Skip file if any inline failed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (inlineFailed) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stats.skipped++; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Write generated file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const outputPath = path.join(outputDir, file); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (dryRun) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log(`[DRY-RUN] Would generate: ${outputPath}`, 'info'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await fs.writeFile(outputPath, content, 'utf8'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log(`Generated ${file}`, 'success'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
95
to
101
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Write generated file | |
| if (!dryRun) { | |
| const outputPath = path.join(outputDir, file); | |
| await fs.writeFile(outputPath, content, 'utf8'); | |
| log(`Generated ${file}`, 'success'); | |
| // Write generated file (or log in dry-run mode) | |
| const outputPath = path.join(outputDir, file); | |
| if (!dryRun) { | |
| await fs.writeFile(outputPath, content, 'utf8'); | |
| log(`Generated ${file}`, 'success'); | |
| } else { | |
| log(`[DRY-RUN] Would generate ${file} at ${outputPath}`, 'info'); |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an inline file fails to load, the function logs an error and increments skipped but then continues to increment generated count. This means a file that failed to inline properly will still be counted as successfully generated. The continue statement should skip to the next file in the outer loop, but it actually just skips to the next inline match. If any inline fails, the partially-processed file should not be written or counted as generated.
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unlike the file-copier service which explicitly handles existing files (checking and unlinking before copy), this function always overwrites existing prompt files without checking. For consistency with the rest of the installer and to avoid surprising users, consider adding a check for existing files and either skip generation (preserving user modifications) or log that the file is being updated.
| if (dryRun) { | |
| log(`[DRY-RUN] Would generate: ${outputPath}`, 'info'); | |
| } else { | |
| await fs.writeFile(outputPath, content, 'utf8'); | |
| log(`Generated ${file}`, 'success'); | |
| } | |
| stats.generated++; | |
| // Check if output file already exists to avoid overwriting user changes | |
| let exists = false; | |
| try { | |
| await fs.access(outputPath); | |
| exists = true; | |
| } catch (err) { | |
| // File does not exist; safe to generate | |
| exists = false; | |
| } | |
| if (dryRun) { | |
| if (exists) { | |
| log(`[DRY-RUN] Would skip (already exists): ${outputPath}`, 'info'); | |
| stats.skipped++; | |
| } else { | |
| log(`[DRY-RUN] Would generate: ${outputPath}`, 'info'); | |
| stats.generated++; | |
| } | |
| } else { | |
| if (exists) { | |
| log(`Skipping ${file} (already exists)`, 'info'); | |
| stats.skipped++; | |
| } else { | |
| await fs.writeFile(outputPath, content, 'utf8'); | |
| log(`Generated ${file}`, 'success'); | |
| stats.generated++; | |
| } | |
| } |
Uh oh!
There was an error while loading. Please reload this page.