1st commit
This commit is contained in:
parent
ade5f91ad7
commit
67a4415600
|
|
@ -2,3 +2,4 @@
|
||||||
.idea/
|
.idea/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
install/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Project Guide (CascadingDev)
|
||||||
|
|
||||||
|
## Daily flow
|
||||||
|
1. Work under `Docs/features/FR_*/...`
|
||||||
|
2. Commit; the pre-commit hook ensures `*.discussion.sum.md` and (later) updates summaries/diagrams.
|
||||||
|
3. Use `Docs/features/.../discussions/*.discussion.md` for stage talks.
|
||||||
|
End each comment with `VOTE: READY|CHANGES|REJECT`.
|
||||||
|
|
||||||
|
## Start a new feature
|
||||||
|
- Create `Docs/features/FR_YYYY-MM-DD_<slug>/request.md` from the template.
|
||||||
|
- Commit; the system seeds stage discussions and summaries automatically.
|
||||||
|
|
||||||
|
## First run
|
||||||
|
- After installation, make an initial commit to activate the hook:
|
||||||
|
```bash
|
||||||
|
git add .
|
||||||
|
git commit -m "chore: initial commit"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Keep discussions append-only.
|
||||||
|
- Human READY is required at Implementation/Release.
|
||||||
|
- Ramble GUI (`ramble.py`) is optional; use it to seed the first feature.
|
||||||
|
|
@ -148,7 +148,12 @@ CascadingDev/
|
||||||
│ └─ runtime/ramble.py
|
│ └─ runtime/ramble.py
|
||||||
├─ tools/build_installer.py # creates install/cascadingdev-<version>/
|
├─ tools/build_installer.py # creates install/cascadingdev-<version>/
|
||||||
├─ install/ # build output (git-ignored)
|
├─ install/ # build output (git-ignored)
|
||||||
│ └─ cascadingdev-<version>/ # unzip + run bundle (setup_cascadingdev.py inside)
|
│ └─ cascadingdev-<version>/ # self-contained installer bundle
|
||||||
|
│ ├─ setup_cascadingdev.py
|
||||||
|
│ ├─ assets/ # shipped templates + hooks
|
||||||
|
│ ├─ ramble.py
|
||||||
|
│ ├─ INSTALL.md # how to run the installer
|
||||||
|
│ └─ VERSION
|
||||||
├─ docs
|
├─ docs
|
||||||
│ └─ DESIGN.md
|
│ └─ DESIGN.md
|
||||||
├─ VERSION # semantic version of CascadingDev
|
├─ VERSION # semantic version of CascadingDev
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
# CascadingDev Installer
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- Python 3.10+ and git
|
||||||
|
- (Optional) PySide6 for GUI (`pip install PySide6`)
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
```bash
|
||||||
|
|
||||||
|
python setup_cascadingdev.py --target /path/to/new-project
|
||||||
|
```
|
||||||
|
|
||||||
|
### Skip GUI
|
||||||
|
```bash
|
||||||
|
python setup_cascadingdev.py --target /path/to/new-project --no-ramble
|
||||||
|
```
|
||||||
|
|
||||||
|
> After installation, open `USER_GUIDE.md` in your new project for daily usage.
|
||||||
|
|
||||||
|
## Rebuild & Run (for maintainers)
|
||||||
|
Rebuild the bundle every time you change assets/ or the installer:
|
||||||
|
```bash
|
||||||
|
python tools/build_installer.py
|
||||||
|
```
|
||||||
|
Then run only the bundled copy:
|
||||||
|
```bash
|
||||||
|
python install/cascadingdev-*/setup_cascadingdev.py --target /path/to/new-project
|
||||||
|
```
|
||||||
|
|
@ -22,50 +22,69 @@ import subprocess
|
||||||
import datetime
|
import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
INSTALL_ROOT = Path(__file__).resolve().parent.parent # installer root (contains this scripts/ dir)
|
# The root directory of the installer package (contains scripts/, assets/, etc.)
|
||||||
|
INSTALL_ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
# ---------- Helper Functions ----------
|
||||||
|
|
||||||
# ---------- helpers ----------
|
|
||||||
def sh(cmd, check=True, cwd=None):
|
def sh(cmd, check=True, cwd=None):
|
||||||
|
"""Run a shell command and return the completed process."""
|
||||||
return subprocess.run(cmd, check=check, text=True, capture_output=True, cwd=cwd)
|
return subprocess.run(cmd, check=check, text=True, capture_output=True, cwd=cwd)
|
||||||
|
|
||||||
def say(msg): print(msg, flush=True)
|
def say(msg):
|
||||||
|
"""Print a message with immediate flush."""
|
||||||
|
print(msg, flush=True)
|
||||||
|
|
||||||
def write_if_missing(path: Path, content: str):
|
def write_if_missing(path: Path, content: str):
|
||||||
|
"""Write content to a file only if it doesn't already exist."""
|
||||||
path.parent.mkdir(parents=True, exist_ok=True)
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
path.write_text(content, encoding="utf-8")
|
path.write_text(content, encoding="utf-8")
|
||||||
|
|
||||||
def copy_if_exists(src: Path, dst: Path):
|
def copy_if_exists(src: Path, dst: Path):
|
||||||
|
"""Copy a file from source to destination if the source exists."""
|
||||||
if src.exists():
|
if src.exists():
|
||||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||||
shutil.copy2(str(src), str(dst))
|
shutil.copy2(str(src), str(dst))
|
||||||
|
|
||||||
def ensure_git_repo(target: Path):
|
def ensure_git_repo(target: Path):
|
||||||
|
"""Initialize a git repository if one doesn't exist at the target path."""
|
||||||
if not (target / ".git").exists():
|
if not (target / ".git").exists():
|
||||||
|
# Initialize git repo with main branch
|
||||||
sh(["git", "init", "-b", "main"], cwd=str(target))
|
sh(["git", "init", "-b", "main"], cwd=str(target))
|
||||||
|
# Create basic .gitignore file
|
||||||
write_if_missing(target / ".gitignore", "\n".join([
|
write_if_missing(target / ".gitignore", "\n".join([
|
||||||
".env", ".env.*", "secrets/", ".git/ai-rules-*", "__pycache__/",
|
".env", ".env.*", "secrets/", ".git/ai-rules-*", "__pycache__/",
|
||||||
"*.pyc", ".pytest_cache/", ".DS_Store",
|
"*.pyc", ".pytest_cache/", ".DS_Store",
|
||||||
]) + "\n")
|
]) + "\n")
|
||||||
|
|
||||||
def install_precommit_hook(target: Path):
|
def install_precommit_hook(target: Path):
|
||||||
|
"""Install the pre-commit hook from installer assets to target git hooks."""
|
||||||
hook_src = INSTALL_ROOT / "assets" / "hooks" / "pre-commit"
|
hook_src = INSTALL_ROOT / "assets" / "hooks" / "pre-commit"
|
||||||
hooks_dir = target / ".git" / "hooks"
|
hooks_dir = target / ".git" / "hooks"
|
||||||
hooks_dir.mkdir(parents=True, exist_ok=True)
|
hooks_dir.mkdir(parents=True, exist_ok=True)
|
||||||
hook_dst = hooks_dir / "pre-commit"
|
hook_dst = hooks_dir / "pre-commit"
|
||||||
|
|
||||||
if not hook_src.exists():
|
if not hook_src.exists():
|
||||||
say("[-] pre-commit hook source missing at scripts/hooks/pre-commit in the installer.")
|
say("[-] pre-commit hook source missing at scripts/hooks/pre-commit in the installer.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Copy hook content and make it executable
|
||||||
hook_dst.write_text(hook_src.read_text(encoding="utf-8"), encoding="utf-8")
|
hook_dst.write_text(hook_src.read_text(encoding="utf-8"), encoding="utf-8")
|
||||||
hook_dst.chmod(0o755)
|
hook_dst.chmod(0o755)
|
||||||
say(f"[+] Installed git hook → {hook_dst}")
|
say(f"[+] Installed git hook → {hook_dst}")
|
||||||
|
|
||||||
def run_ramble_and_collect(target: Path, provider: str = "mock", claude_cmd: str = "claude"):
|
def run_ramble_and_collect(target: Path, provider: str = "mock", claude_cmd: str = "claude"):
|
||||||
|
"""
|
||||||
|
Launch Ramble GUI to collect initial feature request details.
|
||||||
|
Falls back to terminal prompts if GUI fails or returns invalid JSON.
|
||||||
|
"""
|
||||||
ramble = target / "ramble.py"
|
ramble = target / "ramble.py"
|
||||||
if not ramble.exists():
|
if not ramble.exists():
|
||||||
say("[-] ramble.py not found in target; skipping interactive FR capture.")
|
say("[-] ramble.py not found in target; skipping interactive FR capture.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Build Ramble command arguments
|
||||||
args = [
|
args = [
|
||||||
sys.executable, str(ramble),
|
sys.executable, str(ramble),
|
||||||
"--provider", provider,
|
"--provider", provider,
|
||||||
|
|
@ -74,13 +93,16 @@ def run_ramble_and_collect(target: Path, provider: str = "mock", claude_cmd: str
|
||||||
"--fields", "Summary", "Title", "Intent", "ProblemItSolves", "BriefOverview",
|
"--fields", "Summary", "Title", "Intent", "ProblemItSolves", "BriefOverview",
|
||||||
"--criteria", '{"Summary":"<= 2 sentences","Title":"camelCase, <= 24 chars"}'
|
"--criteria", '{"Summary":"<= 2 sentences","Title":"camelCase, <= 24 chars"}'
|
||||||
]
|
]
|
||||||
|
|
||||||
say("[•] Launching Ramble (close the dialog with Submit to return JSON)…")
|
say("[•] Launching Ramble (close the dialog with Submit to return JSON)…")
|
||||||
proc = subprocess.run(args, text=True, capture_output=True, cwd=str(target))
|
proc = subprocess.run(args, text=True, capture_output=True, cwd=str(target))
|
||||||
|
|
||||||
|
# Show any stderr output from Ramble
|
||||||
if proc.stderr and proc.stderr.strip():
|
if proc.stderr and proc.stderr.strip():
|
||||||
say("[Ramble stderr]")
|
say("[Ramble stderr]")
|
||||||
say(proc.stderr.strip())
|
say(proc.stderr.strip())
|
||||||
|
|
||||||
|
# Try to parse JSON output from Ramble
|
||||||
out = (proc.stdout or "").strip()
|
out = (proc.stdout or "").strip()
|
||||||
if out:
|
if out:
|
||||||
try:
|
try:
|
||||||
|
|
@ -88,7 +110,7 @@ def run_ramble_and_collect(target: Path, provider: str = "mock", claude_cmd: str
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
say(f"[-] JSON parse failed: {e}")
|
say(f"[-] JSON parse failed: {e}")
|
||||||
|
|
||||||
# Terminal fallback so setup can proceed without GUI deps
|
# Terminal fallback - collect input manually if GUI fails
|
||||||
say("[!] Falling back to terminal prompts.")
|
say("[!] Falling back to terminal prompts.")
|
||||||
def ask(label, default=""):
|
def ask(label, default=""):
|
||||||
try:
|
try:
|
||||||
|
|
@ -97,6 +119,7 @@ def run_ramble_and_collect(target: Path, provider: str = "mock", claude_cmd: str
|
||||||
except EOFError:
|
except EOFError:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
# Collect required fields via terminal input
|
||||||
fields = {
|
fields = {
|
||||||
"Title": ask("Title (camelCase, <=24 chars)", "initialProjectDesign"),
|
"Title": ask("Title (camelCase, <=24 chars)", "initialProjectDesign"),
|
||||||
"Intent": ask("Intent", "—"),
|
"Intent": ask("Intent", "—"),
|
||||||
|
|
@ -107,10 +130,15 @@ def run_ramble_and_collect(target: Path, provider: str = "mock", claude_cmd: str
|
||||||
return {"fields": fields, "summary": fields.get("Summary", "")}
|
return {"fields": fields, "summary": fields.get("Summary", "")}
|
||||||
|
|
||||||
def seed_process_and_rules(target: Path):
|
def seed_process_and_rules(target: Path):
|
||||||
|
"""Create the standard folder structure, templates, and configuration files."""
|
||||||
|
|
||||||
|
# Create basic process documentation files
|
||||||
write_if_missing(target / "process" / "design.md",
|
write_if_missing(target / "process" / "design.md",
|
||||||
"# Process & Architecture (Local Notes)\n\n(See DESIGN.md for full spec.)\n")
|
"# Process & Architecture (Local Notes)\n\n(See DESIGN.md for full spec.)\n")
|
||||||
write_if_missing(target / "process" / "policies.md",
|
write_if_missing(target / "process" / "policies.md",
|
||||||
"# Policies (Human-readable)\n\nSee machine-readable config in policies.yml.\n")
|
"# Policies (Human-readable)\n\nSee machine-readable config in policies.yml.\n")
|
||||||
|
|
||||||
|
# Create machine-readable policies configuration
|
||||||
write_if_missing(target / "process" / "policies.yml",
|
write_if_missing(target / "process" / "policies.yml",
|
||||||
"""version: 1
|
"""version: 1
|
||||||
voting:
|
voting:
|
||||||
|
|
@ -129,6 +157,7 @@ etiquette:
|
||||||
response_timeout_hours: 24
|
response_timeout_hours: 24
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
# Create template files for different document types
|
||||||
tmpl_dir = target / "process" / "templates"
|
tmpl_dir = target / "process" / "templates"
|
||||||
write_if_missing(tmpl_dir / "feature_request.md",
|
write_if_missing(tmpl_dir / "feature_request.md",
|
||||||
"# Feature Request: <title>\n\n**Intent**: …\n**Motivation / Problem**: …\n**Constraints / Non-Goals**: …\n**Rough Proposal**: …\n**Open Questions**: …\n")
|
"# Feature Request: <title>\n\n**Intent**: …\n**Motivation / Problem**: …\n**Constraints / Non-Goals**: …\n**Rough Proposal**: …\n**Open Questions**: …\n")
|
||||||
|
|
@ -137,6 +166,7 @@ etiquette:
|
||||||
write_if_missing(tmpl_dir / "design_doc.md",
|
write_if_missing(tmpl_dir / "design_doc.md",
|
||||||
"# Design — <FR id / Title>\n\n## Context & Goals\n## Non-Goals & Constraints\n## Options Considered\n## Decision & Rationale\n## Architecture Diagram(s)\n## Risks & Mitigations\n## Acceptance Criteria\n")
|
"# Design — <FR id / Title>\n\n## Context & Goals\n## Non-Goals & Constraints\n## Options Considered\n## Decision & Rationale\n## Architecture Diagram(s)\n## Risks & Mitigations\n## Acceptance Criteria\n")
|
||||||
|
|
||||||
|
# Create AI rules configuration for general markdown files
|
||||||
write_if_missing(target / ".ai-rules.yml",
|
write_if_missing(target / ".ai-rules.yml",
|
||||||
"""version: 1
|
"""version: 1
|
||||||
file_associations:
|
file_associations:
|
||||||
|
|
@ -152,6 +182,7 @@ settings:
|
||||||
temperature: 0.1
|
temperature: 0.1
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
# Create AI rules specific to feature discussions
|
||||||
write_if_missing(target / "Docs" / "features" / ".ai-rules.yml",
|
write_if_missing(target / "Docs" / "features" / ".ai-rules.yml",
|
||||||
"""version: 1
|
"""version: 1
|
||||||
file_associations:
|
file_associations:
|
||||||
|
|
@ -178,11 +209,13 @@ rules:
|
||||||
""")
|
""")
|
||||||
|
|
||||||
def seed_initial_feature(target: Path, req_fields: dict | None):
|
def seed_initial_feature(target: Path, req_fields: dict | None):
|
||||||
|
"""Create the initial feature request and associated discussion files."""
|
||||||
today = datetime.date.today().isoformat()
|
today = datetime.date.today().isoformat()
|
||||||
fr_dir = target / "Docs" / "features" / f"FR_{today}_initial-feature-request"
|
fr_dir = target / "Docs" / "features" / f"FR_{today}_initial-feature-request"
|
||||||
disc_dir = fr_dir / "discussions"
|
disc_dir = fr_dir / "discussions"
|
||||||
disc_dir.mkdir(parents=True, exist_ok=True)
|
disc_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Create feature request content, using Ramble data if available
|
||||||
if req_fields:
|
if req_fields:
|
||||||
title = (req_fields.get("fields", {}) or {}).get("Title", "").strip() or "initialProjectDesign"
|
title = (req_fields.get("fields", {}) or {}).get("Title", "").strip() or "initialProjectDesign"
|
||||||
intent = (req_fields.get("fields", {}) or {}).get("Intent", "").strip() or "—"
|
intent = (req_fields.get("fields", {}) or {}).get("Intent", "").strip() or "—"
|
||||||
|
|
@ -199,10 +232,12 @@ def seed_initial_feature(target: Path, req_fields: dict | None):
|
||||||
**Meta**: Created: {today}
|
**Meta**: Created: {today}
|
||||||
"""
|
"""
|
||||||
else:
|
else:
|
||||||
|
# Fallback to template content if no Ramble data
|
||||||
body = (target / "process" / "templates" / "feature_request.md").read_text(encoding="utf-8")
|
body = (target / "process" / "templates" / "feature_request.md").read_text(encoding="utf-8")
|
||||||
|
|
||||||
(fr_dir / "request.md").write_text(body, encoding="utf-8")
|
(fr_dir / "request.md").write_text(body, encoding="utf-8")
|
||||||
|
|
||||||
|
# Create initial discussion file
|
||||||
(disc_dir / "feature.discussion.md").write_text(
|
(disc_dir / "feature.discussion.md").write_text(
|
||||||
f"""---
|
f"""---
|
||||||
type: discussion
|
type: discussion
|
||||||
|
|
@ -218,6 +253,7 @@ Initial discussion for the first feature request. Append your comments below.
|
||||||
- Maintainer: Kickoff. VOTE: READY
|
- Maintainer: Kickoff. VOTE: READY
|
||||||
""", encoding="utf-8")
|
""", encoding="utf-8")
|
||||||
|
|
||||||
|
# Create companion summary file with structured sections
|
||||||
(disc_dir / "feature.discussion.sum.md").write_text(
|
(disc_dir / "feature.discussion.sum.md").write_text(
|
||||||
"""# Summary — Feature
|
"""# Summary — Feature
|
||||||
|
|
||||||
|
|
@ -259,6 +295,7 @@ READY: 1 • CHANGES: 0 • REJECT: 0
|
||||||
""".replace("{ts}", today), encoding="utf-8")
|
""".replace("{ts}", today), encoding="utf-8")
|
||||||
|
|
||||||
def copy_install_assets_to_target(target: Path):
|
def copy_install_assets_to_target(target: Path):
|
||||||
|
"""Copy essential files from the installer to the target repository."""
|
||||||
# Copy DESIGN.md and ramble.py from installer if present
|
# Copy DESIGN.md and ramble.py from installer if present
|
||||||
copy_if_exists(INSTALL_ROOT / "DESIGN.md", target / "DESIGN.md")
|
copy_if_exists(INSTALL_ROOT / "DESIGN.md", target / "DESIGN.md")
|
||||||
copy_if_exists(INSTALL_ROOT / "ramble.py", target / "ramble.py")
|
copy_if_exists(INSTALL_ROOT / "ramble.py", target / "ramble.py")
|
||||||
|
|
@ -278,13 +315,17 @@ def copy_install_assets_to_target(target: Path):
|
||||||
shutil.copytree(INSTALL_ROOT / "automation", target / "automation", dirs_exist_ok=True)
|
shutil.copytree(INSTALL_ROOT / "automation", target / "automation", dirs_exist_ok=True)
|
||||||
|
|
||||||
def first_commit(target: Path):
|
def first_commit(target: Path):
|
||||||
|
"""Perform the initial git commit of all scaffolded files."""
|
||||||
try:
|
try:
|
||||||
sh(["git", "add", "-A"], cwd=str(target))
|
sh(["git", "add", "-A"], cwd=str(target))
|
||||||
sh(["git", "commit", "-m", "chore: bootstrap Cascading Development scaffolding"], cwd=str(target))
|
sh(["git", "commit", "-m", "chore: bootstrap Cascading Development scaffolding"], cwd=str(target))
|
||||||
except Exception:
|
except Exception:
|
||||||
|
# Silently continue if commit fails (e.g., no git config)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
"""Main entry point for the Cascading Development setup script."""
|
||||||
|
# Parse command line arguments
|
||||||
ap = argparse.ArgumentParser()
|
ap = argparse.ArgumentParser()
|
||||||
ap.add_argument("--target", help="Destination path to create/use the repo")
|
ap.add_argument("--target", help="Destination path to create/use the repo")
|
||||||
ap.add_argument("--provider", choices=["mock", "claude"], default="mock", help="Ramble provider (default: mock)")
|
ap.add_argument("--provider", choices=["mock", "claude"], default="mock", help="Ramble provider (default: mock)")
|
||||||
|
|
@ -292,6 +333,7 @@ def main():
|
||||||
ap.add_argument("--claude-cmd", default="claude")
|
ap.add_argument("--claude-cmd", default="claude")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
# Get target directory from args or prompt user
|
||||||
target_str = args.target
|
target_str = args.target
|
||||||
if not target_str:
|
if not target_str:
|
||||||
target_str = input("Destination repo path (will be created if missing): ").strip()
|
target_str = input("Destination repo path (will be created if missing): ").strip()
|
||||||
|
|
@ -299,15 +341,16 @@ def main():
|
||||||
say("No target specified. Aborting.")
|
say("No target specified. Aborting.")
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
|
# Resolve and create target directory
|
||||||
target = Path(target_str).expanduser().resolve()
|
target = Path(target_str).expanduser().resolve()
|
||||||
target.mkdir(parents=True, exist_ok=True)
|
target.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
say(f"[=] Installing Cascading Development into: {target}")
|
say(f"[=] Installing Cascading Development into: {target}")
|
||||||
|
|
||||||
# Copy assets from installer into target
|
# Step 1: Copy assets from installer into target
|
||||||
copy_install_assets_to_target(target)
|
copy_install_assets_to_target(target)
|
||||||
|
|
||||||
# Ensure folder layout
|
# Step 2: Create standard folder structure
|
||||||
for p in [
|
for p in [
|
||||||
target / "Docs" / "features",
|
target / "Docs" / "features",
|
||||||
target / "Docs" / "discussions" / "reviews",
|
target / "Docs" / "discussions" / "reviews",
|
||||||
|
|
@ -319,24 +362,25 @@ def main():
|
||||||
]:
|
]:
|
||||||
p.mkdir(parents=True, exist_ok=True)
|
p.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Create rules/templates and basic process docs
|
# Step 3: Create rules/templates and basic process docs
|
||||||
seed_process_and_rules(target)
|
seed_process_and_rules(target)
|
||||||
|
|
||||||
# Initialize git & install pre-commit
|
# Step 4: Initialize git & install pre-commit
|
||||||
ensure_git_repo(target)
|
ensure_git_repo(target)
|
||||||
install_precommit_hook(target)
|
install_precommit_hook(target)
|
||||||
|
|
||||||
# Launch Ramble (if available)
|
# Step 5: Launch Ramble (if available and not disabled)
|
||||||
req = None
|
req = None
|
||||||
if not args.no_ramble:
|
if not args.no_ramble:
|
||||||
req = run_ramble_and_collect(target, provider=args.provider, claude_cmd=args.claude_cmd)
|
req = run_ramble_and_collect(target, provider=args.provider, claude_cmd=args.claude_cmd)
|
||||||
|
|
||||||
# Seed first feature based on Ramble output
|
# Step 6: Seed first feature based on Ramble output
|
||||||
seed_initial_feature(target, req)
|
seed_initial_feature(target, req)
|
||||||
|
|
||||||
# First commit
|
# Step 7: Perform initial commit
|
||||||
first_commit(target)
|
first_commit(target)
|
||||||
|
|
||||||
|
# Completion message
|
||||||
say("[✓] Setup complete.")
|
say("[✓] Setup complete.")
|
||||||
say(f"Next steps:\n cd {target}\n git status")
|
say(f"Next steps:\n cd {target}\n git status")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue