CLI tool to create folder structures from Markdown tree representations.
It behaves like mkdir -p and touch combined, creating directories and files as needed. It can be used to quickly scaffold a project structure or generate placeholder files.
- Accepts tree strings in box-drawing (
├──,└──,│) or indentation (spaces) format - Trailing slash
/marks a directory; no trailing/marks a file - Symlink creation with
link-name -> targetsyntax - Inline comments stripped automatically (
# ...,// ...,<- ...,← ...) --dry-run(-n) parses and validates without touching the file system- Prints a summary line on success (
✓ Done. N items created.);--verboseadds per-item detail - Path traversal protection — no file, folder, or symlink target can escape the target directory
- Importable as a Node.js library with full TypeScript types
- Pipable interface to run as
echo tree | touch-allin CI or scripts
npm install -g touch-allor with npx without installing:
npx touch-all@latest "..."touch-all "
my-project/
├── src/
│ ├── index.ts
│ └── index.test.ts
├── package.json
└── README.md
"--path,-p– specifies target directory. By default, the current working directory is used. Can be an absolute path or a path relative to the current working directory.
touch-all "..." --path=./my-project
touch-all "..." -p ~/Documents/my-project--dry-run,-n– parses and validates the tree string without creating any files or directories. Useful for testing and debugging.
touch-all "..." --dry-run
touch-all "..." -n--verbose,-v– prints every created path to the console. Useful for seeing exactly what will be created, especially with complex structures. By default only a summary line is printed on success (✓ Done. N items created.).--verboseadds per-item detail.
touch-all "..." --verbose
touch-all "..." -v--yes,-y– skips the confirmation prompt when symlinks point outside the project root. Required in non-interactive environments (scripts, CI).
touch-all "..." --yes
touch-all "..." -y--completions– generates a completion script for a specific shell. Supported shells:sh,bash,fish,zsh.--log-level– sets the minimum log level for a command. Supported levels:all,trace,debug,info,warning,error,fatal,none. The default log level iswarning.--help,-h– shows the help documentation for a command.--wizard– starts wizard mode for a command, providing an interactive step-by-step interface.--version– shows the version of the application.
my-project/
├── .config/
│ ├── tsconfig.json
│ └── vite.config.ts
├── src/
│ ├── index.ts
│ └── index.test.ts
├── package.json
└── README.md
my-project/
.config/
tsconfig.json
vite.config.ts
src/
index.ts
index.test.ts
package.json
README.md
Both formats produce identical results.
| Syntax | Meaning |
|---|---|
name/ |
directory |
name |
file |
dir/sub/ |
directory at an explicit subpath |
dir/sub/file.ts |
file at an explicit subpath |
... # comment |
ignored (stripped) |
... // comment |
ignored (stripped) |
... <- comment |
ignored (stripped) |
... ← comment |
ignored (stripped) |
name -> target |
symlink pointing to target |
Items at the root level (no indentation / no parent) are created directly inside the target directory.
Use link-name -> target to create a symlink. The target is passed as-is to the OS — use paths relative to the symlink's location, just as you would in a shell.
my-project/
├─ src/
│ ├─ index.ts
│ └─ utils -> ../shared/utils.ts # symlink to a sibling directory
└─ shared/
└─ utils.ts
If target ends with /, the symlink is created as a directory symlink (relevant on Windows). The link name's suffix is ignored.
Warning
If any symlink target resolves outside the project root (--path), touch-all will prompt for confirmation before proceeding. Use --yes to skip the prompt in scripts or CI.
When using fileStructureCreator directly as a library, outside-root symlinks are rejected by default with a PathTraversalError. Pass { allowOutsideSymlinks: true } as the third argument to allow them.
npm install touch-allimport {
parserFolderStructure,
fileStructureCreator,
resolveProjectPathToBase,
isSymlinkOutsideRoot,
PathTraversalError,
} from 'touch-all'
import type { ParserResult, ParserResultLineItem } from 'touch-all'Parses a tree string into a flat list of items. Pure function, no I/O.
Each item is one of:
type ParserResultLineItem =
| { type: 'file'; path: string }
| { type: 'folder'; path: string }
| { type: 'symlink'; path: string; target: string }const items = parserFolderStructure(`
src/
index.ts
link -> ../shared.ts
`)
// [
// { type: 'folder', path: 'src' },
// { type: 'file', path: 'src/index.ts' },
// { type: 'symlink', path: 'src/link', target: '../shared.ts' },
// ]Creates the parsed structure on disk under basePath. Returns an Effect.
By default, symlinks whose targets resolve outside basePath are rejected with PathTraversalError. Pass { allowOutsideSymlinks: true } to allow them.
import { Effect } from 'effect'
import { NodeContext, NodeRuntime } from '@effect/platform-node'
const projectDirectory = '/absolute/target/path'
const items = parserFolderStructure(tree)
// Default: outside-root symlinks are rejected
fileStructureCreator(items, projectDirectory).pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain)
// Opt out of symlink containment check
fileStructureCreator(items, projectDirectory, { allowOutsideSymlinks: true }).pipe(
Effect.provide(NodeContext.layer),
NodeRuntime.runMain
)Pure function that returns true if a symlink target resolves outside basePath. Useful for pre-validating items before passing them to fileStructureCreator.
import { Path } from '@effect/platform'
import { Effect } from 'effect'
import { NodeContext } from '@effect/platform-node'
import { isSymlinkOutsideRoot } from 'touch-all'
Effect.gen(function* () {
const path = yield* Path.Path
const outside = isSymlinkOutsideRoot('src/link', '../../etc/passwd', '/project', path)
// true — target escapes /project
}).pipe(Effect.provide(NodeContext.layer))Resolves projectPath relative to basePath and rejects any path that would escape basePath (path traversal protection).
Warning
projectPath cannot traverse outside of basePath. If projectPath is absolute, it treated as relative to basePath. If projectPath is relative, it is resolved against basePath. In either case, if the resulting path is outside of basePath, a PathTraversalError is thrown.
| Class | _tag |
When thrown |
|---|---|---|
PathTraversalError |
'PathTraversalError' |
A file/folder path or symlink target escapes basePath (unless opted out) |