mirror of
https://github.com/openai/skills.git
synced 2026-09-18 15:06:29 +03:00
Move plan skill to experimental (#22)
Rewrites `plan` skill as `create-plan` and removes all lifecycle management skills. Moved to a new `.experimental` bucket for evaluation and to get feedback on use.
This commit is contained in:
@@ -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
|
||||
[ ] <Step 1>
|
||||
[ ] <Step 2>
|
||||
[ ] <Step 3>
|
||||
[ ] <Step 4>
|
||||
[ ] <Step 5>
|
||||
[ ] <Step 6>
|
||||
|
||||
## Open questions
|
||||
- <Question 1>
|
||||
- <Question 2>
|
||||
- <Question 3>
|
||||
```
|
||||
|
||||
## 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)
|
||||
@@ -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 `<name>.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: <plan-name>
|
||||
description: <1-line summary>
|
||||
---
|
||||
```
|
||||
|
||||
### Implementation plan body template
|
||||
|
||||
```markdown
|
||||
# Plan
|
||||
|
||||
<1-3 sentences: intent, scope, and approach.>
|
||||
|
||||
## Requirements
|
||||
- <Requirement 1>
|
||||
- <Requirement 2>
|
||||
|
||||
## Scope
|
||||
- In:
|
||||
- Out:
|
||||
|
||||
## Files and entry points
|
||||
- <File/module/entry point 1>
|
||||
- <File/module/entry point 2>
|
||||
|
||||
## Data model / API changes
|
||||
- <If applicable, describe schema or contract changes>
|
||||
|
||||
## Action items
|
||||
[ ] <Step 1>
|
||||
[ ] <Step 2>
|
||||
[ ] <Step 3>
|
||||
[ ] <Step 4>
|
||||
[ ] <Step 5>
|
||||
[ ] <Step 6>
|
||||
|
||||
## Testing and validation
|
||||
- <Tests, commands, or validation steps>
|
||||
|
||||
## Risks and edge cases
|
||||
- <Risk 1>
|
||||
- <Risk 2>
|
||||
|
||||
## Open questions
|
||||
- <Question 1>
|
||||
- <Question 2>
|
||||
```
|
||||
|
||||
### Overview plan body template
|
||||
|
||||
```markdown
|
||||
# Plan
|
||||
|
||||
<1-3 sentences: intent and scope of the overview.>
|
||||
|
||||
## Overview
|
||||
<Describe the system, flow, or architecture at a high level.>
|
||||
|
||||
## Diagrams
|
||||
<Include text or Mermaid diagrams if helpful.>
|
||||
|
||||
## Key file references
|
||||
- <File/module/entry point 1>
|
||||
- <File/module/entry point 2>
|
||||
|
||||
## Auth / routing / behavior notes
|
||||
- <Capture relevant differences (e.g., auth modes, routing paths).>
|
||||
|
||||
## Current status
|
||||
- <What is live today vs pending work, if known.>
|
||||
|
||||
## 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."
|
||||
@@ -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
|
||||
- <Requirement 1>
|
||||
- <Requirement 2>
|
||||
|
||||
## Scope
|
||||
- In:
|
||||
- Out:
|
||||
|
||||
## Files and entry points
|
||||
- <File/module/entry point 1>
|
||||
- <File/module/entry point 2>
|
||||
|
||||
## Data model / API changes
|
||||
- <If applicable, describe schema or contract changes>
|
||||
|
||||
## Action items
|
||||
[ ] <Step 1>
|
||||
[ ] <Step 2>
|
||||
[ ] <Step 3>
|
||||
[ ] <Step 4>
|
||||
[ ] <Step 5>
|
||||
[ ] <Step 6>
|
||||
|
||||
## Testing and validation
|
||||
- <Tests, commands, or validation steps>
|
||||
|
||||
## Risks and edge cases
|
||||
- <Risk 1>
|
||||
- <Risk 2>
|
||||
|
||||
## Open questions
|
||||
- <Question 1>
|
||||
- <Question 2>
|
||||
"""
|
||||
|
||||
|
||||
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())
|
||||
@@ -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())
|
||||
@@ -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 '---'.")
|
||||
@@ -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())
|
||||
Reference in New Issue
Block a user