Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The `struct` CLI allows you to generate project structures from YAML configurati
**Basic Usage:**

```sh
struct {info,validate,generate,list,generate-schema,mcp,completion} ...
struct {info,validate,generate,list,generate-schema,mcp,completion,init} ...
```

## Global Options
Expand Down Expand Up @@ -120,6 +120,23 @@ struct completion install [bash|zsh|fish]
- If no shell is provided, the command attempts to auto-detect your current shell and prints the exact commands to enable argcomplete-based completion for struct.
- This does not modify your shell configuration; it only prints the commands you can copy-paste.

### `init`

Initialize a basic .struct.yaml in the target directory.

Usage:

```sh
struct init [path]
```

- Creates a .struct.yaml if it does not exist.
- Includes:
- pre_hooks/post_hooks with echo commands
- files with a README.md placeholder
- folders referencing github/workflows/run-struct at ./
- Non-destructive: if .struct.yaml already exists, it is not overwritten and a message is printed.

## Examples

### Basic Structure Generation
Expand Down
10 changes: 10 additions & 0 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ Or try a simple project structure:
struct generate project/nodejs ./my-node-app
```

## Bootstrap a new project

Start with a minimal .struct.yaml:

```sh
struct init
```

This writes a basic .struct.yaml with hooks, a README, and a reference to the run-struct workflow.

## Next Steps

- Learn about [YAML Configuration](configuration.md)
Expand Down
20 changes: 20 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,26 @@ The generated schema includes all available structures from both the built-in co

## Other Commands

### Initialize a project with .struct.yaml

Create a minimal .struct.yaml in the current directory:

```sh
struct init
```

Or specify a directory:

```sh
struct init ./my-project
```

The file includes:

- pre_hooks/post_hooks with echo commands
- A README.md placeholder in files
- A folders entry pointing to the github/workflows/run-struct workflow at ./

### Validate Configuration

```sh
Expand Down
52 changes: 52 additions & 0 deletions struct_module/commands/init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from struct_module.commands import Command
import os
import textwrap

BASIC_STRUCT_YAML = textwrap.dedent(
"""
# Generated by `struct init`
# Edit as needed to customize your project bootstrap

pre_hooks:
- echo "Starting struct generation"

post_hooks:
- echo "Struct generation completed"

files:
- README.md: |
# Project

Initialized with struct.

folders:
- ./:
struct:
- github/workflows/run-struct
"""
).lstrip()

class InitCommand(Command):
def __init__(self, parser):
super().__init__(parser)
parser.description = "Initialize a basic .struct.yaml in the target directory"
parser.add_argument('path', nargs='?', default='.', help='Directory to initialize (default: current directory)')
parser.set_defaults(func=self.execute)

def execute(self, args):
base_path = os.path.abspath(args.path or '.')
target = os.path.join(base_path, '.struct.yaml')

os.makedirs(base_path, exist_ok=True)

# If file exists, do not overwrite without explicit confirmation behavior (keep simple: skip)
if os.path.exists(target):
self.logger.info(f".struct.yaml already exists at {target}, skipping creation")
print(f"⚠️ .struct.yaml already exists at: {target}")
return

with open(target, 'w') as f:
f.write(BASIC_STRUCT_YAML)

print("✅ Created .struct.yaml")
print(f" - {target}")
4 changes: 4 additions & 0 deletions struct_module/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ def main():
GenerateSchemaCommand(subparsers.add_parser('generate-schema', help='Generate JSON schema for available structures'))
MCPCommand(subparsers.add_parser('mcp', help='MCP (Model Context Protocol) support'))

# init to create a basic .struct.yaml
from struct_module.commands.init import InitCommand
InitCommand(subparsers.add_parser('init', help='Initialize a basic .struct.yaml in the target directory'))

# completion installer
from struct_module.commands.completion import CompletionCommand
CompletionCommand(subparsers.add_parser('completion', help='Manage shell completions'))
Expand Down
48 changes: 48 additions & 0 deletions tests/test_init_command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import argparse
import os
from unittest.mock import patch

from struct_module.commands.init import InitCommand, BASIC_STRUCT_YAML


def test_init_creates_struct_yaml(tmp_path):
parser = argparse.ArgumentParser()
cmd = InitCommand(parser)

target_dir = tmp_path / "proj"
args = parser.parse_args([str(target_dir)])

with patch('builtins.print') as mock_print:
cmd.execute(args)

struct_file = target_dir / '.struct.yaml'
assert struct_file.exists()

content = struct_file.read_text()
# Basic checks for key sections
assert 'pre_hooks:' in content
assert 'post_hooks:' in content
assert 'files:' in content
assert 'README.md' in content
assert 'folders:' in content
assert 'github/workflows/run-struct' in content


def test_init_skips_if_exists(tmp_path):
parser = argparse.ArgumentParser()
cmd = InitCommand(parser)

target_dir = tmp_path / "proj"
target_dir.mkdir(parents=True)
existing = target_dir / '.struct.yaml'
existing.write_text('files: []\n')

args = parser.parse_args([str(target_dir)])

with patch('builtins.print') as mock_print:
cmd.execute(args)
# Should not overwrite existing file
assert existing.read_text() == 'files: []\n'
# Should print a message about skipping
printed = "\n".join(c.args[0] for c in mock_print.call_args_list)
assert '.struct.yaml already exists' in printed
Loading