diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 3eb0176..4bea828 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -22,7 +22,8 @@ "tags": [ "guided-coding", "software-engineering", - "planning" + "planning", + "learning" ] } ] diff --git a/AGENTS.md b/AGENTS.md index 14fa2aa..89b7d89 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,7 +1,6 @@ # Repository Instructions -This repository is the canonical distribution of Guided Coding skills. The root package supports -the Agent Plugins standard and generates a dedicated Claude Code marketplace adapter. +This repository contains skills for Guided Coding. The root package supports the Agent Plugins standard and generates a dedicated Claude Code marketplace adapter. ## Skill authoring @@ -10,18 +9,13 @@ the Agent Plugins standard and generates a dedicated Claude Code marketplace ada - Keep the skill directory and frontmatter name identical. - State in every description that the skill runs only when explicitly requested. - Put Codex-specific interface and invocation policy in `agents/openai.yaml`. -- Configure Claude-specific names and frontmatter in - `tools/GuidedCoding.ClaudeGenerator/claude-skills.json`. -- Do not edit `claude-plugin/claude-skills` directly. Regenerate it with - `dotnet run --project tools/GuidedCoding.ClaudeGenerator`. -- Do not hand-author duplicate skill bodies for individual coding-agent harnesses. -- Keep the generated directory named `claude-skills`. A standard `claude-plugin/skills` directory - is also discovered by GitHub CLI and would duplicate the portable skills during publication. +- Configure Claude-specific names and frontmatter in `tools/GuidedCoding.ClaudeGenerator/claude-skills.json`. +- Do not edit `claude-plugin/claude-skills` directly. Regenerate it with `dotnet run --project tools/GuidedCoding.ClaudeGenerator`. +- Keep the generated directory named `claude-skills`. A standard `claude-plugin/skills` directory is also discovered by GitHub CLI and would duplicate the portable skills during publication. ## Manifests and versions -Keep the version synchronized across `plugin.json`, `claude-plugin/.claude-plugin/plugin.json`, -`.claude-plugin/marketplace.json`, and release tags. +Keep the version synchronized across `plugin.json`, `claude-plugin/.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, and release tags. Use Conventional Commits messages. diff --git a/CHANGELOG.md b/CHANGELOG.md index ec4e70e..6dab3d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,3 +10,4 @@ All notable changes to Guided Coding are documented here. - Require Plan Deviations documents for material departures from frozen plans. - Distribute Guided Coding as portable Agent Skills and an Agent Plugin. - Generate a Claude Code adapter with concise, manually invoked skill names. +- Add Guided Learning skills for Beginners and Advanced devs, track learning progress across sessions in a private, git-backed Guided Learning profile located at `$HOME/.guided-learning/profile.md`. diff --git a/README.md b/README.md index 17faa49..da4a520 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,24 @@ The full method is documented at | `guided-coding-write-plan` | After discussing an issue with your agent, write a plan or follow-up plan. | | `guided-coding-review-plan` | Review a plan draft against the repository (use in fresh conversation). | | `guided-coding-freeze-plan` | Freeze a plan by timestamping its file name and title. | +| `guided-coding-implement` | Implement a frozen plan and verify it through the repository's feedback loops. | +| `guided-coding-implement-show-me` | (Guided Learning) Implement a frozen plan step-by-step where the agent shows you complete code fragments that you enter and discuss. | +| `guided-coding-implement-coach-me` | (Guided Learning) Implement a frozen plan yourself through coached, verifiable milestones. The agent reveals implementation hints progressively. | | `guided-coding-write-deviations` | Summarize follow-up plans and record material implementation differences. | -All workflows require explicit user invocation. +In most Agent Harnesses, these workflows must be invoked explicitly. All workflows set `allow_implicit_invocation: false` for Agent Plugins and `disable-model-invocation: true` for Claude Plugins, respectively. + +## Guided Learning + +Guided Learning is a variation of Guided Coding where the agent does not implement the plan itself but teaches you how to implement it. The `guided-coding-implement-show-me` and `guided-coding-implement-coach-me` skills give you two approaches for Beginning and Advanced developers that want to increase their capabilities. The regular `guided-coding-implement` skill should be used for regular Guided Coding. + +| Stage | Skill | How the agent teaches | +| --- | --- | --- | +| **Beginning** | `guided-coding-implement-show-me` | Splits the plan into layers and presents complete code, fragment by fragment, for you to type and discuss. This is useful when you are new to a topic and want to quickly learn the fundamental concepts. The agent is always ready for your questions. | +| **Advancing** | `guided-coding-implement-coach-me` | Splits the plan into vertical slices that you implement yourself, reviews your work, and reveals hints progressively. This is useful when you are proficient at a topic and want more of a challenge to master your skills. | +| **Mastering** | `guided-coding-implement` | Regular Guided Coding: the Coding Agent implements the plan for you. No teaching involved. | + +Both skills track your progress in `~/.guided-learning/profile.md`: this file contains your preferences for how to be taught and a tree of knowledge areas, each at one of the three stages. On first use, the agent creates this folder as a local git repository (requires Git 2.28 or later) and commits every update locally with a message stating what it observed. This repo is never pushed by default, but you could share it across different machines. We recommend pushing to a private repository. The profile never ends up in your project repository, and you can edit or delete it at any time. ## Install @@ -35,7 +50,7 @@ gh skill install feO2x/guided-coding --all --agent universal --scope project ``` Install one skill by naming it, or add `--scope user` to make the installation available across -repositories. GitHub CLI's skill commands are currently in preview. +repositories. If you want to update, use the following command: @@ -64,7 +79,7 @@ claude plugin marketplace update guided-coding claude plugin update guided-coding@guided-coding --scope project ``` -Restart Claude Code afterwards. Only then will the updated skills be picked up. +> ⚠️ Restart Claude Code afterwards. Only then will the updated skills be picked up. ### Agent Plugin clients diff --git a/claude-plugin/.claude-plugin/plugin.json b/claude-plugin/.claude-plugin/plugin.json index 32372f2..bbe18c6 100644 --- a/claude-plugin/.claude-plugin/plugin.json +++ b/claude-plugin/.claude-plugin/plugin.json @@ -14,7 +14,8 @@ "keywords": [ "guided-coding", "software-engineering", - "planning" + "planning", + "learning" ], "skills": "./claude-skills" } diff --git a/claude-plugin/claude-skills/implement-coach-me/SKILL.md b/claude-plugin/claude-skills/implement-coach-me/SKILL.md new file mode 100644 index 0000000..27cde9d --- /dev/null +++ b/claude-plugin/claude-skills/implement-coach-me/SKILL.md @@ -0,0 +1,83 @@ +--- +name: implement-coach-me +description: "Coach a user through implementing a Guided Coding Frozen Plan one milestone at a time, progressively revealing help when needed. Run only when explicitly requested by the user." +license: "MIT" +disable-model-invocation: true +--- + +# Coach the User Through Implementing a Frozen Plan + +Your goal is to teach the user how to implement a Guided Coding Frozen Plan by letting them solve one milestone at a time. This skill is intended for the Advancing stage: describe each milestone, let the user implement it as a whole, review their work, and help them whenever they get stuck. + +The user makes every change to the repository: they write the code, run the feedback loops, tick Acceptance Criteria, and commit. + +## 1. Establish the Target + +Use the plan named by the user. If none is named, proceed only when there is exactly one plan in `ai-plans/` that has all its Acceptance Criteria unchecked, and it has the latest timestamp of all plans. Otherwise, ask for its path. + +Verify that the plan is frozen: its file name has a timestamp, and it has a `*Frozen at ...*` line below its title. If either marker is missing, explain that the Planning Phase is unfinished and stop. + +## 2. Read the Learning Profile + +`~/.guided-learning/profile.md` is the memory of Guided Learning across sessions. If it is missing, run `git init -b main "$HOME/.guided-learning"` unless that folder already is a git repository, copy `assets/profile.md` relative to this skill file there, and commit it. + +It holds the user's **Preferences** for how to be taught, such as the language to speak or explanation before code, and their **Knowledge**: a tree of areas, each at one of three stages: + +- **Beginning**: new to a domain, needs to learn the fundamental concepts and mechanisms, mostly by copying information. Adaptation and transformation of these do not happen yet. +- **Advancing**: fluent in the fundamentals and able to adapt them to new problems. Does not question the fundamentals. +- **Mastering**: able to adapt and transform concepts quickly, and to question or replace the fundamentals themselves. + +Teach the way the preferences ask. Let the stages of the areas the plan draws on decide how large you make the milestones, how much you explain, and how much help you offer at the start. The deepest node covering an area wins; technology and discipline nodes each apply to their own part of the work. Treat areas the profile does not cover as being at the stage this skill is intended for. What you observe always takes precedence over the profile. + +If you created the profile in this session, ask the user how they like to be taught before you create the roadmap. Otherwise, restate the preferences in one line when you present the roadmap, so that the user can object. + +## 3. Create the Milestone Roadmap + +Present the milestones as a short roadmap without giving away their implementations. + +Build it from vertical slices: each milestone cuts through the layers the plan touches and delivers behavior that runs end-to-end. At the Advancing stage, the user knows the individual areas and practices fitting them together, and a slice exposes a wrong design decision in the first milestone rather than the last. Keep the first slice thin, just enough to connect the layers, and widen it in the following ones. Give each slice one area in focus, ideally the one the user is least practiced in, so that you can tell what the milestone taught. If the plan does not split into slices, or the preferences ask for something else, choose another split that keeps one area in focus per milestone. + +Each milestone leaves a compilable codebase whose feedback loops pass and that can be committed. The user writes the milestone's tests as part of it, since they prove it works; add manual tests where needed, for example, for UI changes. A single milestone is fine if the plan is small enough. + +## 4. How to Work Through a Single Milestone + +Begin each milestone with a high-level description of what it should change and which parts of the plan it addresses. Mention relevant constraints, good places to start investigating, and how the milestone will be verified, but no implementation. Ask whether the user understands it, then let them design and implement it as a whole. + +While they work, answer questions about the codebase, language, framework, design, and tooling directly, and explain concepts and trade-offs whenever that helps them form their own solution. + +When the user signals completion, inspect what they actually changed. Explain what works and why, what does not yet satisfy the milestone or plan, and what to reconsider. Take valid solutions on their own terms, even when they differ from what you expected, and let the user revise until the milestone behaves as described. + +Then explain how to run the feedback loops and manual tests, or go through the output the user brings. When something fails, let them read the error first and teach them how to extract what matters from it. Once you verified the milestone, let the user tick the Acceptance Criteria it fully satisfies, if any, and commit them together with the milestone. Then update the learning profile and move on. + +## 5. Reveal Help Progressively + +Give the user room to solve the milestone independently, but do not let it turn into unproductive frustration. Answer questions about concepts and existing code directly, even when the answers help with the milestone. Toward the implementation itself, reveal one hint at a time: ask a focused question, restate an important invariant, point to similar code or documentation, teach the missing concept, name relevant APIs or types, describe how responsibilities interact, or give a precise implementation outline. Start at the level that fits the situation and the user's stage in the area, not mechanically with a question. The user can ask for more direct help at any time; after each hint, let them try again. + +Provide code only when explanations and outlines are not enough, and then only the smallest fragment that resolves the immediate obstacle, with an explanation. Never hand over the milestone's complete implementation, a patch, or a series of fragments that amounts to one. The goal is productive struggle, not withholding information. + +## 6. Update the Learning Profile + +Update the profile whenever something changed: when the user expresses a preference, after each milestone once the user committed it, and when the user stops early. Re-read the file right before you edit it, and commit to `main` using `git -C "$HOME/.guided-learning"` with a Conventional Commits message whose body states what you observed. Never create branches or push, and keep learning notes out of the repository you are working in. + +Change preferences only as the user says. Write knowledge nodes as nested list items: + +```markdown +- **** `` — covers , +``` + +Knowledge that would survive a switch to another technology stack belongs to a discipline, anything else to a technology. Nest at most three levels: + +- **Technologies**: `` → `` → ``, for example `.NET` → `EF Core` → `change tracking`. The ecosystem is the one whose package manager distributes the technology, so React belongs to `JavaScript`, which includes TypeScript. A technology outside any ecosystem, such as PostgreSQL, is a root itself. +- **Disciplines**: `` → `` → ``, for example `Automated testing` → `Test doubles` → `fakes`. Roots are limited to Algorithms and data structures, Software design and architecture, Automated testing, Data modeling and persistence, Security, Concurrency and distributed systems, Performance, Delivery and operations, and User interface design. Ask the user before you add another one. + +After each milestone, add a node for every area it drew on that the profile does not cover yet. Reuse existing nodes, and name technologies the way their official documentation does. Add a child only when its stage differs from its parent's; otherwise, list it in the parent's optional `covers`. + +Move a stage only on what you observed. Promote at most one step per plan: to Advancing when the user carried a milestone in that area without being handed the implementation, to Mastering when they shaped the design or pushed back on the plan for a reason that held up. Correct a wrong node any distance. Change the node where you saw the evidence, and a parent only when your evidence covers all of it. + +## 7. Handle Plan Issues + +If a plan decision is wrong or an Acceptance Criterion cannot be met as written, work out a solution or workaround and teach it like any other part of the plan; if you cannot, report it to the user. Ideally, you catch this while creating the roadmap. You decide whether a problem that needs external input interrupts or aborts the Implementing Phase. In the Guiding Phase, the reviewer decides how to proceed with your findings. + +## 8. After the Last Milestone + +Summarize what you and the user accomplished and point them to the Guiding Phase. Unless you faced plan issues, all Acceptance Criteria should be ticked. diff --git a/claude-plugin/claude-skills/implement-coach-me/assets/profile.md b/claude-plugin/claude-skills/implement-coach-me/assets/profile.md new file mode 100644 index 0000000..829a49a --- /dev/null +++ b/claude-plugin/claude-skills/implement-coach-me/assets/profile.md @@ -0,0 +1,5 @@ +# Guided Learning Profile + +## Preferences + +## Knowledge diff --git a/claude-plugin/claude-skills/implement-show-me/SKILL.md b/claude-plugin/claude-skills/implement-show-me/SKILL.md new file mode 100644 index 0000000..c905f0e --- /dev/null +++ b/claude-plugin/claude-skills/implement-show-me/SKILL.md @@ -0,0 +1,79 @@ +--- +name: implement-show-me +description: "Instruct a user how to implement a Guided Coding Frozen Plan by presenting and explaining complete code. Run only when explicitly requested by the user." +license: "MIT" +disable-model-invocation: true +--- + +# Show the User How to Implement a Frozen Plan + +Your goal is to teach the user how to implement a Guided Coding Frozen Plan by splitting it into teachable milestones. For each milestone, you present and explain the code one fragment at a time; the user enters it, runs the feedback loops and manual tests, and asks about whatever is unclear. This skill is intended for the Beginning stage, so always present complete fragments: the user should not have to figure out any part of the implementation. + +The user makes every change to the repository, including commits. Typing the code by hand is where much of the learning happens, so encourage it over copying and pasting. + +## 1. Establish the Target + +Use the plan named by the user. If none is named, proceed only when there is exactly one plan in `ai-plans/` that has all its Acceptance Criteria unchecked, and it has the latest timestamp of all plans. Otherwise, ask for its path. + +Verify that the plan is frozen: its file name has a timestamp, and it has a `*Frozen at ...*` line below its title. If either marker is missing, explain that the Planning Phase is unfinished and stop. + +## 2. Read the Learning Profile + +`~/.guided-learning/profile.md` is the memory of Guided Learning across sessions. If it is missing, run `git init -b main "$HOME/.guided-learning"` unless that folder already is a git repository, copy `assets/profile.md` relative to this skill file there, and commit it. + +It holds the user's **Preferences** for how to be taught, such as the language to speak or explanation before code, and their **Knowledge**: a tree of areas, each at one of three stages: + +- **Beginning**: new to a domain, needs to learn the fundamental concepts and mechanisms, mostly by copying information. Adaptation and transformation of these do not happen yet. +- **Advancing**: fluent in the fundamentals and able to adapt them to new problems. Does not question the fundamentals. +- **Mastering**: able to adapt and transform concepts quickly, and to question or replace the fundamentals themselves. + +Teach the way the preferences ask. Let the stages of the areas the plan draws on decide how large you make the milestones, how much you explain, and how much help you offer at the start. The deepest node covering an area wins; technology and discipline nodes each apply to their own part of the work. Treat areas the profile does not cover as being at the stage this skill is intended for. What you observe always takes precedence over the profile. + +If you created the profile in this session, ask the user how they like to be taught before you create the roadmap. Otherwise, restate the preferences in one line when you present the roadmap, so that the user can object. + +## 3. Create the Milestone Roadmap + +Present the milestones as a short roadmap without any code. + +Build it layer by layer, each milestone putting one area in focus and building on the previous ones. For a backend feature, this could be the domain model, then database access, a service, and finally the endpoint. Since nothing runs end-to-end before the last layer, explain in the roadmap how the layers will connect, and remind the user where the current layer sits whenever a milestone begins. If the plan has no layers, or the preferences ask for something else, choose another split that keeps one area in focus per milestone. + +Each milestone leaves a compilable codebase whose feedback loops pass and that can be committed. Its tests are part of the code you present, since they prove the milestone works; add manual tests where needed, for example, for UI changes. A single milestone is fine if that teaches the plan best. + +## 4. How to Work Through a Single Milestone + +Begin each milestone with a high-level description of the changes it introduces and which parts of the plan it addresses, and ask whether the user understands it. + +Then present the code fragment by fragment, never all at once, so that the user builds their mental model of the codebase step by step. Verify each fragment once the user has entered it. + +When all fragments are in place, explain how to run the feedback loops or manual tests. Before the user runs them, ask what they expect to happen and why. Their answer tells you whether your explanation landed. + +Answer questions about the code at any point. When something does not compile or a test fails, let the user read the error first, then point out what to focus on, for example, the information an exception carries. + +Once the milestone behaves as described and you verified it, let the user tick the Acceptance Criteria it fully satisfies, if any, from `- [ ]` to `- [x]`, and commit them together with the milestone. Then update the learning profile and move on. + +## 5. Update the Learning Profile + +Update the profile whenever something changed: when the user expresses a preference, after each milestone once the user committed it, and when the user stops early. Re-read the file right before you edit it, and commit to `main` using `git -C "$HOME/.guided-learning"` with a Conventional Commits message whose body states what you observed. Never create branches or push, and keep learning notes out of the repository you are working in. + +Change preferences only as the user says. Write knowledge nodes as nested list items: + +```markdown +- **** `` — covers , +``` + +Knowledge that would survive a switch to another technology stack belongs to a discipline, anything else to a technology. Nest at most three levels: + +- **Technologies**: `` → `` → ``, for example `.NET` → `EF Core` → `change tracking`. The ecosystem is the one whose package manager distributes the technology, so React belongs to `JavaScript`, which includes TypeScript. A technology outside any ecosystem, such as PostgreSQL, is a root itself. +- **Disciplines**: `` → `` → ``, for example `Automated testing` → `Test doubles` → `fakes`. Roots are limited to Algorithms and data structures, Software design and architecture, Automated testing, Data modeling and persistence, Security, Concurrency and distributed systems, Performance, Delivery and operations, and User interface design. Ask the user before you add another one. + +After each milestone, add a node for every area it drew on that the profile does not cover yet. Reuse existing nodes, and name technologies the way their official documentation does. Add a child only when its stage differs from its parent's; otherwise, list it in the parent's optional `covers`. + +Move a stage only on what you observed. Promote at most one step per plan: to Advancing when the user carried a milestone in that area without being handed the implementation, to Mastering when they shaped the design or pushed back on the plan for a reason that held up. Correct a wrong node any distance. This skill never moves a node above Beginning, because it hands over the implementation. Change the node where you saw the evidence, and a parent only when your evidence covers all of it. + +## 6. Handle Plan Issues + +If a plan decision is wrong or an Acceptance Criterion cannot be met as written, work out a solution or workaround and teach it like any other part of the plan; if you cannot, report it to the user. Ideally, you catch this while creating the roadmap. You decide whether a problem that needs external input interrupts or aborts the Implementing Phase. In the Guiding Phase, the reviewer decides how to proceed with your findings. + +## 7. After the Last Milestone + +Summarize what you and the user accomplished and point them to the Guiding Phase. Unless you faced plan issues, all Acceptance Criteria should be ticked. diff --git a/claude-plugin/claude-skills/implement-show-me/assets/profile.md b/claude-plugin/claude-skills/implement-show-me/assets/profile.md new file mode 100644 index 0000000..829a49a --- /dev/null +++ b/claude-plugin/claude-skills/implement-show-me/assets/profile.md @@ -0,0 +1,5 @@ +# Guided Learning Profile + +## Preferences + +## Knowledge diff --git a/claude-plugin/claude-skills/implement/SKILL.md b/claude-plugin/claude-skills/implement/SKILL.md new file mode 100644 index 0000000..d06ee4b --- /dev/null +++ b/claude-plugin/claude-skills/implement/SKILL.md @@ -0,0 +1,32 @@ +--- +name: implement +description: "Implement a Guided Coding Frozen Plan independently and verify the implementation through the repository's feedback loops. Run only when explicitly requested by the user." +license: "MIT" +disable-model-invocation: true +--- + +# Implement a Frozen Plan + +Your goal is to implement a Frozen Plan created in the Planning Phase of Guided Coding. Once you are finished implementing, a reviewer will check your results in the Guiding Phase. + +## 1. Establish the Target + +Use the plan named by the user. If none is named, proceed only when there is exactly one plan in `ai-plans/` that has all its Acceptance Criteria unchecked, and it has the latest timestamp of all plans. Otherwise, ask for its path. + +Verify that the plan is frozen: its file name has a timestamp, and it has a `*Frozen at ...*` line below its title. If either marker is missing, explain that the Planning Phase is unfinished and stop. + +## 2. Implement and Verify + +Implement the plan, use the feedback loops to verify your code changes. + +The only allowed plan edit is ticking an Acceptance Criterion from `- [ ]` to `- [x]`. Never check a criterion unless it is genuinely satisfied by a feedback loop. The reviewer would otherwise have to identify the gap later in the Guiding Phase, which is one of the hardest errors to spot. + +## 3. Handle Plan Issues + +If a plan decision is wrong or an Acceptance Criterion cannot be met as written, try to solve it or find a workaround, and report it. If a problem genuinely cannot be solved, that's totally fine - simply report it. + +In the Guiding Phase, the reviewer can decide how to proceed with your findings. + +## 4. Finish + +Report what was implemented, which feedback loops ran and their results, which Acceptance Criteria are checked, and which remain unchecked and why. Do not create commits, open or update a pull request, publish anything, etc. - unless the user asked you to do so. diff --git a/claude-plugin/claude-skills/setup/SKILL.md b/claude-plugin/claude-skills/setup/SKILL.md index ea639cb..75f132c 100644 --- a/claude-plugin/claude-skills/setup/SKILL.md +++ b/claude-plugin/claude-skills/setup/SKILL.md @@ -11,9 +11,9 @@ Your goal is to set up or upgrade Guided Coding in the current repository. After Guided Coding needs these artifacts: -- `AGENTS.md` at the repository root, listing the feedback loops and the rules for implementing a Frozen Plan. +- `AGENTS.md` at the repository root, listing the feedback loops and pointing to `ai-plans/AGENTS.md`. - `ai-plans/`, the folder holding all plans and Plan Deviations documents. -- `ai-plans/AGENTS.md`, describing the folder and its file naming rules. +- `ai-plans/AGENTS.md`, describing the folder, its file naming rules, and how Frozen Plans are treated. The outcome must be idempotent. Running this skill on a repository sets the artifacts up from scratch, brings outdated ones up to date, or leaves current ones untouched. Content unrelated to Guided Coding, such as project-specific instructions or user-authored notes, is never changed. @@ -37,9 +37,7 @@ Create `AGENTS.md` in the repository root if it does not exist. Otherwise, make Ensure it contains: 1. `## Feedback Loops`: each command and what it verifies. Report to the user when no feedback loops could be found, and warn that `guided-coding-write-plan` refuses to write plans until at least one is listed. -2. `## Guided Coding`: a link to `ai-plans/AGENTS.md`, and the rules for implementing a Frozen Plan: - - plans in `ai-plans/` are frozen once they carry a timestamp in their file name and a `*Frozen at ...*` line below their title. - - the only permitted edit to a Frozen Plan is checking an Acceptance Criterion from `- [ ]` to `- [x]` after the implementation and the relevant feedback loops verify it. Unmet criteria stay unchecked. +2. `## Guided Coding`: a link to `ai-plans/AGENTS.md`, noting that it holds the file naming conventions and the rules for working with Frozen Plans. Do not restate those rules here; they live next to the plans they govern, and agents pick them up when they read the folder. If both sections already exist and are current, leave the file alone. diff --git a/plugin.json b/plugin.json index db95967..2d5dcd1 100644 --- a/plugin.json +++ b/plugin.json @@ -13,6 +13,7 @@ "keywords": [ "guided-coding", "software-engineering", - "planning" + "planning", + "learning" ] } diff --git a/skills/guided-coding-implement-coach-me/SKILL.md b/skills/guided-coding-implement-coach-me/SKILL.md new file mode 100644 index 0000000..a3a7b69 --- /dev/null +++ b/skills/guided-coding-implement-coach-me/SKILL.md @@ -0,0 +1,82 @@ +--- +name: guided-coding-implement-coach-me +description: Coach a user through implementing a Guided Coding Frozen Plan one milestone at a time, progressively revealing help when needed. Run only when explicitly requested by the user. +license: MIT +--- + +# Coach the User Through Implementing a Frozen Plan + +Your goal is to teach the user how to implement a Guided Coding Frozen Plan by letting them solve one milestone at a time. This skill is intended for the Advancing stage: describe each milestone, let the user implement it as a whole, review their work, and help them whenever they get stuck. + +The user makes every change to the repository: they write the code, run the feedback loops, tick Acceptance Criteria, and commit. + +## 1. Establish the Target + +Use the plan named by the user. If none is named, proceed only when there is exactly one plan in `ai-plans/` that has all its Acceptance Criteria unchecked, and it has the latest timestamp of all plans. Otherwise, ask for its path. + +Verify that the plan is frozen: its file name has a timestamp, and it has a `*Frozen at ...*` line below its title. If either marker is missing, explain that the Planning Phase is unfinished and stop. + +## 2. Read the Learning Profile + +`~/.guided-learning/profile.md` is the memory of Guided Learning across sessions. If it is missing, run `git init -b main "$HOME/.guided-learning"` unless that folder already is a git repository, copy `assets/profile.md` relative to this skill file there, and commit it. + +It holds the user's **Preferences** for how to be taught, such as the language to speak or explanation before code, and their **Knowledge**: a tree of areas, each at one of three stages: + +- **Beginning**: new to a domain, needs to learn the fundamental concepts and mechanisms, mostly by copying information. Adaptation and transformation of these do not happen yet. +- **Advancing**: fluent in the fundamentals and able to adapt them to new problems. Does not question the fundamentals. +- **Mastering**: able to adapt and transform concepts quickly, and to question or replace the fundamentals themselves. + +Teach the way the preferences ask. Let the stages of the areas the plan draws on decide how large you make the milestones, how much you explain, and how much help you offer at the start. The deepest node covering an area wins; technology and discipline nodes each apply to their own part of the work. Treat areas the profile does not cover as being at the stage this skill is intended for. What you observe always takes precedence over the profile. + +If you created the profile in this session, ask the user how they like to be taught before you create the roadmap. Otherwise, restate the preferences in one line when you present the roadmap, so that the user can object. + +## 3. Create the Milestone Roadmap + +Present the milestones as a short roadmap without giving away their implementations. + +Build it from vertical slices: each milestone cuts through the layers the plan touches and delivers behavior that runs end-to-end. At the Advancing stage, the user knows the individual areas and practices fitting them together, and a slice exposes a wrong design decision in the first milestone rather than the last. Keep the first slice thin, just enough to connect the layers, and widen it in the following ones. Give each slice one area in focus, ideally the one the user is least practiced in, so that you can tell what the milestone taught. If the plan does not split into slices, or the preferences ask for something else, choose another split that keeps one area in focus per milestone. + +Each milestone leaves a compilable codebase whose feedback loops pass and that can be committed. The user writes the milestone's tests as part of it, since they prove it works; add manual tests where needed, for example, for UI changes. A single milestone is fine if the plan is small enough. + +## 4. How to Work Through a Single Milestone + +Begin each milestone with a high-level description of what it should change and which parts of the plan it addresses. Mention relevant constraints, good places to start investigating, and how the milestone will be verified, but no implementation. Ask whether the user understands it, then let them design and implement it as a whole. + +While they work, answer questions about the codebase, language, framework, design, and tooling directly, and explain concepts and trade-offs whenever that helps them form their own solution. + +When the user signals completion, inspect what they actually changed. Explain what works and why, what does not yet satisfy the milestone or plan, and what to reconsider. Take valid solutions on their own terms, even when they differ from what you expected, and let the user revise until the milestone behaves as described. + +Then explain how to run the feedback loops and manual tests, or go through the output the user brings. When something fails, let them read the error first and teach them how to extract what matters from it. Once you verified the milestone, let the user tick the Acceptance Criteria it fully satisfies, if any, and commit them together with the milestone. Then update the learning profile and move on. + +## 5. Reveal Help Progressively + +Give the user room to solve the milestone independently, but do not let it turn into unproductive frustration. Answer questions about concepts and existing code directly, even when the answers help with the milestone. Toward the implementation itself, reveal one hint at a time: ask a focused question, restate an important invariant, point to similar code or documentation, teach the missing concept, name relevant APIs or types, describe how responsibilities interact, or give a precise implementation outline. Start at the level that fits the situation and the user's stage in the area, not mechanically with a question. The user can ask for more direct help at any time; after each hint, let them try again. + +Provide code only when explanations and outlines are not enough, and then only the smallest fragment that resolves the immediate obstacle, with an explanation. Never hand over the milestone's complete implementation, a patch, or a series of fragments that amounts to one. The goal is productive struggle, not withholding information. + +## 6. Update the Learning Profile + +Update the profile whenever something changed: when the user expresses a preference, after each milestone once the user committed it, and when the user stops early. Re-read the file right before you edit it, and commit to `main` using `git -C "$HOME/.guided-learning"` with a Conventional Commits message whose body states what you observed. Never create branches or push, and keep learning notes out of the repository you are working in. + +Change preferences only as the user says. Write knowledge nodes as nested list items: + +```markdown +- **** `` — covers , +``` + +Knowledge that would survive a switch to another technology stack belongs to a discipline, anything else to a technology. Nest at most three levels: + +- **Technologies**: `` → `` → ``, for example `.NET` → `EF Core` → `change tracking`. The ecosystem is the one whose package manager distributes the technology, so React belongs to `JavaScript`, which includes TypeScript. A technology outside any ecosystem, such as PostgreSQL, is a root itself. +- **Disciplines**: `` → `` → ``, for example `Automated testing` → `Test doubles` → `fakes`. Roots are limited to Algorithms and data structures, Software design and architecture, Automated testing, Data modeling and persistence, Security, Concurrency and distributed systems, Performance, Delivery and operations, and User interface design. Ask the user before you add another one. + +After each milestone, add a node for every area it drew on that the profile does not cover yet. Reuse existing nodes, and name technologies the way their official documentation does. Add a child only when its stage differs from its parent's; otherwise, list it in the parent's optional `covers`. + +Move a stage only on what you observed. Promote at most one step per plan: to Advancing when the user carried a milestone in that area without being handed the implementation, to Mastering when they shaped the design or pushed back on the plan for a reason that held up. Correct a wrong node any distance. Change the node where you saw the evidence, and a parent only when your evidence covers all of it. + +## 7. Handle Plan Issues + +If a plan decision is wrong or an Acceptance Criterion cannot be met as written, work out a solution or workaround and teach it like any other part of the plan; if you cannot, report it to the user. Ideally, you catch this while creating the roadmap. You decide whether a problem that needs external input interrupts or aborts the Implementing Phase. In the Guiding Phase, the reviewer decides how to proceed with your findings. + +## 8. After the Last Milestone + +Summarize what you and the user accomplished and point them to the Guiding Phase. Unless you faced plan issues, all Acceptance Criteria should be ticked. diff --git a/skills/guided-coding-implement-coach-me/agents/openai.yaml b/skills/guided-coding-implement-coach-me/agents/openai.yaml new file mode 100644 index 0000000..e7a08d2 --- /dev/null +++ b/skills/guided-coding-implement-coach-me/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Coach Me Through a Guided Coding Plan" + short_description: "Solve each milestone yourself with coaching" + default_prompt: "Use $guided-coding-implement-coach-me to coach me through implementing the Frozen Plan by solving each milestone myself." + +policy: + allow_implicit_invocation: false diff --git a/skills/guided-coding-implement-coach-me/assets/profile.md b/skills/guided-coding-implement-coach-me/assets/profile.md new file mode 100644 index 0000000..829a49a --- /dev/null +++ b/skills/guided-coding-implement-coach-me/assets/profile.md @@ -0,0 +1,5 @@ +# Guided Learning Profile + +## Preferences + +## Knowledge diff --git a/skills/guided-coding-implement-show-me/SKILL.md b/skills/guided-coding-implement-show-me/SKILL.md new file mode 100644 index 0000000..7dde13a --- /dev/null +++ b/skills/guided-coding-implement-show-me/SKILL.md @@ -0,0 +1,78 @@ +--- +name: guided-coding-implement-show-me +description: Instruct a user how to implement a Guided Coding Frozen Plan by presenting and explaining complete code. Run only when explicitly requested by the user. +license: MIT +--- + +# Show the User How to Implement a Frozen Plan + +Your goal is to teach the user how to implement a Guided Coding Frozen Plan by splitting it into teachable milestones. For each milestone, you present and explain the code one fragment at a time; the user enters it, runs the feedback loops and manual tests, and asks about whatever is unclear. This skill is intended for the Beginning stage, so always present complete fragments: the user should not have to figure out any part of the implementation. + +The user makes every change to the repository, including commits. Typing the code by hand is where much of the learning happens, so encourage it over copying and pasting. + +## 1. Establish the Target + +Use the plan named by the user. If none is named, proceed only when there is exactly one plan in `ai-plans/` that has all its Acceptance Criteria unchecked, and it has the latest timestamp of all plans. Otherwise, ask for its path. + +Verify that the plan is frozen: its file name has a timestamp, and it has a `*Frozen at ...*` line below its title. If either marker is missing, explain that the Planning Phase is unfinished and stop. + +## 2. Read the Learning Profile + +`~/.guided-learning/profile.md` is the memory of Guided Learning across sessions. If it is missing, run `git init -b main "$HOME/.guided-learning"` unless that folder already is a git repository, copy `assets/profile.md` relative to this skill file there, and commit it. + +It holds the user's **Preferences** for how to be taught, such as the language to speak or explanation before code, and their **Knowledge**: a tree of areas, each at one of three stages: + +- **Beginning**: new to a domain, needs to learn the fundamental concepts and mechanisms, mostly by copying information. Adaptation and transformation of these do not happen yet. +- **Advancing**: fluent in the fundamentals and able to adapt them to new problems. Does not question the fundamentals. +- **Mastering**: able to adapt and transform concepts quickly, and to question or replace the fundamentals themselves. + +Teach the way the preferences ask. Let the stages of the areas the plan draws on decide how large you make the milestones, how much you explain, and how much help you offer at the start. The deepest node covering an area wins; technology and discipline nodes each apply to their own part of the work. Treat areas the profile does not cover as being at the stage this skill is intended for. What you observe always takes precedence over the profile. + +If you created the profile in this session, ask the user how they like to be taught before you create the roadmap. Otherwise, restate the preferences in one line when you present the roadmap, so that the user can object. + +## 3. Create the Milestone Roadmap + +Present the milestones as a short roadmap without any code. + +Build it layer by layer, each milestone putting one area in focus and building on the previous ones. For a backend feature, this could be the domain model, then database access, a service, and finally the endpoint. Since nothing runs end-to-end before the last layer, explain in the roadmap how the layers will connect, and remind the user where the current layer sits whenever a milestone begins. If the plan has no layers, or the preferences ask for something else, choose another split that keeps one area in focus per milestone. + +Each milestone leaves a compilable codebase whose feedback loops pass and that can be committed. Its tests are part of the code you present, since they prove the milestone works; add manual tests where needed, for example, for UI changes. A single milestone is fine if that teaches the plan best. + +## 4. How to Work Through a Single Milestone + +Begin each milestone with a high-level description of the changes it introduces and which parts of the plan it addresses, and ask whether the user understands it. + +Then present the code fragment by fragment, never all at once, so that the user builds their mental model of the codebase step by step. Verify each fragment once the user has entered it. + +When all fragments are in place, explain how to run the feedback loops or manual tests. Before the user runs them, ask what they expect to happen and why. Their answer tells you whether your explanation landed. + +Answer questions about the code at any point. When something does not compile or a test fails, let the user read the error first, then point out what to focus on, for example, the information an exception carries. + +Once the milestone behaves as described and you verified it, let the user tick the Acceptance Criteria it fully satisfies, if any, from `- [ ]` to `- [x]`, and commit them together with the milestone. Then update the learning profile and move on. + +## 5. Update the Learning Profile + +Update the profile whenever something changed: when the user expresses a preference, after each milestone once the user committed it, and when the user stops early. Re-read the file right before you edit it, and commit to `main` using `git -C "$HOME/.guided-learning"` with a Conventional Commits message whose body states what you observed. Never create branches or push, and keep learning notes out of the repository you are working in. + +Change preferences only as the user says. Write knowledge nodes as nested list items: + +```markdown +- **** `` — covers , +``` + +Knowledge that would survive a switch to another technology stack belongs to a discipline, anything else to a technology. Nest at most three levels: + +- **Technologies**: `` → `` → ``, for example `.NET` → `EF Core` → `change tracking`. The ecosystem is the one whose package manager distributes the technology, so React belongs to `JavaScript`, which includes TypeScript. A technology outside any ecosystem, such as PostgreSQL, is a root itself. +- **Disciplines**: `` → `` → ``, for example `Automated testing` → `Test doubles` → `fakes`. Roots are limited to Algorithms and data structures, Software design and architecture, Automated testing, Data modeling and persistence, Security, Concurrency and distributed systems, Performance, Delivery and operations, and User interface design. Ask the user before you add another one. + +After each milestone, add a node for every area it drew on that the profile does not cover yet. Reuse existing nodes, and name technologies the way their official documentation does. Add a child only when its stage differs from its parent's; otherwise, list it in the parent's optional `covers`. + +Move a stage only on what you observed. Promote at most one step per plan: to Advancing when the user carried a milestone in that area without being handed the implementation, to Mastering when they shaped the design or pushed back on the plan for a reason that held up. Correct a wrong node any distance. This skill never moves a node above Beginning, because it hands over the implementation. Change the node where you saw the evidence, and a parent only when your evidence covers all of it. + +## 6. Handle Plan Issues + +If a plan decision is wrong or an Acceptance Criterion cannot be met as written, work out a solution or workaround and teach it like any other part of the plan; if you cannot, report it to the user. Ideally, you catch this while creating the roadmap. You decide whether a problem that needs external input interrupts or aborts the Implementing Phase. In the Guiding Phase, the reviewer decides how to proceed with your findings. + +## 7. After the Last Milestone + +Summarize what you and the user accomplished and point them to the Guiding Phase. Unless you faced plan issues, all Acceptance Criteria should be ticked. diff --git a/skills/guided-coding-implement-show-me/agents/openai.yaml b/skills/guided-coding-implement-show-me/agents/openai.yaml new file mode 100644 index 0000000..e115b46 --- /dev/null +++ b/skills/guided-coding-implement-show-me/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Show Me How to Implement a Guided Coding Plan" + short_description: "Enter and discuss worked code, one milestone at a time" + default_prompt: "Use $guided-coding-implement-show-me to help me implement the Frozen Plan through worked code examples that I enter and discuss." + +policy: + allow_implicit_invocation: false diff --git a/skills/guided-coding-implement-show-me/assets/profile.md b/skills/guided-coding-implement-show-me/assets/profile.md new file mode 100644 index 0000000..829a49a --- /dev/null +++ b/skills/guided-coding-implement-show-me/assets/profile.md @@ -0,0 +1,5 @@ +# Guided Learning Profile + +## Preferences + +## Knowledge diff --git a/skills/guided-coding-implement/SKILL.md b/skills/guided-coding-implement/SKILL.md new file mode 100644 index 0000000..2330b11 --- /dev/null +++ b/skills/guided-coding-implement/SKILL.md @@ -0,0 +1,31 @@ +--- +name: guided-coding-implement +description: Implement a Guided Coding Frozen Plan independently and verify the implementation through the repository's feedback loops. Run only when explicitly requested by the user. +license: MIT +--- + +# Implement a Frozen Plan + +Your goal is to implement a Frozen Plan created in the Planning Phase of Guided Coding. Once you are finished implementing, a reviewer will check your results in the Guiding Phase. + +## 1. Establish the Target + +Use the plan named by the user. If none is named, proceed only when there is exactly one plan in `ai-plans/` that has all its Acceptance Criteria unchecked, and it has the latest timestamp of all plans. Otherwise, ask for its path. + +Verify that the plan is frozen: its file name has a timestamp, and it has a `*Frozen at ...*` line below its title. If either marker is missing, explain that the Planning Phase is unfinished and stop. + +## 2. Implement and Verify + +Implement the plan, use the feedback loops to verify your code changes. + +The only allowed plan edit is ticking an Acceptance Criterion from `- [ ]` to `- [x]`. Never check a criterion unless it is genuinely satisfied by a feedback loop. The reviewer would otherwise have to identify the gap later in the Guiding Phase, which is one of the hardest errors to spot. + +## 3. Handle Plan Issues + +If a plan decision is wrong or an Acceptance Criterion cannot be met as written, try to solve it or find a workaround, and report it. If a problem genuinely cannot be solved, that's totally fine - simply report it. + +In the Guiding Phase, the reviewer can decide how to proceed with your findings. + +## 4. Finish + +Report what was implemented, which feedback loops ran and their results, which Acceptance Criteria are checked, and which remain unchecked and why. Do not create commits, open or update a pull request, publish anything, etc. - unless the user asked you to do so. diff --git a/skills/guided-coding-implement/agents/openai.yaml b/skills/guided-coding-implement/agents/openai.yaml new file mode 100644 index 0000000..afc4af0 --- /dev/null +++ b/skills/guided-coding-implement/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "Implement Guided Coding Plan" + short_description: "Implement a Frozen Plan with repository feedback loops" + default_prompt: "Use $guided-coding-implement to implement this Frozen Plan and run its documented feedback loops." + +policy: + allow_implicit_invocation: false diff --git a/skills/guided-coding-setup/SKILL.md b/skills/guided-coding-setup/SKILL.md index f8f4b5b..4d97f5d 100644 --- a/skills/guided-coding-setup/SKILL.md +++ b/skills/guided-coding-setup/SKILL.md @@ -10,9 +10,9 @@ Your goal is to set up or upgrade Guided Coding in the current repository. After Guided Coding needs these artifacts: -- `AGENTS.md` at the repository root, listing the feedback loops and the rules for implementing a Frozen Plan. +- `AGENTS.md` at the repository root, listing the feedback loops and pointing to `ai-plans/AGENTS.md`. - `ai-plans/`, the folder holding all plans and Plan Deviations documents. -- `ai-plans/AGENTS.md`, describing the folder and its file naming rules. +- `ai-plans/AGENTS.md`, describing the folder, its file naming rules, and how Frozen Plans are treated. The outcome must be idempotent. Running this skill on a repository sets the artifacts up from scratch, brings outdated ones up to date, or leaves current ones untouched. Content unrelated to Guided Coding, such as project-specific instructions or user-authored notes, is never changed. @@ -36,9 +36,7 @@ Create `AGENTS.md` in the repository root if it does not exist. Otherwise, make Ensure it contains: 1. `## Feedback Loops`: each command and what it verifies. Report to the user when no feedback loops could be found, and warn that `guided-coding-write-plan` refuses to write plans until at least one is listed. -2. `## Guided Coding`: a link to `ai-plans/AGENTS.md`, and the rules for implementing a Frozen Plan: - - plans in `ai-plans/` are frozen once they carry a timestamp in their file name and a `*Frozen at ...*` line below their title. - - the only permitted edit to a Frozen Plan is checking an Acceptance Criterion from `- [ ]` to `- [x]` after the implementation and the relevant feedback loops verify it. Unmet criteria stay unchecked. +2. `## Guided Coding`: a link to `ai-plans/AGENTS.md`, noting that it holds the file naming conventions and the rules for working with Frozen Plans. Do not restate those rules here; they live next to the plans they govern, and agents pick them up when they read the folder. If both sections already exist and are current, leave the file alone. diff --git a/tests/GuidedCoding.Tests/PackageValidationTests.cs b/tests/GuidedCoding.Tests/PackageValidationTests.cs index dd260ed..05e9f51 100644 --- a/tests/GuidedCoding.Tests/PackageValidationTests.cs +++ b/tests/GuidedCoding.Tests/PackageValidationTests.cs @@ -16,6 +16,9 @@ public sealed class PackageValidationTests private static readonly string[] ExpectedPortableSkillNames = [ "guided-coding-freeze-plan", + "guided-coding-implement", + "guided-coding-implement-coach-me", + "guided-coding-implement-show-me", "guided-coding-review-plan", "guided-coding-setup", "guided-coding-write-deviations", @@ -27,12 +30,44 @@ public sealed class PackageValidationTests ) { ["guided-coding-freeze-plan"] = "freeze-plan", + ["guided-coding-implement"] = "implement", + ["guided-coding-implement-coach-me"] = "implement-coach-me", + ["guided-coding-implement-show-me"] = "implement-show-me", ["guided-coding-review-plan"] = "review-plan", ["guided-coding-setup"] = "setup", ["guided-coding-write-deviations"] = "write-deviations", ["guided-coding-write-plan"] = "write-plan" }; + private static readonly string[] GuidedLearningSkillNames = + [ + "guided-coding-implement-coach-me", + "guided-coding-implement-show-me" + ]; + + // The Guided Learning skills share every section except these, which differ on purpose. + private static readonly string[] GuidedLearningSectionsThatDiffer = + [ + "Create the Milestone Roadmap", + "How to Work Through a Single Milestone", + "Reveal Help Progressively", + "Update the Learning Profile" + ]; + + private static readonly (string Section, string[] SkillNames)[] SharedSections = + [ + ("Establish the Target", ["guided-coding-implement", .. GuidedLearningSkillNames]) + ]; + + // A snippet is the list that follows its lead-in line. + private static readonly (string LeadIn, string[] SkillNames)[] SharedSnippets = + [ + ( + "Use these commands to get it:", + ["guided-coding-freeze-plan", "guided-coding-write-deviations"] + ) + ]; + private static readonly string[] ForbiddenFrontmatterFields = [ "allowed-tools", @@ -50,6 +85,14 @@ public sealed class PackageValidationTests private static string RepositoryRoot { get; } = FindRepositoryRoot(); + public static TheoryData SharedSectionNames => new ( + SharedSections.Select(shared => shared.Section) + ); + + public static TheoryData SharedSnippetLeadIns => new ( + SharedSnippets.Select(shared => shared.LeadIn) + ); + [Fact] public void ManifestsUseTheSamePackageMetadata() { @@ -289,6 +332,166 @@ public void ClaudePluginContainsTheRepositoryLicense() ); } + [Theory] + [MemberData(nameof(SharedSectionNames))] + public void SkillsSharingASectionKeepItIdentical(string sectionName) + { + var skillNames = SharedSections.Single(shared => shared.Section == sectionName).SkillNames; + var sections = skillNames + .Select( + skillName => ( + SkillName: skillName, + Body: ReadSectionBody( + Path.Combine(RepositoryRoot, "skills", skillName, "SKILL.md"), + sectionName + ) + ) + ) + .ToArray(); + var reference = sections[0]; + + foreach (var section in sections[1..]) + { + Assert.True( + string.Equals(reference.Body, section.Body, StringComparison.Ordinal), + $"\"{sectionName}\" differs between {reference.SkillName} and {section.SkillName}. " + + "The section is duplicated on purpose because skills are standalone; " + + "apply the change to every skill that shares it." + ); + } + } + + [Fact] + public void GuidedLearningSkillsShareAllUndeclaredSections() + { + var skills = GuidedLearningSkillNames + .Select( + skillName => ( + SkillName: skillName, + Path: Path.Combine(RepositoryRoot, "skills", skillName, "SKILL.md") + ) + ) + .Select( + skill => ( + skill.SkillName, + skill.Path, + Headings: ReadSectionHeadings(skill.Path) + .Where(heading => !GuidedLearningSectionsThatDiffer.Contains(heading)) + .ToArray() + ) + ) + .ToArray(); + var reference = skills[0]; + + foreach (var skill in skills[1..]) + { + var unmatched = reference + .Headings + .Except(skill.Headings) + .Concat(skill.Headings.Except(reference.Headings)) + .ToArray(); + Assert.True( + reference.Headings.SequenceEqual(skill.Headings), + $"{reference.SkillName} and {skill.SkillName} have different sections " + + (unmatched.Length > 0 ? + $"(unmatched: {string.Join(", ", unmatched.Select(heading => $"\"{heading}\""))}). " : + "(same sections in a different order). ") + + "Guided Learning skills share every section by default; add the section to the other " + + $"skill or declare it in {nameof(GuidedLearningSectionsThatDiffer)}." + ); + + foreach (var heading in reference.Headings) + { + Assert.True( + string.Equals( + ReadSectionBody(reference.Path, heading), + ReadSectionBody(skill.Path, heading), + StringComparison.Ordinal + ), + $"\"{heading}\" differs between {reference.SkillName} and {skill.SkillName}. " + + "Apply the change to both skills, or declare the section in " + + $"{nameof(GuidedLearningSectionsThatDiffer)} if it should differ." + ); + } + } + } + + [Fact] + public void GuidedLearningSectionsThatDifferExist() + { + var headings = GuidedLearningSkillNames + .SelectMany( + skillName => ReadSectionHeadings( + Path.Combine(RepositoryRoot, "skills", skillName, "SKILL.md") + ) + ) + .ToHashSet(StringComparer.Ordinal); + + foreach (var heading in GuidedLearningSectionsThatDiffer) + { + Assert.True( + headings.Contains(heading), + $"\"{heading}\" is declared in {nameof(GuidedLearningSectionsThatDiffer)} " + + "but no Guided Learning skill has this section anymore." + ); + } + } + + [Theory] + [MemberData(nameof(SharedSnippetLeadIns))] + public void SkillsSharingASnippetKeepItIdentical(string leadIn) + { + var skillNames = SharedSnippets.Single(shared => shared.LeadIn == leadIn).SkillNames; + var snippets = skillNames + .Select( + skillName => ( + SkillName: skillName, + Lines: ReadSnippet( + Path.Combine(RepositoryRoot, "skills", skillName, "SKILL.md"), + leadIn + ) + ) + ) + .ToArray(); + var reference = snippets[0]; + + foreach (var snippet in snippets[1..]) + { + Assert.True( + reference.Lines.SequenceEqual(snippet.Lines, StringComparer.Ordinal), + $"The list after \"{leadIn}\" differs between {reference.SkillName} and " + + $"{snippet.SkillName}. The snippet is duplicated on purpose because skills are " + + "standalone; apply the change to every skill that shares it." + ); + } + } + + [Fact] + public void GuidedLearningSkillsShipTheSameProfileTemplate() + { + var templates = GuidedLearningSkillNames + .Select( + skillName => ( + SkillName: skillName, + Content: File.ReadAllText( + Path.Combine(RepositoryRoot, "skills", skillName, "assets", "profile.md") + ) + ) + ) + .ToArray(); + var reference = templates[0]; + + foreach (var template in templates[1..]) + { + Assert.True( + string.Equals(reference.Content, template.Content, StringComparison.Ordinal), + $"assets/profile.md differs between {reference.SkillName} and {template.SkillName}. " + + "The template is duplicated on purpose because skills are standalone; " + + "apply the change to every skill that ships it." + ); + } + } + private static string FindRepositoryRoot() { for ( @@ -397,6 +600,73 @@ private static string ParseBody(string content) return string.Join('\n', lines[(end + 1)..]); } + private static string ReadSectionBody(string path, string heading) + { + var lines = File.ReadAllText(path).Replace("\r\n", "\n", StringComparison.Ordinal).Split('\n'); + var start = Array.FindIndex(lines, line => IsSectionHeading(line, heading)); + Assert.True(start >= 0, $"\"{heading}\" is missing from {path}."); + + var end = Array.FindIndex( + lines, + start + 1, + line => line.StartsWith("## ", StringComparison.Ordinal) + ); + var body = end < 0 ? lines[(start + 1)..] : lines[(start + 1)..end]; + return string.Join('\n', body).Trim(); + } + + private static bool IsSectionHeading(string line, string heading) + { + return string.Equals(ReadHeadingText(line), heading, StringComparison.Ordinal); + } + + private static string[] ReadSectionHeadings(string path) + { + return File + .ReadAllText(path) + .Replace("\r\n", "\n", StringComparison.Ordinal) + .Split('\n') + .Select(ReadHeadingText) + .OfType() + .ToArray(); + } + + private static string? ReadHeadingText(string line) + { + if (!line.StartsWith("## ", StringComparison.Ordinal)) + { + return null; + } + + // Section numbers differ between skills, so match on the heading text alone. + var text = line[3..].Trim(); + var separator = text.IndexOf(". ", StringComparison.Ordinal); + if (separator > 0 && text[..separator].All(char.IsDigit)) + { + text = text[(separator + 2)..]; + } + + return text; + } + + private static string[] ReadSnippet(string path, string leadIn) + { + var lines = File.ReadAllText(path).Replace("\r\n", "\n", StringComparison.Ordinal).Split('\n'); + var matches = lines + .Select((line, index) => (Line: line, Index: index)) + .Where(candidate => candidate.Line.TrimEnd().EndsWith(leadIn, StringComparison.Ordinal)) + .ToArray(); + Assert.True(matches.Length == 1, $"{path} must contain \"{leadIn}\" exactly once."); + + var snippet = lines + .Skip(matches[0].Index + 1) + .SkipWhile(string.IsNullOrWhiteSpace) + .TakeWhile(line => line.StartsWith("- ", StringComparison.Ordinal)) + .ToArray(); + Assert.True(snippet.Length > 0, $"No list follows \"{leadIn}\" in {path}."); + return snippet; + } + private static SortedDictionary EnumerateResourceFiles( string root, bool excludeAgents diff --git a/tools/GuidedCoding.ClaudeGenerator/claude-skills.json b/tools/GuidedCoding.ClaudeGenerator/claude-skills.json index 6f4712d..2b846eb 100644 --- a/tools/GuidedCoding.ClaudeGenerator/claude-skills.json +++ b/tools/GuidedCoding.ClaudeGenerator/claude-skills.json @@ -4,6 +4,18 @@ "name": "freeze-plan", "disableModelInvocation": true }, + "guided-coding-implement": { + "name": "implement", + "disableModelInvocation": true + }, + "guided-coding-implement-coach-me": { + "name": "implement-coach-me", + "disableModelInvocation": true + }, + "guided-coding-implement-show-me": { + "name": "implement-show-me", + "disableModelInvocation": true + }, "guided-coding-review-plan": { "name": "review-plan", "disableModelInvocation": true