1st commit
This commit is contained in:
parent
5e3a7231f9
commit
5425ce9fbb
|
|
@ -0,0 +1,4 @@
|
||||||
|
.venv/
|
||||||
|
.idea/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import shutil, os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
OUT = ROOT / "install"
|
||||||
|
VER = (ROOT / "VERSION").read_text().strip() if (ROOT / "VERSION").exists() else "0.1.0"
|
||||||
|
BUNDLE = OUT / f"cascadingdev-{VER}"
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if BUNDLE.exists():
|
||||||
|
shutil.rmtree(BUNDLE)
|
||||||
|
# copy essentials
|
||||||
|
(BUNDLE / "assets" / "hooks").mkdir(parents=True, exist_ok=True)
|
||||||
|
(BUNDLE / "assets" / "templates").mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
shutil.copy2(ROOT / "DESIGN.md", BUNDLE / "DESIGN.md")
|
||||||
|
shutil.copy2(ROOT / "assets" / "runtime" / "ramble.py", BUNDLE / "ramble.py")
|
||||||
|
shutil.copy2(ROOT / "assets" / "hooks" / "pre-commit", BUNDLE / "assets" / "hooks" / "pre-commit")
|
||||||
|
for t in ["feature_request.md","discussion.md","design_doc.md"]:
|
||||||
|
shutil.copy2(ROOT / "assets" / "templates" / t, BUNDLE / "assets" / "templates" / t)
|
||||||
|
|
||||||
|
# write installer entrypoint
|
||||||
|
(BUNDLE / "setup_cascadingdev.py").write_text(INSTALLER_PY, encoding="utf-8")
|
||||||
|
(BUNDLE / "INSTALL.md").write_text("Unzip, then run:\n\n python3 setup_cascadingdev.py\n", encoding="utf-8")
|
||||||
|
(BUNDLE / "VERSION").write_text(VER, encoding="utf-8")
|
||||||
|
print(f"[✓] Built installer → {BUNDLE}")
|
||||||
|
|
||||||
|
INSTALLER_PY = r'''#!/usr/bin/env python3
|
||||||
|
import argparse, json, os, shutil, subprocess, sys, datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
HERE = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
def sh(cmd, cwd=None):
|
||||||
|
return subprocess.run(cmd, check=True, text=True, capture_output=True, cwd=cwd)
|
||||||
|
|
||||||
|
def say(x): print(x, flush=True)
|
||||||
|
|
||||||
|
def write_if_missing(p: Path, content: str):
|
||||||
|
p.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
if not p.exists():
|
||||||
|
p.write_text(content, encoding="utf-8")
|
||||||
|
|
||||||
|
def copytree(src: Path, dst: Path):
|
||||||
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
if src.is_file():
|
||||||
|
shutil.copy2(src, dst)
|
||||||
|
else:
|
||||||
|
shutil.copytree(src, dst, dirs_exist_ok=True)
|
||||||
|
|
||||||
|
def ensure_git_repo(target: Path):
|
||||||
|
if not (target / ".git").exists():
|
||||||
|
sh(["git", "init", "-b", "main"], cwd=target)
|
||||||
|
write_if_missing(target / ".gitignore", ".env\n.env.*\nsecrets/\n__pycache__/\n*.pyc\n.pytest_cache/\n.DS_Store\n")
|
||||||
|
|
||||||
|
def install_hook(target: Path):
|
||||||
|
hooks = target / ".git" / "hooks"; hooks.mkdir(parents=True, exist_ok=True)
|
||||||
|
src = HERE / "assets" / "hooks" / "pre-commit"
|
||||||
|
dst = hooks / "pre-commit"
|
||||||
|
dst.write_text(src.read_text(encoding="utf-8"), encoding="utf-8")
|
||||||
|
dst.chmod(0o755)
|
||||||
|
|
||||||
|
def run_ramble(target: Path, provider="mock"):
|
||||||
|
ramble = target / "ramble.py"
|
||||||
|
if not ramble.exists(): return None
|
||||||
|
args = [sys.executable, str(ramble),
|
||||||
|
"--provider", provider,
|
||||||
|
"--prompt", "Describe your initial feature request for this repository",
|
||||||
|
"--fields", "Summary", "Title", "Intent", "ProblemItSolves", "BriefOverview",
|
||||||
|
"--criteria", '{"Summary":"<= 2 sentences","Title":"camelCase, <= 24 chars"}']
|
||||||
|
say("[•] Launching Ramble…")
|
||||||
|
p = subprocess.run(args, text=True, capture_output=True, cwd=target)
|
||||||
|
try:
|
||||||
|
return json.loads((p.stdout or "").strip())
|
||||||
|
except Exception:
|
||||||
|
say("[-] Could not parse Ramble output; using template defaults.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def seed_rules_and_templates(target: Path):
|
||||||
|
write_if_missing(target / ".ai-rules.yml",
|
||||||
|
"version: 1\nfile_associations:\n \"*.md\": \"md-file\"\n\nrules:\n md-file:\n description: \"Normalize Markdown\"\n instruction: |\n Keep markdown tidy (headings, lists, spacing). No content churn.\nsettings:\n model: \"local-mock\"\n temperature: 0.1\n")
|
||||||
|
# copy templates
|
||||||
|
copytree(HERE / "assets" / "templates", target / "process" / "templates")
|
||||||
|
|
||||||
|
def seed_first_feature(target: Path, req):
|
||||||
|
today = datetime.date.today().isoformat()
|
||||||
|
fr = target / "Docs" / "features" / f"FR_{today}_initial-feature-request"
|
||||||
|
disc = fr / "discussions"; disc.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
if req:
|
||||||
|
fields = req.get("fields", {}) or {}
|
||||||
|
title = (fields.get("Title") or "initialProjectDesign").strip()
|
||||||
|
intent = (fields.get("Intent") or "—").strip()
|
||||||
|
problem = (fields.get("ProblemItSolves") or "—").strip()
|
||||||
|
brief = (fields.get("BriefOverview") or "—").strip()
|
||||||
|
summary = (req.get("summary") or "").strip()
|
||||||
|
body = f"# Feature Request: {title}\n\n**Intent**: {intent}\n**Motivation / Problem**: {problem}\n**Brief Overview**: {brief}\n\n**Summary**: {summary}\n**Meta**: Created: {today}\n"
|
||||||
|
else:
|
||||||
|
body = (target / "process" / "templates" / "feature_request.md").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
(fr / "request.md").write_text(body, encoding="utf-8")
|
||||||
|
(disc / "feature.discussion.md").write_text(
|
||||||
|
f"---\ntype: discussion\nstage: feature\nstatus: OPEN\nfeature_id: FR_{today}_initial-feature-request\ncreated: {today}\n---\n## Summary\nKickoff discussion. Append comments below.\n\n## Participation\n- Maintainer: Kickoff. VOTE: READY\n", encoding="utf-8")
|
||||||
|
(disc / "feature.discussion.sum.md").write_text(
|
||||||
|
"# Summary — Feature\n\n<!-- SUMMARY:DECISIONS START -->\n## Decisions (ADR-style)\n- (none yet)\n<!-- SUMMARY:DECISIONS END -->\n\n<!-- SUMMARY:OPEN_QUESTIONS START -->\n## Open Questions\n- (none yet)\n<!-- SUMMARY:OPEN_QUESTIONS END -->\n\n<!-- SUMMARY:AWAITING START -->\n## Awaiting Replies\n- (none yet)\n<!-- SUMMARY:AWAITING END -->\n\n<!-- SUMMARY:ACTION_ITEMS START -->\n## Action Items\n- (none yet)\n<!-- SUMMARY:ACTION_ITEMS END -->\n\n<!-- SUMMARY:VOTES START -->\n## Votes (latest per participant)\nREADY: 1 • CHANGES: 0 • REJECT: 0\n- Maintainer\n<!-- SUMMARY:VOTES END -->\n\n<!-- SUMMARY:TIMELINE START -->\n## Timeline (most recent first)\n- {today} Maintainer: Kickoff\n<!-- SUMMARY:TIMELINE END -->\n\n<!-- SUMMARY:LINKS START -->\n## Links\n- Design/Plan: ../design/design.md\n<!-- SUMMARY:LINKS END -->\n".replace("{today}", today), encoding="utf-8")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--target", help="Destination path for the user's project")
|
||||||
|
ap.add_argument("--no-ramble", action="store_true")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
target = Path(args.target or input("User's project folder: ").strip()).expanduser().resolve()
|
||||||
|
target.mkdir(parents=True, exist_ok=True)
|
||||||
|
say(f"[=] Installing into: {target}")
|
||||||
|
|
||||||
|
# copy top-level assets
|
||||||
|
shutil.copy2(HERE / "DESIGN.md", target / "DESIGN.md")
|
||||||
|
shutil.copy2(HERE / "ramble.py", target / "ramble.py")
|
||||||
|
|
||||||
|
# basic tree
|
||||||
|
for p in [target / "Docs" / "features",
|
||||||
|
target / "Docs" / "discussions" / "reviews",
|
||||||
|
target / "Docs" / "diagrams" / "file_diagrams",
|
||||||
|
target / "scripts" / "hooks",
|
||||||
|
target / "src", target / "tests", target / "process"]:
|
||||||
|
p.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# rules / templates
|
||||||
|
seed_rules_and_templates(target)
|
||||||
|
|
||||||
|
# git + hook
|
||||||
|
ensure_git_repo(target)
|
||||||
|
install_hook(target)
|
||||||
|
|
||||||
|
# ramble
|
||||||
|
req = None if args.no-ramble else run_ramble(target)
|
||||||
|
|
||||||
|
# seed FR
|
||||||
|
seed_first_feature(target, req)
|
||||||
|
|
||||||
|
try:
|
||||||
|
sh(["git", "add", "-A"], cwd=target)
|
||||||
|
sh(["git", "commit", "-m", "chore: bootstrap Cascading Development scaffolding"], cwd=target)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
say("[✓] Done. Next:\n cd " + str(target) + "\n git status")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
'''
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue