@timeax/scaffold 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/readme.md CHANGED
@@ -1,4 +1,4 @@
1
- # Scaffold - `@timeax/scaffold`
1
+ # @timeax/scaffold
2
2
 
3
3
  A tiny, opinionated scaffolding tool that keeps your project structure in sync with a **declarative tree** (like `structure.txt`) – Prisma‑style.
4
4
 
@@ -8,11 +8,13 @@ A tiny, opinionated scaffolding tool that keeps your project structure in sync w
8
8
  * Reverse‑engineer existing projects into `*.txt` structures.
9
9
  * Watch for changes and re‑apply automatically.
10
10
 
11
+ > **Supported structure files:** `.tss`, `.stx`, `structure.txt`, and any `.txt` files inside `.scaffold/`.
12
+
11
13
  ---
12
14
 
13
15
  ## Features
14
16
 
15
- * **Prisma‑style scaffold directory**: all config and structure lives under `scaffold/` by default.
17
+ * **Prisma‑style scaffold directory**: all config and structure lives under `.scaffold/` by default.
16
18
  * **Config‑driven groups**: declare multiple roots (e.g. `app`, `frontend`) with their own structure files.
17
19
  * **Plain‑text structure files**: strict, easy‑to‑read tree syntax with indentation and annotations.
18
20
  * **Safe apply**:
@@ -26,8 +28,14 @@ A tiny, opinionated scaffolding tool that keeps your project structure in sync w
26
28
  * Regular hooks around file create/delete.
27
29
  * Stub hooks around content generation.
28
30
  * **Stubs**: programmatic content generators for files (e.g. React pages, controllers, etc.).
29
- * **Watch mode**: watch `scaffold/` for changes and re‑run automatically.
31
+ * **Watch mode**: watch `.scaffold/` for changes and re‑run automatically.
30
32
  * **Scanner**: generate `structure.txt` (or per‑group `*.txt`) from an existing codebase.
33
+ * **VS Code integration** (via a companion extension):
34
+
35
+ * Syntax highlighting for `.tss`, `.stx`, `structure.txt`, and `.scaffold/**/*.txt`.
36
+ * Inline diagnostics using the same parser as the CLI.
37
+ * “Go to file” from a structure line.
38
+ * Simple formatting and sorting commands.
31
39
 
32
40
  ---
33
41
 
@@ -57,7 +65,7 @@ pnpm scaffold init
57
65
  This will create:
58
66
 
59
67
  ```txt
60
- scaffold/
68
+ .scaffold/
61
69
  config.ts # main ScaffoldConfig
62
70
  structure.txt # example structure (single-root mode)
63
71
  ```
@@ -74,7 +82,7 @@ scaffold init --dir tools/scaffold
74
82
 
75
83
  ### 2. Define your structure
76
84
 
77
- By default, `scaffold/structure.txt` is used in single‑root mode.
85
+ By default, `.scaffold/structure.txt` is used in single‑root mode.
78
86
 
79
87
  Example:
80
88
 
@@ -98,12 +106,16 @@ README.md
98
106
 
99
107
  **Rules:**
100
108
 
101
- * Indent with **2 spaces per level** (strict).
109
+ * Indent with **2 spaces per level** (strict by default; configurable).
102
110
  * Directories **must** end with `/`.
103
111
  * Files **must not** end with `/`.
104
112
  * You **cannot indent under a file** (files cannot have children).
105
113
  * You can’t “skip” levels (no jumping from depth 0 to depth 2 in one go).
106
- * Lines starting with `#` are comments.
114
+ * Lines starting with `#` or `//` (after indentation) are comments.
115
+ * Inline comments are supported:
116
+
117
+ * `index.ts # comment`
118
+ * `index.ts // comment`
107
119
 
108
120
  #### Annotations
109
121
 
@@ -124,20 +136,22 @@ Supported inline annotations:
124
136
 
125
137
  These map onto the `StructureEntry` fields in TypeScript.
126
138
 
139
+ > `:` is reserved for annotations (e.g. `@stub:page`). Paths themselves must **not** contain `:`.
140
+
127
141
  ---
128
142
 
129
143
  ### 3. Configure groups (optional but recommended)
130
144
 
131
- In `scaffold/config.ts` you can enable grouped mode:
145
+ In `.scaffold/config.ts` you can enable grouped mode:
132
146
 
133
147
  ```ts
134
148
  import type { ScaffoldConfig } from '@timeax/scaffold';
135
149
 
136
150
  const config: ScaffoldConfig = {
137
- root: '.', // project root (optional, defaults to cwd)
151
+ base: '.', // project root (optional, defaults to cwd)
138
152
 
139
153
  groups: [
140
- { name: 'app', root: 'app', structureFile: 'app.txt' },
154
+ { name: 'app', root: 'app', structureFile: 'app.txt' },
141
155
  { name: 'frontend', root: 'resources/js', structureFile: 'frontend.txt' },
142
156
  ],
143
157
 
@@ -148,21 +162,21 @@ const config: ScaffoldConfig = {
148
162
  export default config;
149
163
  ```
150
164
 
151
- Then create per‑group structure files in `scaffold/`:
165
+ Then create per‑group structure files in `.scaffold/`:
152
166
 
153
167
  ```txt
154
- # scaffold/app.txt
168
+ # .scaffold/app.txt
155
169
  App/Services/
156
170
  UserService.php
157
171
 
158
- # scaffold/frontend.txt
172
+ # .scaffold/frontend.txt
159
173
  src/
160
174
  index.tsx
161
175
  pages/
162
176
  home.tsx
163
177
  ```
164
178
 
165
- > When `groups` is defined and non‑empty, single‑root `structure`/`structureFile` is ignored.
179
+ > When `groups` is defined and non‑empty, single‑root `structure` / `structureFile` is ignored.
166
180
 
167
181
  ---
168
182
 
@@ -173,16 +187,16 @@ src/
173
187
  scaffold
174
188
 
175
189
  # or with explicit scaffold dir / config
176
- scaffold --dir scaffold --config scaffold/config.ts
190
+ scaffold --dir .scaffold --config .scaffold/config.ts
177
191
  ```
178
192
 
179
193
  What happens:
180
194
 
181
- * Config is loaded from `scaffold/config.*` (Prisma‑style resolution).
195
+ * Config is loaded from `.scaffold/config.*` (Prisma‑style resolution).
182
196
  * Structure(s) are resolved (grouped or single‑root).
183
197
  * Files/directories missing on disk are created.
184
198
  * New files are registered in `.scaffold-cache.json` (under project root by default).
185
- * Any previously created files that are no longer in the structure are candidates for deletion.
199
+ * Any previously created files that are no longer in the structure are candidates for deletion:
186
200
 
187
201
  * Small files are deleted automatically.
188
202
  * Large files (configurable threshold) trigger an interactive prompt.
@@ -195,8 +209,8 @@ scaffold --watch
195
209
 
196
210
  * Watches:
197
211
 
198
- * `scaffold/config.*`
199
- * `scaffold/*.txt`
212
+ * `.scaffold/config.*`
213
+ * `.scaffold/*.txt`
200
214
  * Debounces rapid edits.
201
215
  * Prevents overlapping runs.
202
216
 
@@ -213,11 +227,13 @@ scaffold [options]
213
227
  Options:
214
228
 
215
229
  * `-c, --config <path>` – override config file path.
216
- * `-d, --dir <path>` – override scaffold directory (default: `./scaffold`).
230
+ * `-d, --dir <path>` – override scaffold directory (default: `./.scaffold`).
217
231
  * `-w, --watch` – watch scaffold directory for changes.
218
232
  * `--quiet` – silence logs.
219
233
  * `--debug` – verbose debug logs.
220
234
 
235
+ ---
236
+
221
237
  ### `scaffold init`
222
238
 
223
239
  Initialize the scaffold directory + config + structure.
@@ -228,42 +244,85 @@ scaffold init [options]
228
244
 
229
245
  Options:
230
246
 
231
- * `-d, --dir <path>` – scaffold directory (default: `./scaffold`, inherited from root options).
247
+ * `-d, --dir <path>` – scaffold directory (default: `./.scaffold`, inherited from root options).
232
248
  * `--force` – overwrite existing `config.ts` / `structure.txt`.
233
249
 
250
+ ---
251
+
234
252
  ### `scaffold scan`
235
253
 
236
254
  Generate `structure.txt`‑style definitions from an existing project.
237
255
 
238
256
  Two modes:
239
257
 
240
- 1. **Config‑aware mode** (default if no `--root` / `--out` given):
258
+ #### 1. Config‑aware mode (default if no `--root` / `--out` given)
259
+
260
+ ```bash
261
+ scaffold scan
262
+ scaffold scan --from-config
263
+ scaffold scan --from-config --groups app frontend
264
+ ```
265
+
266
+ * Loads `.scaffold/config.ts`.
267
+ * For each `group` in config:
268
+
269
+ * Scans `group.root` on disk.
270
+ * Writes to `.scaffold/<group.structureFile || group.name + '.txt'>`.
271
+ * `--groups` filters which groups to scan.
272
+
273
+ #### 2. Manual mode (single root)
274
+
275
+ ```bash
276
+ scaffold scan -r src
277
+ scaffold scan -r src -o .scaffold/src.txt
278
+ ```
279
+
280
+ Options:
241
281
 
242
- ```bash
243
- scaffold scan
244
- scaffold scan --from-config
245
- scaffold scan --from-config --groups app frontend
246
- ```
282
+ * `-r, --root <path>` – directory to scan.
283
+ * `-o, --out <path>` – output file (otherwise prints to stdout).
284
+ * `--ignore <patterns...>` – extra globs to ignore (in addition to defaults like `node_modules/**`, `.git/**`, etc.).
247
285
 
248
- * Loads `scaffold/config.ts`.
249
- * For each `group` in config:
286
+ ---
287
+
288
+ ### `scaffold structures`
250
289
 
251
- * Scans `group.root` on disk.
252
- * Writes to `scaffold/<group.structureFile || group.name + '.txt'>`.
253
- * `--groups` filters which groups to scan.
290
+ Ensure that all structure files declared in your config exist.
254
291
 
255
- 2. **Manual mode** (single root):
292
+ ```bash
293
+ scaffold structures
294
+ ```
256
295
 
257
- ```bash
258
- scaffold scan -r src
259
- scaffold scan -r src -o scaffold/src.txt
260
- ```
296
+ What it does:
261
297
 
262
- Options:
298
+ * Loads `.scaffold/config.*`.
299
+ * Determines which structure files are expected:
263
300
 
264
- * `-r, --root <path>` directory to scan.
265
- * `-o, --out <path>` output file (otherwise prints to stdout).
266
- * `--ignore <patterns...>` extra globs to ignore (in addition to defaults like `node_modules/**`, `.git/**`, etc.).
301
+ * **Grouped mode** (`config.groups` defined): each group gets `group.structureFile || `${group.name}.txt``.
302
+ * **Single-root mode** (no groups): uses `config.structureFile || 'structure.txt'`.
303
+ * For each expected structure file:
304
+
305
+ * If it **already exists** → it is left untouched.
306
+ * If it is **missing** → it is created with a small header comment.
307
+
308
+ Examples:
309
+
310
+ ```bash
311
+ # With grouped config:
312
+ # groups: [
313
+ # { name: 'app', root: 'app', structureFile: 'app.txt' },
314
+ # { name: 'frontend', root: 'resources/js', structureFile: 'frontend.txt' },
315
+ # ]
316
+ scaffold structures
317
+ # => ensures .scaffold/app.txt and .scaffold/frontend.txt exist
318
+
319
+ # With single-root config:
320
+ # structureFile: 'structure.txt'
321
+ scaffold structures
322
+ # => ensures .scaffold/structure.txt exists
323
+ ```
324
+
325
+ This is useful right after setting up or editing `.scaffold/config.ts` so that all declared structure files are present and ready to edit.
267
326
 
268
327
  ---
269
328
 
@@ -276,8 +335,8 @@ import { runOnce } from '@timeax/scaffold';
276
335
 
277
336
  await runOnce(process.cwd(), {
278
337
  // optional overrides
279
- configPath: 'scaffold/config.ts',
280
- scaffoldDir: 'scaffold',
338
+ configPath: '.scaffold/config.ts',
339
+ scaffoldDir: '.scaffold',
281
340
  });
282
341
  ```
283
342
 
@@ -298,7 +357,7 @@ const results = await scanProjectFromConfig(process.cwd(), {
298
357
  groups: ['app', 'frontend'],
299
358
  });
300
359
 
301
- // write group structure files to scaffold/
360
+ // write group structure files to .scaffold/
302
361
  await writeScannedStructuresFromConfig(process.cwd(), {
303
362
  groups: ['app'],
304
363
  });
@@ -362,7 +421,9 @@ const config: ScaffoldConfig = {
362
421
  name: 'page',
363
422
  async getContent(ctx) {
364
423
  const name = ctx.targetPath.split('/').pop();
365
- return `export default function ${name}() {\n return <div>${name}</div>;\n}`;
424
+ return `export default function ${name}() {
425
+ return <div>${name}</div>;
426
+ }`;
366
427
  },
367
428
  hooks: {
368
429
  preStub: [
@@ -379,7 +440,7 @@ const config: ScaffoldConfig = {
379
440
  };
380
441
  ```
381
442
 
382
- In `structure.txt`:
443
+ In a structure file:
383
444
 
384
445
  ```txt
385
446
  src/
@@ -420,5 +481,82 @@ Some things this package is intentionally designed to grow into:
420
481
  * Stub groups (one logical stub creating multiple files).
421
482
  * Built‑in templates for common stacks (Laravel + Inertia, Next.js, etc.).
422
483
  * Better diff/dry‑run UX (show what will change without touching disk).
484
+ * Deeper VS Code integration:
485
+
486
+ * Tree-aware sorting.
487
+ * Visual tree editor.
488
+ * Code actions / quick fixes for common mistakes.
423
489
 
424
490
  PRs and ideas are welcome ✨
491
+
492
+ ---
493
+
494
+ ## VS Code extension
495
+
496
+ There is an official VS Code companion extension for `@timeax/scaffold` that makes working with your structure files much nicer.
497
+
498
+ ### Language support
499
+
500
+ The extension adds a custom language **Scaffold Structure** and:
501
+
502
+ * Highlights:
503
+
504
+ * Directories (lines ending with `/`)
505
+ * Files
506
+ * Inline annotations like `@stub:name`, `@include:pattern`, `@exclude:pattern`
507
+ * Comments using `#` or `//` (full-line and inline)
508
+ * Treats the following files as scaffold structures:
509
+
510
+ * `*.tss`
511
+ * `*.stx`
512
+ * `structure.txt`
513
+ * Any `.txt` file inside your `.scaffold/` directory
514
+
515
+ The syntax rules match the CLI parser:
516
+
517
+ * Indent is in fixed steps (configurable via `indentStep`).
518
+ * Only directories can have children.
519
+ * `:` is reserved for annotations and not allowed inside path names.
520
+
521
+ ### Editor commands
522
+
523
+ The extension contributes several commands (available via the Command Palette and context menus when editing a scaffold structure file):
524
+
525
+ * **Scaffold: Go to file**
526
+
527
+ * Reads the path on the current line and opens the corresponding file in your project.
528
+ * Respects `.scaffold/config.*`:
529
+
530
+ * Uses `base`/`root` to resolve paths.
531
+ * If the current structure file belongs to a `group`, it resolves relative to that group’s `root`.
532
+ * If the file doesn’t exist, it can prompt to create it and open it immediately.
533
+
534
+ * **Scaffold: Format structure file**
535
+
536
+ * Normalizes line endings and trims trailing whitespace.
537
+ * Designed to be safe even on partially-invalid files.
538
+ * Future versions may use the full AST from `@timeax/scaffold` to enforce indentation and ordering.
539
+
540
+ * **Scaffold: Sort entries**
541
+
542
+ * Naive helper that sorts non-comment lines lexicographically while keeping comment/blank lines in place.
543
+ * Useful for quick cleanups of small structure files.
544
+
545
+ * **Scaffold: Open config**
546
+
547
+ * Opens `.scaffold/config.*` for the current workspace (searching common extensions like `config.ts`, `config.mts`, etc.).
548
+
549
+ * **Scaffold: Open .scaffold folder**
550
+
551
+ * Reveals the `.scaffold/` directory in the VS Code Explorer.
552
+
553
+ ### Live validation (diagnostics)
554
+
555
+ Whenever you open or edit a scaffold structure file:
556
+
557
+ * The extension calls `parseStructureText` from `@timeax/scaffold` under the hood.
558
+ * If parsing fails, the error message (e.g. invalid indentation, children under a file, bad path, etc.) is shown as an editor diagnostic (squiggly underline) on the relevant line.
559
+
560
+ This means your editor and the CLI always agree on what is valid, since they share the same parser and rules.
561
+
562
+ > The extension is optional, but highly recommended if you edit `*.tss` / `*.stx` or `structure.txt` files frequently.
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ // scripts/postpublish.mjs
3
+
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+
7
+ function incrementPatch(version) {
8
+ const parts = version.split(".");
9
+
10
+ if (parts.length !== 3) {
11
+ throw new Error(`[postpublish] Unsupported version format: "${version}"`);
12
+ }
13
+
14
+ const [majorRaw, minorRaw, patchRaw] = parts;
15
+ const major = Number(majorRaw);
16
+ const minor = Number(minorRaw);
17
+ const patch = Number(patchRaw);
18
+
19
+ if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch)) {
20
+ throw new Error(
21
+ `[postpublish] Version contains non-numeric parts: "${version}"`
22
+ );
23
+ }
24
+
25
+ const nextPatch = patch + 1;
26
+ return `${major}.${minor}.${nextPatch}`;
27
+ }
28
+
29
+ function main() {
30
+ const pkgPath = path.resolve(process.cwd(), "package.json");
31
+
32
+ if (!fs.existsSync(pkgPath)) {
33
+ console.error("[postpublish] Could not find package.json at", pkgPath);
34
+ process.exit(1);
35
+ }
36
+
37
+ const raw = fs.readFileSync(pkgPath, "utf8");
38
+ let pkg;
39
+
40
+ try {
41
+ pkg = JSON.parse(raw);
42
+ } catch (e) {
43
+ console.error("[postpublish] Failed to parse package.json:", e);
44
+ process.exit(1);
45
+ }
46
+
47
+ const currentVersion = pkg.version;
48
+ if (typeof currentVersion !== "string") {
49
+ console.error(
50
+ '[postpublish] package.json does not have a string "version" field.'
51
+ );
52
+ process.exit(1);
53
+ }
54
+
55
+ let nextVersion;
56
+ try {
57
+ nextVersion = incrementPatch(currentVersion);
58
+ } catch (e) {
59
+ console.error(String(e));
60
+ process.exit(1);
61
+ }
62
+
63
+ pkg.version = nextVersion;
64
+
65
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
66
+
67
+ console.log(
68
+ `[postpublish] Bumped version in package.json: ${currentVersion} → ${nextVersion}`
69
+ );
70
+ }
71
+
72
+ main();
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ // scripts/prepublish.mjs
3
+
4
+ import { spawnSync } from "node:child_process";
5
+ import readline from "node:readline/promises";
6
+ import { stdin as input, stdout as output } from "node:process";
7
+
8
+ function run(command, args, options = {}) {
9
+ const result = spawnSync(command, args, {
10
+ stdio: "inherit",
11
+ ...options,
12
+ });
13
+
14
+ if (result.error) {
15
+ console.error(`[prepublish] Failed to run ${command}:`, result.error);
16
+ process.exit(1);
17
+ }
18
+
19
+ if (typeof result.status === "number" && result.status !== 0) {
20
+ process.exit(result.status);
21
+ }
22
+
23
+ return result;
24
+ }
25
+
26
+ function runCapture(command, args, options = {}) {
27
+ const result = spawnSync(command, args, {
28
+ encoding: "utf8",
29
+ stdio: ["inherit", "pipe", "pipe"],
30
+ ...options,
31
+ });
32
+
33
+ if (result.error) {
34
+ throw result.error;
35
+ }
36
+
37
+ return result;
38
+ }
39
+
40
+ async function main() {
41
+ // Check if we're in a git repo
42
+ try {
43
+ const revParse = runCapture("git", [
44
+ "rev-parse",
45
+ "--is-inside-work-tree",
46
+ ]);
47
+ if (revParse.status !== 0) {
48
+ console.log("[prepublish] Not a git repository; skipping git checks.");
49
+ return;
50
+ }
51
+ } catch {
52
+ console.log("[prepublish] Not a git repository; skipping git checks.");
53
+ return;
54
+ }
55
+
56
+ // Check for uncommitted changes
57
+ const status = runCapture("git", ["status", "--porcelain"]);
58
+ const outputText = status.stdout.trim();
59
+
60
+ if (!outputText) {
61
+ console.log("[prepublish] Working tree clean. Nothing to commit.");
62
+ return;
63
+ }
64
+
65
+ console.log("[prepublish] You have uncommitted changes:\n");
66
+ console.log(outputText + "\n");
67
+
68
+ const rl = readline.createInterface({ input, output });
69
+ const message = (
70
+ await rl.question(
71
+ "[prepublish] Commit message (leave blank to abort publish): "
72
+ )
73
+ ).trim();
74
+ await rl.close();
75
+
76
+ if (!message) {
77
+ console.error(
78
+ "[prepublish] No commit message provided. Aborting publish."
79
+ );
80
+ process.exit(1);
81
+ }
82
+
83
+ console.log("[prepublish] Staging changes...");
84
+ run("git", ["add", "."]);
85
+
86
+ console.log("[prepublish] Committing...");
87
+ run("git", ["commit", "-m", message]);
88
+
89
+ console.log("[prepublish] Pushing to default remote...");
90
+ run("git", ["push"]);
91
+
92
+ console.log("[prepublish] Git push completed. Proceeding with publish.");
93
+ }
94
+
95
+ await main();
package/src/cli/main.ts CHANGED
@@ -5,6 +5,7 @@ import { Command } from 'commander';
5
5
  import { runOnce, RunOptions } from '../core/runner';
6
6
  import { watchScaffold } from '../core/watcher';
7
7
  import {
8
+ ensureStructureFilesFromConfig,
8
9
  scanDirectoryToStructureText,
9
10
  writeScannedStructuresFromConfig,
10
11
  } from '../core/scan-structure';
@@ -170,6 +171,30 @@ async function handleInitCommand(
170
171
  );
171
172
  }
172
173
 
174
+
175
+ async function handleStructuresCommand(
176
+ cwd: string,
177
+ baseOpts: BaseCliOptions,
178
+ ) {
179
+ const logger = createCliLogger(baseOpts);
180
+
181
+ logger.info('Ensuring structure files declared in config exist...');
182
+
183
+ const { created, existing } = await ensureStructureFilesFromConfig(cwd, {
184
+ scaffoldDirOverride: baseOpts.dir,
185
+ });
186
+
187
+ if (created.length === 0) {
188
+ logger.info('All structure files already exist. Nothing to do.');
189
+ } else {
190
+ for (const filePath of created) {
191
+ logger.info(`Created structure file: ${filePath}`);
192
+ }
193
+ }
194
+
195
+ existing.forEach((p) => logger.debug(`Structure file already exists: ${p}`));
196
+ }
197
+
173
198
  async function main() {
174
199
  const cwd = process.cwd();
175
200
 
@@ -234,9 +259,22 @@ async function main() {
234
259
  await handleRunCommand(cwd, opts);
235
260
  });
236
261
 
262
+ interface StructuresCliOptions { }
263
+
264
+ program
265
+ .command('structures')
266
+ .description(
267
+ 'Create missing structure files specified in the config (does not overwrite existing files)',
268
+ )
269
+ .action(async (_opts: StructuresCliOptions, cmd: Command) => {
270
+ const baseOpts = cmd.parent?.opts<BaseCliOptions>() ?? {};
271
+ await handleStructuresCommand(cwd, baseOpts);
272
+ });
273
+
237
274
  await program.parseAsync(process.argv);
238
275
  }
239
276
 
277
+
240
278
  // Run and handle errors
241
279
  main().catch((err) => {
242
280
  defaultLogger.error(err);
@@ -7,12 +7,13 @@ import crypto from 'crypto';
7
7
  import { pathToFileURL } from 'url';
8
8
  import { transform } from 'esbuild';
9
9
 
10
- import type { ScaffoldConfig } from '../schema';
10
+ import { SCAFFOLD_ROOT_DIR, type ScaffoldConfig } from '../schema';
11
11
  import { defaultLogger } from '../util/logger';
12
12
  import { ensureDirSync } from '../util/fs-utils';
13
13
 
14
14
  const logger = defaultLogger.child('[config]');
15
15
 
16
+
16
17
  export interface LoadScaffoldConfigOptions {
17
18
  /**
18
19
  * Optional explicit scaffold directory path (absolute or relative to cwd).
@@ -68,7 +69,7 @@ export async function loadScaffoldConfig(
68
69
  // First pass: figure out an initial scaffold dir just to locate config.*
69
70
  const initialScaffoldDir = options.scaffoldDir
70
71
  ? path.resolve(absCwd, options.scaffoldDir)
71
- : path.join(absCwd, 'scaffold');
72
+ : path.join(absCwd, SCAFFOLD_ROOT_DIR);
72
73
 
73
74
  const configPath =
74
75
  options.configPath ?? resolveConfigPath(initialScaffoldDir);
@@ -85,7 +86,7 @@ export async function loadScaffoldConfig(
85
86
  // Final scaffoldDir (can still be overridden by CLI)
86
87
  const scaffoldDir = options.scaffoldDir
87
88
  ? path.resolve(absCwd, options.scaffoldDir)
88
- : path.join(configRoot, 'scaffold');
89
+ : path.join(configRoot, SCAFFOLD_ROOT_DIR);
89
90
 
90
91
  // projectRoot (base) is relative to configRoot
91
92
  const baseRoot = config.base
@@ -4,6 +4,7 @@ import fs from 'fs';
4
4
  import path from 'path';
5
5
  import { ensureDirSync } from '../util/fs-utils';
6
6
  import { defaultLogger } from '../util/logger';
7
+ import { SCAFFOLD_ROOT_DIR } from '../schema';
7
8
 
8
9
  const logger = defaultLogger.child('[init]');
9
10
 
@@ -52,7 +53,10 @@ const config: ScaffoldConfig = {
52
53
  // base: 'src', // apply to <root>/src
53
54
  // base: '..', // apply to parent of <root>
54
55
  // base: '.',
55
-
56
+
57
+ // Number of spaces per indent level in structure files (default: 2).
58
+ // indentStep: 2,
59
+
56
60
  // Cache file path, relative to base.
57
61
  // cacheFile: '.scaffold-cache.json',
58
62
 
@@ -85,7 +89,8 @@ const config: ScaffoldConfig = {
85
89
  export default config;
86
90
  `;
87
91
 
88
- const DEFAULT_STRUCTURE_TXT = `# scaffold/structure.txt
92
+
93
+ const DEFAULT_STRUCTURE_TXT = `# ${SCAFFOLD_ROOT_DIR}/structure.txt
89
94
  # Example structure definition.
90
95
  # - Indent with 2 spaces per level
91
96
  # - Directories must end with "/"
@@ -113,7 +118,7 @@ export async function initScaffold(
113
118
  structurePath: string;
114
119
  created: { config: boolean; structure: boolean };
115
120
  }> {
116
- const scaffoldDirRel = options.scaffoldDir ?? 'scaffold';
121
+ const scaffoldDirRel = options.scaffoldDir ?? SCAFFOLD_ROOT_DIR;
117
122
  const scaffoldDirAbs = path.resolve(cwd, scaffoldDirRel);
118
123
  const configFileName = options.configFileName ?? 'config.ts';
119
124
  const structureFileName = options.structureFileName ?? 'structure.txt';