diff --git a/CLAUDE.md b/CLAUDE.md index 1c65dde..0bc8f6c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -139,7 +139,7 @@ User runs /setup | Module | Purpose | |--------|---------| | `skills/readiness/SKILL.md` | Harness Readiness Report | -| `skills/setup/SKILL.md` | Setup Report | +| `skills/setup/SKILL.md` | If the current directory is empty, scaffold in place: | | `skills/setup/scripts/generate-claude-md.js` | Generate tailored CLAUDE.md files for a project from templates. | | `skills/setup/scripts/init-project.js` | Project scaffolding script for Node/TypeScript projects. | | `skills/setup/scripts/install-enforcement.js` | Copies enforcement tooling into a target project. | @@ -200,7 +200,6 @@ Before merging: - **Setup has two code paths**: Node/TS uses the "fast path" (scripts do the work). All other stacks use the "adaptive path" (Claude creates files). The eval uses `conversation_must_not_mention` to catch cross-contamination (e.g., Python setup mentioning npm). - **Squash merges leave branches dirty**: After squash-merging a PR, the branch still shows unmerged commits. Always create a fresh branch from main for follow-up work — don't reuse the old branch. - **CLAUDE.md auto-updates on commit**: The pre-commit hook runs `repo-generate-docs.js` to regenerate AUTO markers. Don't manually edit content between `` and `` markers — it will be overwritten. -- **Eval prompts must say "in the current directory"**: `init-project.js --name=X` creates a subdirectory. Eval fixtures run in temp dirs, so the grader checks the temp dir root. If the prompt says "call it myapp", Claude creates `myapp/` and the grader finds nothing. --- @@ -212,4 +211,3 @@ Before merging: | Enforcement script patterns | `skills/setup/references/enforcement-scripts.md` | | Node/TypeScript stack reference | `skills/setup/references/stack-node-typescript.md` | | Eval suite documentation | `tests/evals/README.md` | -| Unit test documentation | `tests/scripts/README.md` | diff --git a/skills/setup/SKILL.md b/skills/setup/SKILL.md index d2ccba2..389cf20 100644 --- a/skills/setup/SKILL.md +++ b/skills/setup/SKILL.md @@ -69,9 +69,15 @@ Skip this phase entirely for existing projects. **Node/TypeScript path (fast path — script does the work):** ```bash +# If the current directory is empty, scaffold in place: +node $SCRIPTS_DIR/init-project.js --name= --framework= --in-place + +# If the user wants a new subdirectory (rare — only when explicitly requested): node $SCRIPTS_DIR/init-project.js --name= --framework= ``` +Default to `--in-place` unless the user explicitly asks to create a new directory. + **All other stacks (adaptive path — Claude does the work):** 1. `git init` diff --git a/skills/setup/scripts/init-project.js b/skills/setup/scripts/init-project.js index b83cde4..587ec33 100644 --- a/skills/setup/scripts/init-project.js +++ b/skills/setup/scripts/init-project.js @@ -20,11 +20,12 @@ const childProcess = require('node:child_process'); const VALID_FRAMEWORKS = ['vite', 'nextjs', 'express', 'fastify', 'none']; function parseArgs(argv) { - const flags = { name: null, framework: 'none', skipInstall: false }; + const flags = { name: null, framework: 'none', skipInstall: false, inPlace: false }; for (const arg of argv.slice(2)) { if (arg.startsWith('--name=')) flags.name = arg.slice('--name='.length); else if (arg.startsWith('--framework=')) flags.framework = arg.slice('--framework='.length); else if (arg === '--skip-install') flags.skipInstall = true; + else if (arg === '--in-place') flags.inPlace = true; } if (!VALID_FRAMEWORKS.includes(flags.framework)) { console.error('Unknown framework "' + flags.framework + '". Must be one of: ' + VALID_FRAMEWORKS.join(', ')); @@ -86,12 +87,12 @@ function main() { // Resolve working directory let projectDir = process.cwd(); - if (flags.name) { + if (flags.name && !flags.inPlace) { projectDir = path.join(process.cwd(), flags.name); fs.mkdirSync(projectDir, { recursive: true }); } - const projectName = path.basename(projectDir); + const projectName = flags.name || path.basename(projectDir); // Git init if needed if (!fs.existsSync(path.join(projectDir, '.git'))) { diff --git a/tests/scripts/init-project.test.js b/tests/scripts/init-project.test.js index 71484c8..7eef544 100644 --- a/tests/scripts/init-project.test.js +++ b/tests/scripts/init-project.test.js @@ -59,6 +59,26 @@ describe('--name flag', () => { }); }); +// --------------------------------------------------------------------------- +// --in-place: uses name for package.json but stays in current directory +// --------------------------------------------------------------------------- +describe('--in-place flag', () => { + it('scaffolds in current directory, not a subdirectory', () => { + runScript(['--name=myapp', '--framework=none', '--skip-install', '--in-place'], tmpDir); + + expect(fs.existsSync(path.join(tmpDir, 'package.json'))).toBe(true); + expect(fs.existsSync(path.join(tmpDir, 'src'))).toBe(true); + expect(fs.existsSync(path.join(tmpDir, 'myapp'))).toBe(false); + }); + + it('uses the --name value in package.json', () => { + runScript(['--name=myapp', '--framework=none', '--skip-install', '--in-place'], tmpDir); + + const pkg = JSON.parse(fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf8')); + expect(pkg.name).toBe('myapp'); + }); +}); + // --------------------------------------------------------------------------- // No --name: initializes in current directory // ---------------------------------------------------------------------------