Skip to content

Commit 53493b8

Browse files
committed
feat: add workflow init command for project onboarding (#42)
- New command: workflow init - Detects AGENTS.md/README.md/CONTRIBUTING.md - Adds workflow-as-list documentation - Creates .workflow-as-list/config.ini - Updates .gitignore - Creates workflow/README.md - Options: --docs-only, --config-only, --force NOTE: 217 lines (under 256 limit)
1 parent f65c441 commit 53493b8

File tree

8 files changed

+268
-41
lines changed

8 files changed

+268
-41
lines changed

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,12 @@ Thumbs.db
5050
# Local overrides (allowed)
5151
.env.local
5252
~/
53+
54+
# workflow-as-list cache
55+
.imports/
56+
.workflow-as-list/
57+
58+
# workflow-as-list cache
59+
.imports/
60+
.workflow-as-list/
61+

.tmp/traceflux-plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!-- .tmp/traceflux-plan.md -->
12
# Traceflux Bootstrap Plan
23

34
**Status**: Temporary (delete after completion)

AGENTS.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,26 @@ Test files:
129129

130130
Last Updated: 2026-03-12
131131
Maintainer: Tracer (迹)
132+
133+
## Workflow Automation
134+
135+
This project uses workflow-as-list for process automation.
136+
137+
NOTE: For `.workflow.list` files:
138+
- Documentation: `workflow --help`
139+
- Execute: `workflow check/run/exec <workflow-name>`
140+
- Import caching: Automatic (`.imports/`)
141+
142+
Available workflows:
143+
```bash
144+
workflow list
145+
```
146+
147+
Quick start:
148+
```bash
149+
workflow check <name> # Validate
150+
workflow run <name> # Execute
151+
workflow exec read <id> # Read step
152+
workflow exec next <id> # Advance
153+
```
154+

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!-- CHANGELOG.md -->
12
# CHANGELOG
23

34

src/workflow_as_list/cli/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .approve import approve
1212
from .check import check
1313
from .exec import app as exec_app
14+
from .init import init
1415
from .list import list_workflows
1516
from .reject import reject
1617
from .run import run
@@ -27,6 +28,7 @@
2728
app.command()(run)
2829
app.command(name="list")(list_workflows)
2930
app.command()(show)
31+
app.command()(init)
3032
app.add_typer(exec_app, name="exec")
3133
app.add_typer(server_app, name="server")
3234

src/workflow_as_list/cli/init.py

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
# src/workflow_as_list/cli/init.py
2+
"""workflow init command - Initialize workflow-as-list integration.
3+
4+
REFERENCE: #42 - workflow init command for project onboarding
5+
"""
6+
7+
import re
8+
from pathlib import Path
9+
10+
import typer
11+
from rich.console import Console
12+
13+
from ..constants import ensure_directories
14+
15+
console = Console()
16+
17+
# Templates moved to reduce file size
18+
WORKFLOW_DOCS_SECTION = """
19+
## Workflow Automation
20+
21+
This project uses workflow-as-list for process automation.
22+
23+
NOTE: For `.workflow.list` files, use `workflow --help` or execute:
24+
workflow check/run/exec <workflow-name>
25+
26+
Quick start:
27+
workflow list # List available workflows
28+
workflow check <name> # Validate workflow
29+
workflow run <name> # Start execution
30+
"""
31+
32+
CONFIG_TEMPLATE = """[workflow]
33+
project_name = {project_name}
34+
cache_dir = .imports
35+
state_dir = .workflow-as-list/state
36+
"""
37+
38+
GITIGNORE_ENTRIES = """
39+
# workflow-as-list cache
40+
.imports/
41+
.workflow-as-list/
42+
"""
43+
44+
WORKFLOW_README_TEMPLATE = """# Project Workflows
45+
46+
Workflows manage {project_name} development.
47+
48+
Usage:
49+
workflow check workflow/<name>
50+
workflow run workflow/<name>
51+
52+
NOTE: Import caching is automatic (see .imports/)
53+
"""
54+
55+
56+
def print_output(type: str, message: str):
57+
"""Print formatted output."""
58+
styles = {"INFO": "blue", "SUCCESS": "green", "WARNING": "yellow", "ERROR": "red"}
59+
console.print(f"[{styles.get(type, 'white')}]{type}[/] {message}")
60+
61+
62+
def get_project_name() -> str:
63+
"""Get project name from pyproject.toml or directory."""
64+
pyproject = Path("pyproject.toml")
65+
if pyproject.exists():
66+
match = re.search(r'name\s*=\s*"([^"]+)"', pyproject.read_text())
67+
if match:
68+
return match.group(1)
69+
return Path.cwd().name
70+
71+
72+
def find_docs_file() -> Path | None:
73+
"""Find AGENTS.md, README.md, or CONTRIBUTING.md."""
74+
for name in ["AGENTS.md", "README.md", "CONTRIBUTING.md"]:
75+
path = Path(name)
76+
if path.exists():
77+
return path
78+
return None
79+
80+
81+
def is_initialized() -> bool:
82+
"""Check if already initialized."""
83+
docs_file = find_docs_file()
84+
if docs_file and "workflow-as-list" in docs_file.read_text().lower():
85+
return True
86+
return Path(".workflow-as-list/config.ini").exists()
87+
88+
89+
def update_docs(force: bool = False) -> bool:
90+
"""Add workflow-as-list section to docs."""
91+
docs_file = find_docs_file()
92+
if not docs_file:
93+
print_output("WARNING", "No AGENTS.md/README.md/CONTRIBUTING.md found")
94+
return False
95+
96+
content = docs_file.read_text()
97+
if "workflow-as-list" in content.lower() and not force:
98+
print_output("INFO", f"Already initialized: {docs_file.name}")
99+
return False
100+
101+
# Find insertion point
102+
insertion_point = len(content)
103+
for pattern, pos in [
104+
(r"(## Getting Started\n)", "after"),
105+
(r"(## Development\n)", "before"),
106+
(r"(## Setup\n)", "after"),
107+
]:
108+
match = re.search(pattern, content)
109+
if match:
110+
insertion_point = match.end() if pos == "after" else match.start()
111+
break
112+
113+
docs_file.write_text(
114+
content[:insertion_point] + WORKFLOW_DOCS_SECTION + content[insertion_point:]
115+
)
116+
print_output("SUCCESS", f"Updated {docs_file.name}")
117+
return True
118+
119+
120+
def create_config(force: bool = False) -> bool:
121+
"""Create .workflow-as-list/config.ini."""
122+
config_path = Path(".workflow-as-list/config.ini")
123+
if config_path.exists() and not force:
124+
print_output("INFO", f"Config exists: {config_path}")
125+
return False
126+
config_path.parent.mkdir(exist_ok=True)
127+
config_path.write_text(CONFIG_TEMPLATE.format(project_name=get_project_name()))
128+
print_output("SUCCESS", f"Created {config_path}")
129+
return True
130+
131+
132+
def update_gitignore(force: bool = False) -> bool:
133+
"""Add cache dirs to .gitignore."""
134+
gitignore_path = Path(".gitignore")
135+
if gitignore_path.exists():
136+
content = gitignore_path.read_text()
137+
if ".imports/" in content and ".workflow-as-list/" in content and not force:
138+
print_output("INFO", ".gitignore already updated")
139+
return False
140+
gitignore_path.write_text(content.rstrip() + "\n\n" + GITIGNORE_ENTRIES)
141+
else:
142+
gitignore_path.write_text(GITIGNORE_ENTRIES)
143+
print_output("SUCCESS", f"Updated {gitignore_path.name}")
144+
return True
145+
146+
147+
def create_workflow_readme(force: bool = False) -> bool:
148+
"""Create workflow/README.md if workflow/ exists."""
149+
workflow_dir = Path("workflow")
150+
if not workflow_dir.is_dir():
151+
return False
152+
readme_path = workflow_dir / "README.md"
153+
if readme_path.exists() and not force:
154+
print_output("INFO", "workflow/README.md exists")
155+
return False
156+
readme_path.write_text(
157+
WORKFLOW_README_TEMPLATE.format(project_name=get_project_name())
158+
)
159+
print_output("SUCCESS", "Created workflow/README.md")
160+
return True
161+
162+
163+
def init(
164+
docs_only: bool = typer.Option(
165+
False, "--docs-only", help="Only update documentation"
166+
),
167+
config_only: bool = typer.Option(
168+
False, "--config-only", help="Only create config files"
169+
),
170+
force: bool = typer.Option(False, "--force", help="Overwrite existing files"),
171+
):
172+
"""Initialize workflow-as-list integration for a project.
173+
174+
Adds workflow-as-list documentation to AGENTS.md or README.md,
175+
creates configuration files, and updates .gitignore.
176+
177+
Example:
178+
workflow init
179+
workflow init --docs-only
180+
workflow init --force
181+
"""
182+
# Ensure directories exist
183+
ensure_directories()
184+
185+
# Check if running in a project directory
186+
docs_file = find_docs_file()
187+
if not docs_file and not (docs_only or config_only):
188+
print_output("ERROR", "No AGENTS.md, README.md, or CONTRIBUTING.md found")
189+
print_output("ERROR", "Current directory does not appear to be a project")
190+
print_output("INFO", "Create a README.md or AGENTS.md file first")
191+
raise typer.Exit(1)
192+
193+
# Check if already initialized
194+
if is_initialized() and not force:
195+
print_output("WARNING", "Project already initialized with workflow-as-list")
196+
print_output("INFO", "Use --force to re-initialize")
197+
198+
print_output("INFO", "Initializing workflow-as-list integration...")
199+
200+
success = False
201+
202+
if docs_only:
203+
success = update_docs(force)
204+
elif config_only:
205+
create_config(force)
206+
update_gitignore(force)
207+
create_workflow_readme(force)
208+
success = True
209+
else:
210+
# Full initialization
211+
success = update_docs(force)
212+
create_config(force)
213+
update_gitignore(force)
214+
create_workflow_readme(force)
215+
216+
if success:
217+
print_output(
218+
"SUCCESS", "Project initialized. Run 'workflow list' to see workflows."
219+
)
220+
else:
221+
print_output("WARNING", "Initialization completed with warnings")

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

workflow/README.md

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,17 @@
1-
# Self-Hosted Workflows
1+
<!-- workflow/README.md -->
2+
# Project Workflows
23

3-
Purpose: Use workflow-as-list to develop workflow-as-list.
4-
5-
Not here: Generic templates → `examples/`
6-
7-
---
8-
9-
## Mapping
10-
11-
Format: `workflow/<name>.workflow.list`
12-
13-
- `<name>`: Workflow identifier (single segment)
14-
15-
Rule: Flat structure, routed by `main.workflow.list` (TODO).
16-
17-
---
18-
19-
## Constraints
20-
21-
1. **Project-Specific** — Binds to workflow-as-list
22-
2. **Evolving** — Changes with project needs
23-
3. **Validated** — Must be used in real work
24-
25-
---
26-
27-
## Usage
4+
Workflows in this directory manage workflow-as-list development.
285

6+
**Usage:**
297
```bash
308
workflow check workflow/<name>
31-
workflow approve <name>
32-
workflow run <name>
9+
workflow run workflow/<name>
3310
```
3411

35-
Query structure: `ls workflow/`
36-
37-
---
38-
39-
## Philosophy
40-
41-
If we want others to trust our rules, we must use them ourselves.
42-
43-
See: `examples/README.md` (template extraction)
44-
45-
---
12+
**Import caching:**
13+
- Automatic on first execution
14+
- Cache location: `.imports/`
15+
- Verification: SHA-256 hash
4616

47-
Last Updated: 2026-03-13
17+
See: workflow-as-list documentation

0 commit comments

Comments
 (0)