diff --git a/skills/.system/plan/LICENSE.txt b/skills/.experimental/create-plan/LICENSE.txt similarity index 100% rename from skills/.system/plan/LICENSE.txt rename to skills/.experimental/create-plan/LICENSE.txt diff --git a/skills/.experimental/create-plan/SKILL.md b/skills/.experimental/create-plan/SKILL.md new file mode 100644 index 0000000..fdd0050 --- /dev/null +++ b/skills/.experimental/create-plan/SKILL.md @@ -0,0 +1,74 @@ +--- +name: create-plan +description: Create a concise plan. Use when a user explicitly asks for a plan related to a coding task. +metadata: + short-description: Create a plan +--- + +# Create Plan + +## Goal + +Turn a user prompt into a **single, actionable plan** delivered in the final assistant message. + +## Minimal workflow + +Throughout the entire workflow, operate in read-only mode. Do not write or update files. + +1. **Scan context quickly** + - Read `README.md` and any obvious docs (`docs/`, `CONTRIBUTING.md`, `ARCHITECTURE.md`). + - Skim relevant files (the ones most likely touched). + - Identify constraints (language, frameworks, CI/test commands, deployment shape). + +2. **Ask follow-ups only if blocking** + - Ask **at most 1–2 questions**. + - Only ask if you cannot responsibly plan without the answer; prefer multiple-choice. + - If unsure but not blocked, make a reasonable assumption and proceed. + +3. **Create a plan using the template below** + - Start with **1 short paragraph** describing the intent and approach. + - Clearly call out what is **in scope** and what is **not in scope** in short. + - Then provide a **small checklist** of action items (default 6–10 items). + - Each checklist item should be a concrete action and, when helpful, mention files/commands. + - **Make items atomic and ordered**: discovery → changes → tests → rollout. + - **Verb-first**: “Add…”, “Refactor…”, “Verify…”, “Ship…”. + - Include at least one item for **tests/validation** and one for **edge cases/risk** when applicable. + - If there are unknowns, include a tiny **Open questions** section (max 3). + +4. **Do not preface the plan with meta explanations; output only the plan as per template** + +## Plan template (follow exactly) + +```markdown +# Plan + +<1–3 sentences: what we’re doing, why, and the high-level approach.> + +## Scope +- In: +- Out: + +## Action items +[ ] +[ ] +[ ] +[ ] +[ ] +[ ] + +## Open questions +- +- +- +``` + +## Checklist item guidance +Good checklist items: +- Point to likely files/modules: src/..., app/..., services/... +- Name concrete validation: “Run npm test”, “Add unit tests for X” +- Include safe rollout when relevant: feature flag, migration plan, rollback note + +Avoid: +- Vague steps (“handle backend”, “do auth”) +- Too many micro-steps +- Writing code snippets (keep the plan implementation-agnostic) diff --git a/skills/.system/plan/SKILL.md b/skills/.system/plan/SKILL.md deleted file mode 100644 index 5d49c33..0000000 --- a/skills/.system/plan/SKILL.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -name: plan -description: Generate a plan for how an agent should accomplish a complex coding task. Use when a user asks for a plan, and optionally when they want to save, find, read, update, or delete plan files in $CODEX_HOME/plans (default ~/.codex/plans). -metadata: - short-description: Generate a plan for a complex task ---- - -# Plan - -## Overview - -Draft structured plans that clarify intent, scope, requirements, action items, testing/validation, and risks. - -Optionally, save plans to disk as markdown files with YAML frontmatter and free-form content. When drafting in chat, output only the plan body without frontmatter; add frontmatter only when saving to disk. Only write to the plans folder; do not modify the repository codebase. - -This skill can also be used to draft codebase or system overviews. - -## Core rules - -- Resolve the plans directory as `$CODEX_HOME/plans` or `~/.codex/plans` when `CODEX_HOME` is not set. -- Create the plans directory if it does not exist. -- Never write to the repo; only read files to understand context. -- Require frontmatter with **only** `name` and `description` (single-line values) for on-disk plans. -- When presenting a draft plan in chat, omit frontmatter and start at `# Plan`. -- Enforce naming rules: short, lower-case, hyphen-delimited; filename must equal `.md`. -- If a plan is not found, state it clearly and offer to create one. -- Allow overview-style plans that document flows, architecture, or context without a work checklist. - -## Decide the task - -1. **Find/list**: discover plans by frontmatter summary; confirm if multiple matches exist. -2. **Read/use**: validate frontmatter; present summary and full contents. -3. **Create**: inspect repo read-only; choose plan style (implementation vs overview); draft plan; write to plans directory only. -4. **Update**: load plan; revise content and/or description; preserve frontmatter keys; overwrite the plan file. -5. **Delete**: confirm intent, then remove the plan file if asked. - -## Plan discovery - -- Prefer `scripts/list_plans.py` for quick summaries. -- Use `scripts/read_plan_frontmatter.py` to validate a specific plan. -- If name mismatches filename or frontmatter is missing fields, call it out and ask whether to fix. - -## Plan creation workflow - -1. Scan context quickly: read README.md and obvious docs (docs/, CONTRIBUTING.md, ARCHITECTURE.md); skim likely touched files; identify constraints (language, frameworks, CI/test commands, deployment). -2. Ask follow-ups only if blocked: at most 1-2 questions, prefer multiple-choice. If unsure but not blocked, state assumptions and proceed. -3. Identify scope, constraints, and data model/API implications (or capture existing behavior for an overview). -4. Draft either an ordered implementation plan or a structured overview plan with diagrams/notes as needed. -5. Immediately output the plan body only (no frontmatter), then ask the user if they want to 1. Make changes, 2. Implement it, 3. Save it as per plan. -6. If the user wants to save it, prepend frontmatter and save the plan under the computed plans directory using `scripts/create_plan.py`. - - -## Plan update workflow - -- Re-read the plan and related code/docs before updating. -- Keep the plan name stable unless the user explicitly wants a rename. -- If renaming, update both frontmatter `name` and filename together. - -## Scripts (low-freedom helpers) - -Create a plan file (body only; frontmatter is written for you). Run from the plan skill directory: - -```bash -python ./scripts/create_plan.py \ - --name codex-rate-limit-overview \ - --description "Scope and update plan for Codex rate limiting" \ - --body-file /tmp/plan-body.md -``` - -Read frontmatter summary for a plan (run from the plan skill directory): - -```bash -python ./scripts/read_plan_frontmatter.py ~/.codex/plans/codex-rate-limit-overview.md -``` - -List plan summaries (optional filter; run from the plan skill directory): - -```bash -python ./scripts/list_plans.py --query "rate limit" -``` - -## Plan file format - -Use one of the structures below for the plan body. When drafting, output only the body (no frontmatter). When saving, prepend this frontmatter: - -```markdown ---- -name: -description: <1-line summary> ---- -``` - -### Implementation plan body template - -```markdown -# Plan - -<1-3 sentences: intent, scope, and approach.> - -## Requirements -- -- - -## Scope -- In: -- Out: - -## Files and entry points -- -- - -## Data model / API changes -- - -## Action items -[ ] -[ ] -[ ] -[ ] -[ ] -[ ] - -## Testing and validation -- - -## Risks and edge cases -- -- - -## Open questions -- -- -``` - -### Overview plan body template - -```markdown -# Plan - -<1-3 sentences: intent and scope of the overview.> - -## Overview - - -## Diagrams - - -## Key file references -- -- - -## Auth / routing / behavior notes -- - -## Current status -- - -## Action items -- None (overview only). - -## Testing and validation -- None (overview only). - -## Risks and edge cases -- None (overview only). - -## Open questions -- None. -``` - -## Writing guidance - -- Start with 1 short paragraph describing intent and approach. -- Keep action items ordered and atomic (discovery -> changes -> tests -> rollout); use verb-first phrasing. -- Scale action item count to complexity (simple: 1-2; complex: up to about 10). -- Include file/entry-point hints and concrete validation steps where useful. -- Always include testing/validation and risks/edge cases in implementation plans; include safe rollout/rollback when relevant. -- Use open questions only when necessary (max 3). -- Avoid vague steps, micro-steps, and code snippets; keep the plan implementation-agnostic. -- For overview plans, keep action items minimal and set non-applicable sections to "None." diff --git a/skills/.system/plan/scripts/create_plan.py b/skills/.system/plan/scripts/create_plan.py deleted file mode 100644 index 4cbfa8f..0000000 --- a/skills/.system/plan/scripts/create_plan.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env python3 -"""Create or overwrite a plan markdown file in $CODEX_HOME/plans.""" - -from __future__ import annotations - -import argparse -import sys -from pathlib import Path - -from plan_utils import get_plans_dir, validate_plan_name - -DEFAULT_TEMPLATE = """# Plan - -<1-3 sentences: intent, scope, and approach.> - -## Requirements -- -- - -## Scope -- In: -- Out: - -## Files and entry points -- -- - -## Data model / API changes -- - -## Action items -[ ] -[ ] -[ ] -[ ] -[ ] -[ ] - -## Testing and validation -- - -## Risks and edge cases -- -- - -## Open questions -- -- -""" - - -def read_body(args: argparse.Namespace) -> str | None: - if args.template: - return DEFAULT_TEMPLATE - if args.body_file: - return Path(args.body_file).read_text(encoding="utf-8") - if not sys.stdin.isatty(): - return sys.stdin.read() - return None - - -def main() -> int: - parser = argparse.ArgumentParser( - description="Create a plan file under $CODEX_HOME/plans or ~/.codex/plans." - ) - parser.add_argument("--name", required=True, help="Plan name (lower-case, hyphen-delimited).") - parser.add_argument("--description", required=True, help="Short plan description.") - parser.add_argument( - "--body-file", - help="Path to markdown body (without frontmatter). If omitted, read from stdin.", - ) - parser.add_argument( - "--template", - action="store_true", - help="Write a template body instead of reading from stdin or --body-file.", - ) - parser.add_argument( - "--overwrite", - action="store_true", - help="Overwrite the plan file if it already exists.", - ) - args = parser.parse_args() - - name = args.name.strip() - description = args.description.strip() - validate_plan_name(name) - if not description or "\n" in description: - raise SystemExit("Description must be a single line.") - - body = read_body(args) - if body is None: - raise SystemExit("Provide --body-file, stdin, or --template to supply plan content.") - - body = body.strip() - if not body: - raise SystemExit("Plan body cannot be empty.") - if body.lstrip().startswith("---"): - raise SystemExit("Plan body should not include frontmatter.") - - plans_dir = get_plans_dir() - plans_dir.mkdir(parents=True, exist_ok=True) - plan_path = plans_dir / f"{name}.md" - - if plan_path.exists() and not args.overwrite: - raise SystemExit(f"Plan already exists: {plan_path}. Use --overwrite to replace.") - - content = f"---\nname: {name}\ndescription: {description}\n---\n\n{body}\n" - plan_path.write_text(content, encoding="utf-8") - print(str(plan_path)) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/skills/.system/plan/scripts/list_plans.py b/skills/.system/plan/scripts/list_plans.py deleted file mode 100644 index db6c412..0000000 --- a/skills/.system/plan/scripts/list_plans.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -"""List plan summaries by reading frontmatter only.""" - -from __future__ import annotations - -import argparse -import json - -from plan_utils import get_plans_dir, parse_frontmatter - - -def main() -> int: - parser = argparse.ArgumentParser(description="List plan summaries from $CODEX_HOME/plans.") - parser.add_argument("--query", help="Case-insensitive substring to filter name/description.") - parser.add_argument("--json", action="store_true", help="Emit JSON output.") - args = parser.parse_args() - - plans_dir = get_plans_dir() - if not plans_dir.exists(): - raise SystemExit(f"Plans directory not found: {plans_dir}") - - query = args.query.lower() if args.query else None - items = [] - for path in sorted(plans_dir.glob("*.md")): - try: - data = parse_frontmatter(path) - except ValueError: - continue - name = data.get("name") - description = data.get("description") - if not name or not description: - continue - if query: - haystack = f"{name} {description}".lower() - if query not in haystack: - continue - items.append({"name": name, "description": description, "path": str(path)}) - - if args.json: - print(json.dumps(items)) - else: - for item in items: - print(f"{item['name']}\t{item['description']}\t{item['path']}") - - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/skills/.system/plan/scripts/plan_utils.py b/skills/.system/plan/scripts/plan_utils.py deleted file mode 100644 index 1a36a4b..0000000 --- a/skills/.system/plan/scripts/plan_utils.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -"""Shared helpers for plan scripts.""" - -from __future__ import annotations - -import os -import re -from pathlib import Path - -_NAME_RE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$") - - -def get_codex_home() -> Path: - """Return CODEX_HOME if set, else ~/.codex.""" - return Path(os.environ.get("CODEX_HOME", "~/.codex")).expanduser() - - -def get_plans_dir() -> Path: - return get_codex_home() / "plans" - - -def validate_plan_name(name: str) -> None: - if not name or not _NAME_RE.match(name): - raise ValueError( - "Invalid plan name. Use short, lower-case, hyphen-delimited names " - "(e.g., codex-rate-limit-overview)." - ) - - -def parse_frontmatter(path: Path) -> dict: - """Parse YAML frontmatter from a markdown file without reading the body.""" - with path.open("r", encoding="utf-8") as handle: - first = handle.readline() - if first.strip() != "---": - raise ValueError("Frontmatter must start with '---'.") - - data: dict[str, str] = {} - for line in handle: - stripped = line.strip() - if stripped == "---": - return data - if not stripped or stripped.startswith("#"): - continue - if ":" not in line: - raise ValueError(f"Invalid frontmatter line: {line.rstrip()}") - key, value = line.split(":", 1) - key = key.strip() - value = value.strip() - if value and len(value) >= 2 and value[0] == value[-1] and value[0] in ('"', "'"): - value = value[1:-1] - data[key] = value - - raise ValueError("Frontmatter must end with '---'.") diff --git a/skills/.system/plan/scripts/read_plan_frontmatter.py b/skills/.system/plan/scripts/read_plan_frontmatter.py deleted file mode 100644 index 1c881ee..0000000 --- a/skills/.system/plan/scripts/read_plan_frontmatter.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 -"""Read plan frontmatter without loading the full markdown body.""" - -from __future__ import annotations - -import argparse -import json -from pathlib import Path - -from plan_utils import parse_frontmatter - - -def main() -> int: - parser = argparse.ArgumentParser(description="Read name/description from plan frontmatter.") - parser.add_argument("plan_path", help="Path to the plan markdown file.") - parser.add_argument("--json", action="store_true", help="Emit JSON output.") - args = parser.parse_args() - - path = Path(args.plan_path).expanduser() - if not path.exists(): - raise SystemExit(f"Plan not found: {path}") - - data = parse_frontmatter(path) - name = data.get("name") - description = data.get("description") - if not name or not description: - raise SystemExit("Frontmatter must include name and description.") - - payload = {"name": name, "description": description, "path": str(path)} - if args.json: - print(json.dumps(payload)) - else: - print(f"name: {name}") - print(f"description: {description}") - print(f"path: {path}") - - return 0 - - -if __name__ == "__main__": - raise SystemExit(main())