{"format":1,"manifest":{"name":"project-scaffold","publisher":"vyloxi","version":"1.0.0","displayName":"Project Scaffold","description":"Start a project from a small template (static site, Node, Express, Python, Flask, Rust, Go, PHP), add a .gitignore, LICENSE, Dockerfile or CI file, or run an official generator.","engines":{"ferrum":"^0.1.0"},"categories":["Tools"],"icon":"icon.png","repository":"https://github.com/voukengdongmofrankysteve/ferrum-market","license":"MIT","main":"extension.js","activationEvents":["onCommand:projectScaffold.new","onCommand:projectScaffold.addFile","onCommand:projectScaffold.generator"],"permissions":["workspace.read","workspace.write","terminal"],"contributes":{"commands":[{"command":"projectScaffold.new","title":"New Project from Template...","category":"Project Scaffold"},{"command":"projectScaffold.addFile","title":"Add a File (.gitignore, LICENSE, Dockerfile, CI...)...","category":"Project Scaffold"},{"command":"projectScaffold.generator","title":"Run an Official Project Generator...","category":"Project Scaffold"}],"configuration":{"title":"Project Scaffold","properties":{"projectScaffold.author":{"type":"string","default":"","description":"Your name, offered as the copyright holder when you add a LICENSE."}}}}},"files":{"icon.png":{"encoding":"base64","content":"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"},"extension.js":{"encoding":"utf8","content":"// Project Scaffold: start a project from a small, honest template, or add one useful file (.gitignore, .editorconfig, LICENSE,\n// Dockerfile, CI workflow). Files are written into the folder you opened; files that already exist are never overwritten.\n// Bigger starters (Vite, Next.js, Laravel, Django) run their official generator in the terminal.\n\nconst slug = (s) => String(s || '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 60);\nconst snake = (s) => slug(s).replace(/-/g, '_') || 'app';\nconst safeName = (s) => /^[A-Za-z0-9][A-Za-z0-9._-]{0,60}$/.test(String(s || ''));\n\nconst GITIGNORE = {\n  node: 'node_modules/\\ndist/\\nbuild/\\ncoverage/\\n.env\\n.env.local\\n*.log\\n.DS_Store\\n',\n  python: '__pycache__/\\n*.py[cod]\\n.venv/\\nvenv/\\n.env\\n*.egg-info/\\ndist/\\nbuild/\\n.pytest_cache/\\n.mypy_cache/\\n.ruff_cache/\\n.DS_Store\\n',\n  rust: '/target\\nCargo.lock.bak\\n.env\\n.DS_Store\\n',\n  go: '/bin/\\n/vendor/\\n*.exe\\n*.test\\n*.out\\n.env\\n.DS_Store\\n',\n  php: '/vendor/\\n.env\\n.phpunit.result.cache\\n/storage/*.key\\n.DS_Store\\n',\n  general: '.env\\n.env.*\\n!.env.example\\n*.log\\n.DS_Store\\nThumbs.db\\n.idea/\\n.vscode/\\n',\n};\n\nconst EDITORCONFIG = 'root = true\\n\\n[*]\\ncharset = utf-8\\nend_of_line = lf\\nindent_style = space\\nindent_size = 2\\ninsert_final_newline = true\\ntrim_trailing_whitespace = true\\n\\n[*.{py,rs}]\\nindent_size = 4\\n\\n[Makefile]\\nindent_style = tab\\n\\n[*.md]\\ntrim_trailing_whitespace = false\\n';\n\nconst mit = (holder, year) => `MIT License\\n\\nCopyright (c) ${year} ${holder}\\n\\nPermission is hereby granted, free of charge, to any person obtaining a copy\\nof this software and associated documentation files (the \"Software\"), to deal\\nin the Software without restriction, including without limitation the rights\\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\\ncopies of the Software, and to permit persons to whom the Software is\\nfurnished to do so, subject to the following conditions:\\n\\nThe above copyright notice and this permission notice shall be included in all\\ncopies or substantial portions of the Software.\\n\\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\\nSOFTWARE.\\n`;\n\nconst readme = (name, run) => `# ${name}\\n\\nDescribe what ${name} does in one or two sentences.\\n\\n## Run it\\n\\n\\`\\`\\`\\n${run}\\n\\`\\`\\`\\n`;\n\nconst TEMPLATES = {\n  'static-site': {\n    label: 'Static website', detail: 'index.html, css/style.css and js/main.js',\n    make: (n) => ({\n      'index.html': `<!doctype html>\\n<html lang=\"en\">\\n<head>\\n  <meta charset=\"utf-8\">\\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\\n  <title>${n}</title>\\n  <link rel=\"stylesheet\" href=\"css/style.css\">\\n</head>\\n<body>\\n  <main>\\n    <h1>${n}</h1>\\n    <p>Edit <code>index.html</code> to get started.</p>\\n  </main>\\n  <script src=\"js/main.js\"></script>\\n</body>\\n</html>\\n`,\n      'css/style.css': `:root { --bg: #ffffff; --fg: #1f2937; --accent: #2563eb; }\\n@media (prefers-color-scheme: dark) { :root { --bg: #111827; --fg: #e5e7eb; --accent: #60a5fa; } }\\n* { box-sizing: border-box; }\\nbody { margin: 0; font-family: system-ui, sans-serif; background: var(--bg); color: var(--fg); line-height: 1.6; }\\nmain { max-width: 42rem; margin: 0 auto; padding: 3rem 1.25rem; }\\na { color: var(--accent); }\\n`,\n      'js/main.js': `document.addEventListener('DOMContentLoaded', () => {\\n  console.log('${n} is ready');\\n});\\n`,\n      'README.md': readme(n, 'Open index.html in a browser, or serve the folder:\\nnpx serve .'),\n      '.gitignore': GITIGNORE.general,\n    }),\n  },\n  'node-cli': {\n    label: 'Node.js command line tool', detail: 'package.json, bin, src and a test using node:test',\n    make: (n) => ({\n      'package.json': JSON.stringify({ name: slug(n), version: '0.1.0', description: '', type: 'module', bin: { [slug(n)]: 'bin/cli.js' }, main: 'src/index.js', scripts: { start: 'node bin/cli.js', test: 'node --test' }, engines: { node: '>=20' }, license: 'MIT' }, null, 2) + '\\n',\n      'bin/cli.js': `#!/usr/bin/env node\\nimport { run } from '../src/index.js';\\n\\nrun(process.argv.slice(2));\\n`,\n      'src/index.js': `export function greet(name = 'world') {\\n  return \\`Hello, \\${name}!\\`;\\n}\\n\\nexport function run(args) {\\n  console.log(greet(args[0]));\\n}\\n`,\n      'test/index.test.js': `import test from 'node:test';\\nimport assert from 'node:assert/strict';\\nimport { greet } from '../src/index.js';\\n\\ntest('greets the world by default', () => {\\n  assert.equal(greet(), 'Hello, world!');\\n});\\n\\ntest('greets a name', () => {\\n  assert.equal(greet('Ada'), 'Hello, Ada!');\\n});\\n`,\n      'README.md': readme(n, 'npm start -- Ada\\nnpm test'),\n      '.gitignore': GITIGNORE.node,\n    }),\n  },\n  'express-api': {\n    label: 'Express API', detail: 'A small JSON API with a health check, an items route and tests',\n    make: (n) => ({\n      'package.json': JSON.stringify({ name: slug(n), version: '0.1.0', private: true, type: 'module', main: 'src/server.js', scripts: { start: 'node src/server.js', dev: 'node --watch src/server.js', test: 'node --test' }, dependencies: { express: '^4.21.0' }, engines: { node: '>=20' } }, null, 2) + '\\n',\n      'src/app.js': `import express from 'express';\\n\\nexport function createApp() {\\n  const app = express();\\n  app.use(express.json());\\n\\n  const items = [];\\n\\n  app.get('/health', (req, res) => res.json({ ok: true }));\\n  app.get('/items', (req, res) => res.json(items));\\n  app.post('/items', (req, res) => {\\n    const name = String(req.body?.name ?? '').trim();\\n    if (!name) return res.status(422).json({ error: 'name is required' });\\n    const item = { id: items.length + 1, name };\\n    items.push(item);\\n    res.status(201).json(item);\\n  });\\n\\n  app.use((req, res) => res.status(404).json({ error: 'not found' }));\\n  return app;\\n}\\n`,\n      'src/server.js': `import { createApp } from './app.js';\\n\\nconst port = Number(process.env.PORT) || 3000;\\ncreateApp().listen(port, () => console.log(\\`Listening on http://localhost:\\${port}\\`));\\n`,\n      'test/app.test.js': `import test from 'node:test';\\nimport assert from 'node:assert/strict';\\nimport { createApp } from '../src/app.js';\\n\\nasync function withServer(fn) {\\n  const server = createApp().listen(0);\\n  try {\\n    await fn(\\`http://localhost:\\${server.address().port}\\`);\\n  } finally {\\n    server.close();\\n  }\\n}\\n\\ntest('health check', async () => {\\n  await withServer(async (url) => {\\n    const res = await fetch(url + '/health');\\n    assert.equal(res.status, 200);\\n    assert.deepEqual(await res.json(), { ok: true });\\n  });\\n});\\n\\ntest('creating an item needs a name', async () => {\\n  await withServer(async (url) => {\\n    const res = await fetch(url + '/items', { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' });\\n    assert.equal(res.status, 422);\\n  });\\n});\\n`,\n      '.env.example': 'PORT=3000\\n',\n      'README.md': readme(n, 'npm install\\nnpm run dev\\nnpm test'),\n      '.gitignore': GITIGNORE.node,\n    }),\n  },\n  'python-package': {\n    label: 'Python package', detail: 'pyproject.toml, src layout and a pytest test',\n    make: (n) => {\n      const pkg = snake(n);\n      return {\n        'pyproject.toml': `[build-system]\\nrequires = [\"setuptools>=68\"]\\nbuild-backend = \"setuptools.build_meta\"\\n\\n[project]\\nname = \"${slug(n)}\"\\nversion = \"0.1.0\"\\ndescription = \"\"\\nreadme = \"README.md\"\\nrequires-python = \">=3.10\"\\ndependencies = []\\n\\n[project.optional-dependencies]\\ndev = [\"pytest\"]\\n\\n[tool.setuptools.packages.find]\\nwhere = [\"src\"]\\n\\n[tool.pytest.ini_options]\\ntestpaths = [\"tests\"]\\npythonpath = [\"src\"]\\n`,\n        [`src/${pkg}/__init__.py`]: `\"\"\"${n}.\"\"\"\\n\\n__version__ = \"0.1.0\"\\n`,\n        [`src/${pkg}/main.py`]: `def greet(name: str = \"world\") -> str:\\n    return f\"Hello, {name}!\"\\n\\n\\nif __name__ == \"__main__\":\\n    print(greet())\\n`,\n        'tests/test_main.py': `from ${pkg}.main import greet\\n\\n\\ndef test_greet_default():\\n    assert greet() == \"Hello, world!\"\\n\\n\\ndef test_greet_name():\\n    assert greet(\"Ada\") == \"Hello, Ada!\"\\n`,\n        'README.md': readme(n, `python -m venv .venv\\n.venv/bin/pip install -e \".[dev]\"   # on Windows: .venv\\\\Scripts\\\\pip\\n.venv/bin/python -m pytest`),\n        '.gitignore': GITIGNORE.python,\n      };\n    },\n  },\n  'flask-app': {\n    label: 'Flask web app', detail: 'app.py, a template, a stylesheet and requirements.txt',\n    make: (n) => ({\n      'app.py': `from flask import Flask, render_template\\n\\napp = Flask(__name__)\\n\\n\\n@app.route(\"/\")\\ndef index():\\n    return render_template(\"index.html\", title=\"${n}\")\\n\\n\\n@app.route(\"/health\")\\ndef health():\\n    return {\"ok\": True}\\n\\n\\nif __name__ == \"__main__\":\\n    app.run(debug=True)\\n`,\n      'templates/index.html': `<!doctype html>\\n<html lang=\"en\">\\n<head>\\n  <meta charset=\"utf-8\">\\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\\n  <title>{{ title }}</title>\\n  <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='style.css') }}\">\\n</head>\\n<body>\\n  <main><h1>{{ title }}</h1></main>\\n</body>\\n</html>\\n`,\n      'static/style.css': `body { font-family: system-ui, sans-serif; max-width: 42rem; margin: 3rem auto; padding: 0 1.25rem; line-height: 1.6; }\\n`,\n      'requirements.txt': 'Flask>=3.0\\n',\n      'README.md': readme(n, 'python -m venv .venv\\n.venv/bin/pip install -r requirements.txt\\n.venv/bin/python app.py'),\n      '.gitignore': GITIGNORE.python,\n    }),\n  },\n  'rust-bin': {\n    label: 'Rust program', detail: 'Cargo.toml and src/main.rs with a test',\n    make: (n) => ({\n      'Cargo.toml': `[package]\\nname = \"${slug(n)}\"\\nversion = \"0.1.0\"\\nedition = \"2021\"\\n\\n[dependencies]\\n`,\n      'src/main.rs': `fn greet(name: &str) -> String {\\n    format!(\"Hello, {name}!\")\\n}\\n\\nfn main() {\\n    println!(\"{}\", greet(\"world\"));\\n}\\n\\n#[cfg(test)]\\nmod tests {\\n    use super::*;\\n\\n    #[test]\\n    fn greets() {\\n        assert_eq!(greet(\"Ada\"), \"Hello, Ada!\");\\n    }\\n}\\n`,\n      'README.md': readme(n, 'cargo run\\ncargo test'),\n      '.gitignore': GITIGNORE.rust,\n    }),\n  },\n  'go-module': {\n    label: 'Go module', detail: 'go.mod, main.go and a test',\n    make: (n) => ({\n      'go.mod': `module ${slug(n)}\\n\\ngo 1.22\\n`,\n      'main.go': `package main\\n\\nimport \"fmt\"\\n\\nfunc Greet(name string) string {\\n\\treturn fmt.Sprintf(\"Hello, %s!\", name)\\n}\\n\\nfunc main() {\\n\\tfmt.Println(Greet(\"world\"))\\n}\\n`,\n      'main_test.go': `package main\\n\\nimport \"testing\"\\n\\nfunc TestGreet(t *testing.T) {\\n\\tif got := Greet(\"Ada\"); got != \"Hello, Ada!\" {\\n\\t\\tt.Fatalf(\"got %q\", got)\\n\\t}\\n}\\n`,\n      'README.md': readme(n, 'go run .\\ngo test ./...'),\n      '.gitignore': GITIGNORE.go,\n    }),\n  },\n  'php-composer': {\n    label: 'PHP library', detail: 'composer.json, a class, a test and a public/index.php',\n    make: (n) => {\n      const ns = slug(n).split('-').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join('') || 'App';\n      return {\n        'composer.json': JSON.stringify({ name: 'vendor/' + slug(n), description: '', type: 'library', license: 'MIT', require: { php: '>=8.1' }, 'require-dev': { 'phpunit/phpunit': '^11.0' }, autoload: { 'psr-4': { [ns + '\\\\']: 'src/' } }, scripts: { test: 'phpunit' } }, null, 2) + '\\n',\n        'src/Greeter.php': `<?php\\n\\ndeclare(strict_types=1);\\n\\nnamespace ${ns};\\n\\nfinal class Greeter\\n{\\n    public function greet(string $name = 'world'): string\\n    {\\n        return \"Hello, {$name}!\";\\n    }\\n}\\n`,\n        'tests/GreeterTest.php': `<?php\\n\\ndeclare(strict_types=1);\\n\\nuse ${ns}\\\\Greeter;\\nuse PHPUnit\\\\Framework\\\\TestCase;\\n\\nfinal class GreeterTest extends TestCase\\n{\\n    public function testGreetsByName(): void\\n    {\\n        $this->assertSame('Hello, Ada!', (new Greeter())->greet('Ada'));\\n    }\\n}\\n`,\n        'public/index.php': `<?php\\n\\ndeclare(strict_types=1);\\n\\nrequire __DIR__ . '/../vendor/autoload.php';\\n\\n$greeter = new ${ns}\\\\Greeter();\\necho $greeter->greet();\\n`,\n        'phpunit.xml': `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<phpunit bootstrap=\"vendor/autoload.php\" colors=\"true\">\\n  <testsuites>\\n    <testsuite name=\"default\"><directory>tests</directory></testsuite>\\n  </testsuites>\\n</phpunit>\\n`,\n        'README.md': readme(n, 'composer install\\ncomposer test\\nphp -S localhost:8000 -t public'),\n        '.gitignore': GITIGNORE.php,\n      };\n    },\n  },\n};\n\nconst SINGLE = {\n  'gitignore-node': { label: '.gitignore for Node.js', file: '.gitignore', text: () => GITIGNORE.node },\n  'gitignore-python': { label: '.gitignore for Python', file: '.gitignore', text: () => GITIGNORE.python },\n  'gitignore-rust': { label: '.gitignore for Rust', file: '.gitignore', text: () => GITIGNORE.rust },\n  'gitignore-go': { label: '.gitignore for Go', file: '.gitignore', text: () => GITIGNORE.go },\n  'gitignore-php': { label: '.gitignore for PHP', file: '.gitignore', text: () => GITIGNORE.php },\n  'gitignore-general': { label: '.gitignore (general)', file: '.gitignore', text: () => GITIGNORE.general },\n  editorconfig: { label: '.editorconfig', file: '.editorconfig', text: () => EDITORCONFIG },\n  prettier: { label: '.prettierrc', file: '.prettierrc', text: () => '{\\n  \"singleQuote\": true,\\n  \"semi\": true,\\n  \"printWidth\": 100,\\n  \"trailingComma\": \"all\"\\n}\\n' },\n  license: { label: 'LICENSE (MIT)', file: 'LICENSE', text: (n, ctx) => mit(ctx.holder || 'Your Name', ctx.year) },\n  readme: { label: 'README.md', file: 'README.md', text: (n) => readme(n, 'Describe how to run it.') },\n  'dockerfile-node': { label: 'Dockerfile for Node.js', file: 'Dockerfile', text: () => 'FROM node:22-alpine\\nWORKDIR /app\\nCOPY package*.json ./\\nRUN npm ci --omit=dev\\nCOPY . .\\nENV NODE_ENV=production\\nEXPOSE 3000\\nCMD [\"node\", \"src/server.js\"]\\n' },\n  'dockerfile-python': { label: 'Dockerfile for Python', file: 'Dockerfile', text: () => 'FROM python:3.12-slim\\nWORKDIR /app\\nCOPY requirements.txt ./\\nRUN pip install --no-cache-dir -r requirements.txt\\nCOPY . .\\nEXPOSE 8000\\nCMD [\"python\", \"app.py\"]\\n' },\n  dockerignore: { label: '.dockerignore', file: '.dockerignore', text: () => 'node_modules\\n.git\\n.env\\n*.log\\n__pycache__\\n.venv\\ntarget\\ndist\\n' },\n  compose: { label: 'docker-compose.yml', file: 'docker-compose.yml', text: (n) => `services:\\n  app:\\n    build: .\\n    ports:\\n      - \"3000:3000\"\\n    env_file: .env\\n    restart: unless-stopped\\n` },\n  'ci-node': { label: 'GitHub Actions CI for Node.js', file: '.github/workflows/ci.yml', text: () => 'name: CI\\n\\non:\\n  push:\\n    branches: [main]\\n  pull_request:\\n\\njobs:\\n  test:\\n    runs-on: ubuntu-latest\\n    steps:\\n      - uses: actions/checkout@v4\\n      - uses: actions/setup-node@v4\\n        with:\\n          node-version: 22\\n          cache: npm\\n      - run: npm ci\\n      - run: npm test\\n' },\n  'ci-python': { label: 'GitHub Actions CI for Python', file: '.github/workflows/ci.yml', text: () => 'name: CI\\n\\non:\\n  push:\\n    branches: [main]\\n  pull_request:\\n\\njobs:\\n  test:\\n    runs-on: ubuntu-latest\\n    steps:\\n      - uses: actions/checkout@v4\\n      - uses: actions/setup-python@v5\\n        with:\\n          python-version: \"3.12\"\\n          cache: pip\\n      - run: pip install -e \".[dev]\"\\n      - run: pytest\\n' },\n  'ci-rust': { label: 'GitHub Actions CI for Rust', file: '.github/workflows/ci.yml', text: () => 'name: CI\\n\\non:\\n  push:\\n    branches: [main]\\n  pull_request:\\n\\njobs:\\n  test:\\n    runs-on: ubuntu-latest\\n    steps:\\n      - uses: actions/checkout@v4\\n      - uses: dtolnay/rust-toolchain@stable\\n      - uses: swatinem/rust-cache@v2\\n      - run: cargo test\\n' },\n};\n\nconst GENERATORS = {\n  vite: { label: 'Vite: React + TypeScript', cmd: (n) => `npm create vite@latest ${n} -- --template react-ts` },\n  'vite-vue': { label: 'Vite: Vue + TypeScript', cmd: (n) => `npm create vite@latest ${n} -- --template vue-ts` },\n  'vite-svelte': { label: 'Vite: Svelte + TypeScript', cmd: (n) => `npm create vite@latest ${n} -- --template svelte-ts` },\n  next: { label: 'Next.js', cmd: (n) => `npx create-next-app@latest ${n}` },\n  laravel: { label: 'Laravel', cmd: (n) => `composer create-project laravel/laravel ${n}` },\n  django: { label: 'Django (creates a virtual environment first)', cmd: (n) => `python -m venv ${n}-env && ${n}-env/bin/pip install django && ${n}-env/bin/django-admin startproject ${snake(n)} ${n}` },\n  cargo: { label: 'Rust with cargo', cmd: (n) => `cargo new ${n}` },\n};\n\nexports._test = { TEMPLATES, SINGLE, GENERATORS, slug, snake, safeName, mit };\n\nexports.activate = function (context) {\n  const reg = (id, fn) => context.subscriptions.push(ferrum.commands.register(id, fn));\n  const exists = async (path) => { try { await ferrum.workspace.readFile(path); return true; } catch { return false; } };\n  const folderName = () => (ferrum.workspace.root || '').split(/[\\\\/]/).filter(Boolean).pop() || 'my-project';\n\n  async function writeAll(files) {\n    const written = [];\n    const skipped = [];\n    for (const [path, text] of Object.entries(files)) {\n      if (await exists(path)) skipped.push(path);\n      else { await ferrum.workspace.writeFile(path, text); written.push(path); }\n    }\n    return { written, skipped };\n  }\n\n  reg('projectScaffold.new', async () => {\n    if (!ferrum.workspace.root) return ferrum.window.showMessage('Open an empty folder first; the project is created inside it.', 'warn');\n    const pick = await ferrum.window.showQuickPick(Object.entries(TEMPLATES).map(([id, t]) => ({ label: t.label, detail: t.detail, id })), { placeholder: 'Start a project in ' + folderName() });\n    if (!pick) return;\n    const name = ((await ferrum.window.showInputBox({ prompt: 'Project name', value: folderName() })) || '').trim();\n    if (!name) return;\n    const files = TEMPLATES[pick.id].make(name);\n    const { written, skipped } = await writeAll(files);\n    ferrum.window.showMessage('Created ' + written.length + ' file' + (written.length === 1 ? '' : 's') + (skipped.length ? '. Kept your existing: ' + skipped.join(', ') : '') + '.');\n    const first = ['README.md', ...written].find((p) => written.includes(p));\n    if (first) await ferrum.editor.openFile(first);\n  });\n\n  reg('projectScaffold.addFile', async () => {\n    if (!ferrum.workspace.root) return ferrum.window.showMessage('Open a folder first.', 'warn');\n    const pick = await ferrum.window.showQuickPick(Object.entries(SINGLE).map(([id, s]) => ({ label: s.label, detail: s.file, id })), { placeholder: 'Add a file to ' + folderName() });\n    if (!pick) return;\n    const s = SINGLE[pick.id];\n    if (await exists(s.file)) return ferrum.window.showMessage(s.file + ' already exists. It was not changed.', 'warn');\n    let holder = '';\n    if (pick.id === 'license') holder = ((await ferrum.window.showInputBox({ prompt: 'Copyright holder', value: String(ferrum.settings.get('projectScaffold.author') || '') })) || '').trim();\n    await ferrum.workspace.writeFile(s.file, s.text(folderName(), { holder, year: new Date().getFullYear() }));\n    await ferrum.editor.openFile(s.file);\n  });\n\n  reg('projectScaffold.generator', async () => {\n    const pick = await ferrum.window.showQuickPick(Object.entries(GENERATORS).map(([id, g]) => ({ label: g.label, detail: g.cmd('<name>'), id })), { placeholder: 'Run an official project generator in the terminal' });\n    if (!pick) return;\n    const name = ((await ferrum.window.showInputBox({ prompt: 'Name of the new folder', value: 'my-app' })) || '').trim();\n    if (!safeName(name)) return name ? ferrum.window.showMessage('Use letters, digits, dots, dashes and underscores for the name.', 'warn') : undefined;\n    const cmd = GENERATORS[pick.id].cmd(name);\n    if (!(await ferrum.window.confirm('Run this in the terminal?\\n\\n' + cmd, 'Run'))) return;\n    await ferrum.terminal.run(cmd);\n  });\n};\n\nexports.deactivate = function () {};\n"},"README.md":{"encoding":"utf8","content":"# Project Scaffold\n\nOpen an empty folder and run **New Project from Template...**:\n\n| Template | You get |\n| --- | --- |\n| Static website | index.html, css, js |\n| Node.js command line tool | package.json, bin, src, a `node:test` test |\n| Express API | health check, an items route, tests |\n| Python package | pyproject.toml, src layout, a pytest test |\n| Flask web app | app.py, a template, a stylesheet |\n| Rust program | Cargo.toml, main.rs with a test |\n| Go module | go.mod, main.go, a test |\n| PHP library | composer.json, a class, a PHPUnit test, public/index.php |\n\nThe templates are small on purpose, with a working test and a README, and **no files that already exist are overwritten**.\n\n**Add a File...** adds one useful file: a `.gitignore` (Node, Python, Rust, Go, PHP or general), `.editorconfig`, `.prettierrc`, LICENSE (MIT), README, a Dockerfile (Node or Python), `.dockerignore`, `docker-compose.yml`, or a GitHub Actions CI workflow (Node, Python or Rust).\n\n**Run an Official Project Generator...** shows the command and, after you confirm, runs it in the terminal: Vite (React, Vue or Svelte with TypeScript), Next.js, Laravel, Django or `cargo new`. These download packages, so they need your network and the tools installed.\n\n## Permissions\n\n**workspace.read** and **workspace.write** to create files in the folder you opened, **terminal** for the generators.\n"}}}