1st commit
This commit is contained in:
parent
db728ac2e4
commit
d78b0d83c8
|
|
@ -146,7 +146,7 @@ Initial discussion for feature `{fid}`. Append your comments below.
|
|||
## Participation
|
||||
- Maintainer: Kickoff. VOTE: READY
|
||||
"""
|
||||
write_text(dir_disc / "feature.discussion.md", req)
|
||||
write_text(dir_disc / "feature.feature.discussion.md", req)
|
||||
|
||||
sum_md = f"""# Summary — Feature
|
||||
|
||||
|
|
|
|||
|
|
@ -699,6 +699,7 @@ def parse_args():
|
|||
p.add_argument("--prompt", default="Explain your new feature idea")
|
||||
p.add_argument("--fields", nargs="+", default=["Summary","Title","Intent","ProblemItSolves","BriefOverview"])
|
||||
p.add_argument("--criteria", default="", help="JSON mapping of field -> criteria")
|
||||
p.add_argument("--hints", default="", help="JSON list of hint strings")
|
||||
p.add_argument("--timeout", type=int, default=90)
|
||||
p.add_argument("--tail", type=int, default=6000)
|
||||
p.add_argument("--debug", action="store_true")
|
||||
|
|
@ -733,11 +734,23 @@ if __name__ == "__main__":
|
|||
print("[FATAL] 'requests' is required for image backends. pip install requests", file=sys.stderr)
|
||||
sys.exit(3)
|
||||
|
||||
# Parse JSON args (tolerate empty/invalid)
|
||||
try:
|
||||
criteria = json.loads(args.criteria) if args.criteria else {}
|
||||
if not isinstance(criteria, dict): criteria = {}
|
||||
except Exception:
|
||||
criteria = {}
|
||||
try:
|
||||
hints = json.loads(args.hints) if args.hints else None
|
||||
if hints is not None and not isinstance(hints, list): hints = None
|
||||
except Exception:
|
||||
hints = None
|
||||
|
||||
demo = open_ramble_dialog(
|
||||
prompt=args.prompt,
|
||||
fields=args.fields,
|
||||
field_criteria=criteria,
|
||||
hints=None,
|
||||
hints = hints,
|
||||
provider=provider,
|
||||
enable_stability=args.stability,
|
||||
enable_pexels=args.pexels,
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
type: discussion
|
||||
stage: <feature|design|implementation|testing|review>
|
||||
status: OPEN
|
||||
feature_id: <FR_...>
|
||||
created: <YYYY-MM-DD>
|
||||
|
||||
promotion_rule:
|
||||
allow_agent_votes: true
|
||||
ready_min_eligible_votes: all
|
||||
reject_min_eligible_votes: all
|
||||
|
||||
participation:
|
||||
instructions: |
|
||||
- Append your input at the end as: "YourName: your comment…"
|
||||
- Every comment must end with a vote line: "VOTE: READY|CHANGES|REJECT"
|
||||
- Agents/bots must prefix names with "AI_"
|
||||
|
||||
voting:
|
||||
values: [READY, CHANGES, REJECT]
|
||||
---
|
||||
## Summary
|
||||
2-4 sentence summary of current state
|
||||
|
||||
## Participation
|
||||
comments appended below
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<!--META
|
||||
{
|
||||
"kind": "discussion",
|
||||
"tokens": ["FeatureId", "CreatedDate"]
|
||||
}
|
||||
-->
|
||||
|
||||
## Summary
|
||||
Initial discussion for {FeatureId}. Append your comments below.
|
||||
|
||||
## Participation
|
||||
- Maintainer: Kickoff. VOTE: READY
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<!--META
|
||||
{
|
||||
"kind": "discussion_summary",
|
||||
"tokens": ["FeatureId", "CreatedDate"]
|
||||
}
|
||||
-->
|
||||
|
||||
# Summary — Feature {FeatureId}
|
||||
|
||||
<!-- SUMMARY:DECISIONS START -->
|
||||
## Decisions (ADR-style)
|
||||
- (none yet)
|
||||
<!-- SUMMARY:DECISIONS END -->
|
||||
|
||||
<!-- SUMMARY:OPEN_QUESTIONS START -->
|
||||
## Open Questions
|
||||
- (none yet)
|
||||
<!-- SUMMARY:OPEN_QUESTIONS END -->
|
||||
|
||||
<!-- SUMMARY:AWAITING START -->
|
||||
## Awaiting Replies
|
||||
- (none yet)
|
||||
<!-- SUMMARY:AWAITING END -->
|
||||
|
||||
<!-- SUMMARY:ACTION_ITEMS START -->
|
||||
## Action Items
|
||||
- (none yet)
|
||||
<!-- SUMMARY:ACTION_ITEMS END -->
|
||||
|
||||
<!-- SUMMARY:VOTES START -->
|
||||
## Votes (latest per participant)
|
||||
READY: 1 • CHANGES: 0 • REJECT: 0
|
||||
- Maintainer
|
||||
<!-- SUMMARY:VOTES END -->
|
||||
|
||||
<!-- SUMMARY:TIMELINE START -->
|
||||
## Timeline (most recent first)
|
||||
- {CreatedDate} Maintainer: Kickoff
|
||||
<!-- SUMMARY:TIMELINE END -->
|
||||
|
||||
<!-- SUMMARY:LINKS START -->
|
||||
## Links
|
||||
- Design/Plan: ../design/design.md
|
||||
<!-- SUMMARY:LINKS END -->
|
||||
|
|
@ -1,10 +1,35 @@
|
|||
# Feature Request: <title>
|
||||
|
||||
**Feature ID**: <FR_YYYY-MM-DD_slug>
|
||||
**Intent**: <one paragraph describing purpose>
|
||||
**Motivation / Problem**: <why this is needed now>
|
||||
**Constraints / Non-Goals**: <bulleted list of limitations>
|
||||
**Rough Proposal**: <short implementation outline>
|
||||
**Open Questions**: <bulleted list of uncertainties>
|
||||
**Meta**: Created: <date> • Author: <name>
|
||||
Discussion Template (process/templates/discussion.md):
|
||||
<!--META
|
||||
{
|
||||
"kind": "feature_request",
|
||||
"ramble_fields": [
|
||||
{"name": "Title", "hint": "camelCase, ≤24 chars", "default": "initialProjectDesign"},
|
||||
{"name": "Intent"},
|
||||
{"name": "ProblemItSolves"},
|
||||
{"name": "BriefOverview"},
|
||||
{"name": "Summary", "hint": "≤2 sentences"}
|
||||
],
|
||||
"criteria": {
|
||||
"Title": "camelCase, <= 24 chars",
|
||||
"Summary": "<= 2 sentences"
|
||||
},
|
||||
"hints": [
|
||||
"What is it called?",
|
||||
"Who benefits most?",
|
||||
"What problem does it solve?",
|
||||
"What does success look like?"
|
||||
],
|
||||
"tokens": ["FeatureId", "CreatedDate", "Title", "Intent", "ProblemItSolves", "BriefOverview", "Summary"]
|
||||
}
|
||||
-->
|
||||
|
||||
# Feature Request: {Title}
|
||||
|
||||
**Intent**: {Intent}
|
||||
**Motivation / Problem**: {ProblemItSolves}
|
||||
**Brief Overview**: {BriefOverview}
|
||||
|
||||
**Summary**: {Summary}
|
||||
|
||||
**Meta**: FeatureId: {FeatureId} • Created: {CreatedDate}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.egg-info/
|
||||
.pytest_cache/
|
||||
.mypy_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
|
||||
# Node (if any JS tooling appears)
|
||||
node_modules/
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Env / secrets
|
||||
.env
|
||||
.env.*
|
||||
secrets/
|
||||
|
||||
# OS/editor
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Project
|
||||
.git/ai-rules-*
|
||||
|
|
@ -850,7 +850,7 @@ rules:
|
|||
feature_request:
|
||||
outputs:
|
||||
feature_discussion:
|
||||
path: "{dir}/discussions/feature.discussion.md"
|
||||
path: "{dir}/discussions/feature.feature.discussion.md"
|
||||
output_type: "feature_discussion_writer"
|
||||
instruction: |
|
||||
If missing: create with standard header (stage: feature, status: OPEN),
|
||||
|
|
@ -870,7 +870,7 @@ rules:
|
|||
outputs:
|
||||
# 1) Append the new AI comment to the discussion (append-only)
|
||||
self_append:
|
||||
path: "{dir}/discussions/feature.discussion.md"
|
||||
path: "{dir}/discussions/feature.feature.discussion.md"
|
||||
output_type: "feature_discussion_writer"
|
||||
instruction: |
|
||||
Append concise comment signed with AI name, ending with a single vote line.
|
||||
|
|
@ -890,7 +890,7 @@ rules:
|
|||
|
||||
# 3) Promotion artifacts when READY_FOR_DESIGN
|
||||
design_discussion:
|
||||
path: "{dir}/discussions/design.discussion.md"
|
||||
path: "{dir}/discussions/design.feature.discussion.md"
|
||||
output_type: "design_discussion_writer"
|
||||
instruction: |
|
||||
Create ONLY if feature discussion status is READY_FOR_DESIGN.
|
||||
|
|
@ -930,7 +930,7 @@ rules:
|
|||
Update only the marker-bounded sections from the discussion content.
|
||||
|
||||
impl_discussion:
|
||||
path: "{dir}/discussions/implementation.discussion.md"
|
||||
path: "{dir}/discussions/implementation.feature.discussion.md"
|
||||
output_type: "impl_discussion_writer"
|
||||
instruction: |
|
||||
Create ONLY if design discussion status is READY_FOR_IMPLEMENTATION.
|
||||
|
|
@ -974,7 +974,7 @@ rules:
|
|||
Include unchecked items from ../implementation/tasks.md in ACTION_ITEMS.
|
||||
|
||||
test_discussion:
|
||||
path: "{dir}/discussions/testing.discussion.md"
|
||||
path: "{dir}/discussions/testing.feature.discussion.md"
|
||||
output_type: "test_discussion_writer"
|
||||
instruction: |
|
||||
Create ONLY if implementation status is READY_FOR_TESTING.
|
||||
|
|
@ -1024,7 +1024,7 @@ rules:
|
|||
Initialize bug discussion and fix plan in the same folder.
|
||||
|
||||
review_discussion:
|
||||
path: "{dir}/discussions/review.discussion.md"
|
||||
path: "{dir}/discussions/review.feature.discussion.md"
|
||||
output_type: "review_discussion_writer"
|
||||
instruction: |
|
||||
Create ONLY if all test checklist items pass.
|
||||
|
|
@ -1141,7 +1141,7 @@ resolve_template() {
|
|||
ext="${basename##*.}"
|
||||
# nearest FR_* ancestor as feature_id
|
||||
feature_id="$(echo "$rel_path" | sed -n 's|.*Docs/features/\(FR_[^/]*\).*|\1|p')"
|
||||
# infer stage from <stage>.discussion.md when applicable
|
||||
# infer stage from <stage>.feature.discussion.md when applicable
|
||||
stage="$(echo "$basename" | sed -n 's/^\([A-Za-z0-9_-]\+\)\.discussion\.md$/\1/p')"
|
||||
echo "$tmpl" \
|
||||
| sed -e "s|{date}|$today|g" \
|
||||
|
|
@ -1288,7 +1288,7 @@ Rule Definition (in Docs/features/.ai-rules.yml):
|
|||
discussion_moderator_nudge:
|
||||
outputs:
|
||||
self_append:
|
||||
path: "{dir}/discussions/{stage}.discussion.md"
|
||||
path: "{dir}/discussions/{stage}.feature.discussion.md"
|
||||
output_type: "discussion_moderator_writer"
|
||||
instruction: |
|
||||
Act as AI_Moderator. Analyze the entire discussion and:
|
||||
|
|
@ -1425,7 +1425,7 @@ Bypass & Minimal Patch:
|
|||
|
||||
```bash
|
||||
.git/ai-rules-debug/
|
||||
├─ 20251021-143022-12345-feature.discussion.md/
|
||||
├─ 20251021-143022-12345-feature.feature.discussion.md/
|
||||
│ ├─ raw.out # Raw model output
|
||||
│ ├─ clean.diff # Extracted patch
|
||||
│ ├─ sanitized.diff # After sanitization
|
||||
|
|
@ -1975,7 +1975,7 @@ Docs/features/FR_.../
|
|||
type: discussion-summary
|
||||
stage: feature # feature|design|implementation|testing|review
|
||||
status: ACTIVE # ACTIVE|SNAPSHOT|ARCHIVED
|
||||
source_discussion: feature.discussion.md
|
||||
source_discussion: feature.feature.discussion.md
|
||||
feature_id: FR_YYYY-MM-DD_<slug>
|
||||
updated: YYYY-MM-DDTHH:MM:SSZ
|
||||
policy:
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Examples:
|
|||
python setup_cascadingdev.py --target ~/dev/my-new-repo
|
||||
python setup_cascadingdev.py --target /abs/path --no-ramble
|
||||
"""
|
||||
import json
|
||||
import json, re
|
||||
import argparse
|
||||
import datetime
|
||||
import sys
|
||||
|
|
@ -57,16 +57,79 @@ def run(cmd: list[str], cwd: Path | None = None) -> int:
|
|||
proc = subprocess.Popen(cmd, cwd=cwd, stdout=sys.stdout, stderr=sys.stderr)
|
||||
return proc.wait()
|
||||
|
||||
# --- Tiny template helpers ----------------------------------------------------
|
||||
# Self-contained; no external dependencies
|
||||
_META_RE = re.compile(r"<!--META\s*(\{.*?\})\s*-->", re.S)
|
||||
|
||||
def load_template_with_meta(path: Path) -> tuple[dict, str]:
|
||||
"""
|
||||
Returns (meta: dict, body_without_meta: str). If no META, ({} , full text).
|
||||
META must be a single JSON object inside <!--META ... -->.
|
||||
"""
|
||||
if not path.exists():
|
||||
return {}, ""
|
||||
text = path.read_text(encoding="utf-8")
|
||||
m = _META_RE.search(text)
|
||||
if not m:
|
||||
return {}, text
|
||||
meta_json = m.group(1)
|
||||
try:
|
||||
meta = json.loads(meta_json)
|
||||
except Exception:
|
||||
meta = {}
|
||||
body = _META_RE.sub("", text, count=1).lstrip()
|
||||
return meta, body
|
||||
|
||||
def render_placeholders(body: str, values: dict) -> str:
|
||||
"""
|
||||
Simple {Token} replacement. Leaves unknown tokens as-is.
|
||||
"""
|
||||
# two-pass: {{Token}} then {Token}
|
||||
out = body
|
||||
for k, v in values.items():
|
||||
out = out.replace("{{" + k + "}}", str(v))
|
||||
try:
|
||||
out = out.format_map({k: v for k, v in values.items()})
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
def meta_ramble_config(meta: dict) -> tuple[list[str], dict, dict, list[str]]:
|
||||
"""
|
||||
From template META, extract:
|
||||
- fields: list of field names in order
|
||||
- defaults: {field: default_value}
|
||||
- criteria: {field: rule/description} (optional)
|
||||
- hints: [str, ...] (optional)
|
||||
"""
|
||||
fields: list[str] = []
|
||||
defaults: dict = {}
|
||||
for spec in meta.get("ramble_fields", []):
|
||||
name = spec.get("name")
|
||||
if name:
|
||||
fields.append(name)
|
||||
if "default" in spec:
|
||||
defaults[name] = spec["default"]
|
||||
criteria = meta.get("criteria", {}) or {}
|
||||
hints = meta.get("hints", []) or []
|
||||
return fields, defaults, criteria, hints
|
||||
|
||||
def ensure_git_repo(target: Path):
|
||||
"""Initialize a git repository if one doesn't exist at the target path."""
|
||||
if not (target / ".git").exists():
|
||||
# Initialize git repo with main branch
|
||||
run(["git", "init", "-b", "main"], cwd=target)
|
||||
# Create basic .gitignore file
|
||||
write_if_missing(target / ".gitignore", "\n".join([
|
||||
".env", ".env.*", "secrets/", ".git/ai-rules-*", "__pycache__/",
|
||||
"*.pyc", ".pytest_cache/", ".DS_Store",
|
||||
]) + "\n")
|
||||
# Seed .gitignore from template if present; otherwise fallback
|
||||
tmpl_gitignore = INSTALL_ROOT / "assets" / "templates" / "root_gitignore"
|
||||
if tmpl_gitignore.exists():
|
||||
copy_if_missing(tmpl_gitignore, target / ".gitignore")
|
||||
else:
|
||||
write_if_missing(target / ".gitignore", "\n".join([
|
||||
"__pycache__/", "*.py[cod]", "*.egg-info/", ".pytest_cache/",
|
||||
".mypy_cache/", ".coverage", "htmlcov/", "node_modules/",
|
||||
"dist/", "build/", ".env", ".env.*", "secrets/", ".DS_Store",
|
||||
".git/ai-rules-*",
|
||||
]) + "\n")
|
||||
|
||||
def install_precommit_hook(target: Path):
|
||||
"""Install the pre-commit hook from installer assets to target git hooks."""
|
||||
|
|
@ -90,21 +153,34 @@ def run_ramble_and_collect(target: Path, provider: str = "mock", claude_cmd: str
|
|||
Launch Ramble GUI to collect initial feature request details.
|
||||
Falls back to terminal prompts if GUI fails or returns invalid JSON.
|
||||
"""
|
||||
# Find FR template + read META (for field names)
|
||||
fr_tmpl = INSTALL_ROOT / "assets" / "templates" / "feature_request.md"
|
||||
meta, _ = load_template_with_meta(fr_tmpl)
|
||||
field_names, _defaults, criteria, hints = meta_ramble_config(meta)
|
||||
|
||||
# Fallback to your previous default fields if template lacks META
|
||||
if not field_names:
|
||||
field_names = ["Summary", "Title", "Intent", "ProblemItSolves", "BriefOverview"]
|
||||
|
||||
ramble = target / "ramble.py"
|
||||
if not ramble.exists():
|
||||
say("[-] ramble.py not found in target; skipping interactive FR capture.")
|
||||
return None
|
||||
|
||||
# Build Ramble command arguments
|
||||
# Build Ramble arguments dynamically from the template-defined fields
|
||||
args = [
|
||||
sys.executable, str(ramble),
|
||||
"--provider", provider,
|
||||
"--claude-cmd", claude_cmd,
|
||||
"--prompt", "Describe your initial feature request for this repository",
|
||||
"--fields", "Summary", "Title", "Intent", "ProblemItSolves", "BriefOverview",
|
||||
"--criteria", '{"Summary":"<= 2 sentences","Title":"camelCase, <= 24 chars"}'
|
||||
"--fields", *field_names,
|
||||
]
|
||||
|
||||
if criteria:
|
||||
args += ["--criteria", json.dumps(criteria)]
|
||||
if hints:
|
||||
args += ["--hints", json.dumps(hints)]
|
||||
|
||||
say("[•] Launching Ramble (close the dialog with Submit to return JSON)…")
|
||||
proc = subprocess.run(args, text=True, capture_output=True, cwd=str(target))
|
||||
|
||||
|
|
@ -170,53 +246,72 @@ def seed_process_and_rules(target: Path):
|
|||
copy_if_missing(t_rules_features, rules_dir / ".ai-rules.yml")
|
||||
|
||||
def seed_initial_feature(target: Path, req_fields: dict | None):
|
||||
"""Create the initial feature request and associated discussion files."""
|
||||
today = datetime.date.today().isoformat()
|
||||
fr_dir = target / "Docs" / "features" / f"FR_{today}_initial-feature-request"
|
||||
feature_id = f"FR_{today}_initial-feature-request"
|
||||
fr_dir = target / "Docs" / "features" / feature_id
|
||||
disc_dir = fr_dir / "discussions"
|
||||
disc_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Create feature request content, using Ramble data if available
|
||||
if req_fields:
|
||||
title = (req_fields.get("fields", {}) or {}).get("Title", "").strip() or "initialProjectDesign"
|
||||
intent = (req_fields.get("fields", {}) or {}).get("Intent", "").strip() or "—"
|
||||
problem = (req_fields.get("fields", {}) or {}).get("ProblemItSolves", "").strip() or "—"
|
||||
brief = (req_fields.get("fields", {}) or {}).get("BriefOverview", "").strip() or "—"
|
||||
summary = (req_fields.get("summary") or "").strip()
|
||||
body = f"""# Feature Request: {title}
|
||||
# Gather values from Ramble result (if any)
|
||||
fields = (req_fields or {}).get("fields", {}) if req_fields else {}
|
||||
# Load FR template + META
|
||||
fr_tmpl = INSTALL_ROOT / "assets" / "templates" / "feature_request.md"
|
||||
fr_meta, fr_body = load_template_with_meta(fr_tmpl)
|
||||
field_names, defaults, _criteria, _hints = meta_ramble_config(fr_meta)
|
||||
|
||||
# Build values map with defaults → ramble fields → system tokens
|
||||
values = {}
|
||||
values.update(defaults) # template defaults
|
||||
values.update(fields) # user-entered
|
||||
values.update({ # system tokens
|
||||
"FeatureId": feature_id,
|
||||
"CreatedDate": today,
|
||||
})
|
||||
|
||||
# If no template body, fall back to your old default
|
||||
if not fr_body.strip():
|
||||
title = values.get("Title", "initialProjectDesign")
|
||||
intent = values.get("Intent", "—")
|
||||
problem = values.get("ProblemItSolves", "—")
|
||||
brief = values.get("BriefOverview", "—")
|
||||
summary = values.get("Summary", "")
|
||||
fr_body = f"""# Feature Request: {title}
|
||||
|
||||
**Intent**: {intent}
|
||||
**Motivation / Problem**: {problem}
|
||||
**Brief Overview**: {brief}
|
||||
|
||||
**Summary**: {summary}
|
||||
|
||||
**Meta**: Created: {today}
|
||||
"""
|
||||
|
||||
(fr_dir / "request.md").write_text(render_placeholders(fr_body, values), encoding="utf-8")
|
||||
|
||||
# --- feature.discussion.md ---
|
||||
disc_tmpl = INSTALL_ROOT / "assets" / "templates" / "feature.discussion.md"
|
||||
d_meta, d_body = load_template_with_meta(disc_tmpl)
|
||||
# Always include the front-matter for rules, then template body (or fallback)
|
||||
fm = f"""---\ntype: discussion\nstage: feature\nstatus: OPEN\nfeature_id: {feature_id}\ncreated: {today}\n---\n"""
|
||||
if not d_body.strip():
|
||||
d_body = (
|
||||
"## Summary\n"
|
||||
f"Initial discussion for {feature_id}. Append your comments below.\n\n"
|
||||
"## Participation\n"
|
||||
"- Maintainer: Kickoff. VOTE: READY\n"
|
||||
)
|
||||
(disc_dir / "feature.discussion.md").write_text(fm + render_placeholders(d_body, values), encoding="utf-8")
|
||||
|
||||
# --- feature.discussion.sum.md ---
|
||||
sum_tmpl = INSTALL_ROOT / "assets" / "templates" / "feature.discussion.sum.md"
|
||||
s_meta, s_body = load_template_with_meta(sum_tmpl)
|
||||
if s_body.strip():
|
||||
# use template
|
||||
(disc_dir / "feature.discussion.sum.md").write_text(render_placeholders(s_body, values), encoding="utf-8")
|
||||
else:
|
||||
# Fallback to template content if no Ramble data
|
||||
body = (target / "process" / "templates" / "feature_request.md").read_text(encoding="utf-8")
|
||||
|
||||
(fr_dir / "request.md").write_text(body, encoding="utf-8")
|
||||
|
||||
# Create initial discussion file
|
||||
(disc_dir / "feature.discussion.md").write_text(
|
||||
f"""---
|
||||
type: discussion
|
||||
stage: feature
|
||||
status: OPEN
|
||||
feature_id: FR_{today}_initial-feature-request
|
||||
created: {today}
|
||||
---
|
||||
## Summary
|
||||
Initial discussion for the first feature request. Append your comments below.
|
||||
|
||||
## Participation
|
||||
- Maintainer: Kickoff. VOTE: READY
|
||||
""", encoding="utf-8")
|
||||
|
||||
# Create companion summary file with structured sections
|
||||
(disc_dir / "feature.discussion.sum.md").write_text(
|
||||
"""# Summary — Feature
|
||||
# your existing static content
|
||||
(disc_dir / "feature.discussion.sum.md").write_text(
|
||||
"""# Summary — Feature
|
||||
|
||||
<!-- SUMMARY:DECISIONS START -->
|
||||
## Decisions (ADR-style)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,15 @@ def main():
|
|||
shutil.copy2(ROOT / "assets" / "hooks" / "pre-commit", BUNDLE / "assets" / "hooks" / "pre-commit")
|
||||
|
||||
# copy core templates
|
||||
for t in ["feature_request.md","discussion.md","design_doc.md","USER_GUIDE.md"]:
|
||||
|
||||
for t in [
|
||||
"feature_request.md",
|
||||
"feature.discussion.md",
|
||||
"feature.discussion.sum.md",
|
||||
"design_doc.md",
|
||||
"USER_GUIDE.md",
|
||||
"root_gitignore",
|
||||
]:
|
||||
shutil.copy2(ROOT / "assets" / "templates" / t, BUNDLE / "assets" / "templates" / t)
|
||||
|
||||
# copy (recursively) the contents of process/ and rules/ templates folders
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ def main():
|
|||
required = [
|
||||
root / "assets" / "hooks" / "pre-commit",
|
||||
root / "assets" / "templates" / "feature_request.md",
|
||||
root / "assets" / "templates" / "discussion.md",
|
||||
root / "assets" / "templates" / "feature.discussion.md",
|
||||
root / "assets" / "templates" / "design_doc.md",
|
||||
root / "assets" / "templates" / "USER_GUIDE.md", # now required
|
||||
root / "assets" / "runtime" / "ramble.py",
|
||||
|
|
|
|||
Loading…
Reference in New Issue