-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat: add hybrid /add-extension skill for merging SE-2 extensions #1232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
technophile-04
wants to merge
6
commits into
main
Choose a base branch
from
add-extension-hybrid
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
f9c5975
feat: add hybrid /add-extension skill for merging SE-2 extensions
technophile-04 66a5ad4
remove the hardcoded example and allow agents to fetch all the files
technophile-04 f8ad75d
remove the duplicated section from skill.md
technophile-04 c1e8979
hoist skill to .agents dir
technophile-04 99d8259
remove the word hybrid from skills
technophile-04 ef7d876
clean up unecessary code
technophile-04 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| You fetch and distill the create-eth contributor documentation about the extension/templating system. | ||
|
|
||
| ## What you do | ||
|
|
||
| Fetch the following docs from the create-eth repository and return the key information that an AI agent needs to correctly process `.args.mjs` files during extension merging. | ||
|
|
||
| ## Docs to fetch | ||
|
|
||
| ### 1. TEMPLATING.md | ||
|
|
||
| ```bash | ||
| curl -sL https://raw.githubusercontent.com/scaffold-eth/create-eth/main/contributors/TEMPLATING.md | ||
| ``` | ||
|
|
||
| ### 2. THIRD-PARTY-EXTENSION.md | ||
|
|
||
| ```bash | ||
| curl -sL https://raw.githubusercontent.com/scaffold-eth/create-eth/main/contributors/THIRD-PARTY-EXTENSION.md | ||
| ``` | ||
|
|
||
| ### 3. (Optional) TEMPLATE-FILES.md | ||
|
|
||
| Only fetch this if the caller specifically asks for the template registry: | ||
|
|
||
| ```bash | ||
| curl -sL https://raw.githubusercontent.com/scaffold-eth/create-eth/main/contributors/TEMPLATE-FILES.md | ||
| ``` | ||
|
|
||
| ## What to extract and return | ||
|
|
||
| After fetching, return the information organized by these topics: | ||
|
|
||
| ### Template files | ||
| - Naming convention: `{original-name}.template.mjs` | ||
| - They `export default` a function receiving named args and returning a string | ||
| - All args are arrays of strings (may be empty) | ||
| - Function signature: `(Record<string, string[]>) => string` | ||
|
|
||
| ### Args files | ||
| - Naming convention: `{original-name}.args.mjs` | ||
| - Must be at the same relative path as the template | ||
| - Use named exports (e.g., `export const preContent = "..."`) | ||
| - Can use global variables by exporting functions instead of values | ||
|
|
||
| ### The `withDefaults` utility | ||
| - Wraps the template function with default values for all args | ||
| - Throws if an args file sends an unexpected argument name | ||
| - All args wrapped in arrays internally | ||
|
|
||
| ### The `$$expr$$` convention | ||
| - `$$variableName$$` = raw expression, no quotes (e.g., `$$deployerPrivateKey$$` becomes `deployerPrivateKey`) | ||
| - `${variableName}` = string interpolation within template literals | ||
| - Used in objects like `configOverrides` to reference variables | ||
|
|
||
| ### Rules for template args | ||
| - `preContent` (string) for imports and variable declarations | ||
| - `<name>Overrides` (object) to extend existing variables/objects | ||
| - Descriptive string args for new code/logic | ||
| - `stringify` utility for serializing objects/arrays/bigints | ||
| - `deepMerge` utility for combining objects (arrays are replaced, not concatenated) | ||
|
|
||
| ### Complex argument patterns | ||
| - Replace object: `${stringify(replacedObj[0])}` | ||
| - Deep merge object: `${stringify(deepMerge(defaultObj, objToMerge[0]))}` | ||
| - Array spread: `${stringify(['a', 'b', ...arrayToSpread[0]])}` | ||
| - BigInt: `${stringify(someBigInt[0])}` | ||
|
|
||
| ### Extension folder anatomy | ||
| - Normal files: copied directly to output | ||
| - Templated files: `.template.mjs` and `.args.mjs` files | ||
| - `package.json`: merged using `merge-packages` (last version wins on conflict) | ||
| - `solidity-frameworks/` folder for framework-specific files | ||
|
|
||
| ### Available global variables in args | ||
| - `solidityFramework` - the selected Solidity framework ("hardhat" or "foundry") | ||
|
|
||
| Keep the response focused and concise. The main agent needs actionable information, not the full raw docs. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| You fetch template sources from the create-eth repository to help the main agent understand how .args.mjs exports are consumed by their corresponding templates. | ||
|
|
||
| ## What you do | ||
|
|
||
| When invoked, you receive one or more **target file paths** (e.g., `packages/nextjs/components/Header.tsx`). For each target file, you find and fetch the corresponding `.template.mjs` source from create-eth, then return the template source and an explanation of how each arg is used. | ||
|
|
||
| ## Step-by-step process | ||
|
|
||
| ### 1. Fetch the template registry | ||
|
|
||
| Fetch `https://raw.githubusercontent.com/scaffold-eth/create-eth/main/contributors/TEMPLATE-FILES.md` using Bash: | ||
|
|
||
| ```bash | ||
| curl -sL https://raw.githubusercontent.com/scaffold-eth/create-eth/main/contributors/TEMPLATE-FILES.md | ||
| ``` | ||
|
|
||
| ### 2. Find matching templates | ||
|
|
||
| The registry contains markdown tables like: | ||
|
|
||
| ``` | ||
| | [(type) `Header.tsx.template.mjs`](https://github.com/.../Header.tsx.template.mjs) | [`Header.tsx.args.mjs`](https://github.com/.../Header.tsx.args.mjs) | | ||
| ``` | ||
|
|
||
| For each requested target file (e.g., `packages/nextjs/components/Header.tsx`): | ||
| - Look for a row where the template filename matches `Header.tsx.template.mjs` | ||
| - Verify the URL path contains the right directory structure (e.g., `packages/nextjs/components/`) | ||
| - Extract both the template URL and the example args URL | ||
|
|
||
| Templates live under these base paths in the URLs: | ||
| - `templates/base/` - shared files (nextjs, root) | ||
| - `templates/solidity-frameworks/hardhat/` - hardhat-specific | ||
| - `templates/solidity-frameworks/foundry/` - foundry-specific | ||
|
|
||
| ### 3. Fetch the template source | ||
|
|
||
| Convert the GitHub URL to a raw URL: | ||
| - Replace `github.com` with `raw.githubusercontent.com` | ||
| - Remove `/blob/` from the path | ||
|
|
||
| Then fetch it: | ||
| ```bash | ||
| curl -sL https://raw.githubusercontent.com/scaffold-eth/create-eth/main/templates/base/packages/nextjs/components/Header.tsx.template.mjs | ||
| ``` | ||
|
|
||
| Also fetch the example args file (these are in the `create-eth-extensions` repo, branch `example`). | ||
|
|
||
| ### 4. Return results | ||
|
|
||
| For each target file, return: | ||
|
|
||
| 1. **The template source code** - the complete raw source | ||
| 2. **The function signature** - list the parameters of the `contents` function (these are the args the template accepts) | ||
| 3. **How each arg is used** - for each parameter, explain: | ||
| - Where it's interpolated in the template output | ||
| - What type it expects (string, array, object) | ||
| - What it does (e.g., "inserted after imports", "spread into menuLinks array", "deep-merged into config") | ||
|
|
||
| ## Important notes for the main agent | ||
|
|
||
| Include these notes in your response: | ||
|
|
||
| - **`[0]` indexing**: In template sources, args are accessed as `argName[0]`. This is because `withDefaults` wraps values in arrays (to support multiple extensions). When applying args to project files, use the value directly -- do NOT wrap in arrays. | ||
| - **`$$expr$$` convention**: Values wrapped in `$$...$$` are raw JavaScript expressions. Strip the `$$` delimiters and use as bare code (unquoted). Add corresponding imports if needed. | ||
| - **`stringify` and `deepMerge`**: Templates use these utilities from `utils.js` for serializing objects and deep-merging configs. The main agent should replicate these operations when modifying project files. | ||
| - If no matching template is found for a target file, say so clearly. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| .tmp |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,210 @@ | ||
| --- | ||
| name: add-extension | ||
| description: Merge a scaffold-eth extension into the current project | ||
| disable-model-invocation: false | ||
| allowed-tools: Bash, Read, Write, Edit, Glob, Grep | ||
| user-invocable: true | ||
| --- | ||
|
|
||
| Merge the `$0` extension into this Scaffold-ETH-2 project. | ||
| A Node.js script handles deterministic operations, then you handle intelligent file merging. | ||
|
|
||
| ## Step 1: Run the Script | ||
|
|
||
| ```bash | ||
| node .agents/skills/add-extension/skill.mjs $0 $@ | ||
| ``` | ||
|
|
||
| The script will: | ||
| 1. Validate the extension exists in the registry | ||
| 2. Detect the SE-2 project and solidity framework | ||
| 3. Fetch extension files from GitHub | ||
| 4. Copy new files directly into the project | ||
| 5. Merge package.json dependencies and scripts | ||
| 6. Register new workspaces | ||
| 7. Output **AI merge tasks** as JSON between markers | ||
|
|
||
| ## Step 2: Process AI Merge Tasks | ||
|
|
||
| After the script runs, look for output between these markers: | ||
|
|
||
| ``` | ||
| --- AI_MERGE_TASKS_BEGIN --- | ||
| [JSON array of tasks] | ||
| --- AI_MERGE_TASKS_END --- | ||
| ``` | ||
|
|
||
| And optionally: | ||
|
|
||
| ``` | ||
| --- TEMPLATE_TASKS_BEGIN --- | ||
| [JSON array of template tasks] | ||
| --- TEMPLATE_TASKS_END --- | ||
| ``` | ||
|
|
||
| Each task has a `type` field. Process them in order: | ||
|
|
||
| ### Task type: `args_merge` | ||
|
|
||
| Fields: | ||
| - `targetFile`: relative path to the file to modify (e.g. `packages/nextjs/components/Header.tsx`) | ||
| - `exports`: array of `{ name, type }` describing each exported constant | ||
| - `argsContent`: the raw source code of the `.args.mjs` file | ||
|
|
||
| **Read the target file**, then apply each export according to the rules in "How .args.mjs Exports Work" below. | ||
|
|
||
| ### Task type: `file_conflict` | ||
|
|
||
| Fields: | ||
| - `targetFile`: relative path to the conflicting file in the project | ||
| - `extensionContent`: the full text content of the extension's version of the file (inlined in JSON) | ||
|
|
||
| The extension has a file that already exists in the project. Read the existing project file at `targetFile`, then compare it with `extensionContent` and merge them: | ||
| - **Imports**: Union both sets of imports (no duplicates) | ||
| - **New functions/components**: Add any new functions from the extension | ||
| - **Modified functions**: If both versions modify the same function, prefer the extension's changes but preserve any project-specific customizations | ||
| - **Configuration objects**: Deep merge, with extension values taking precedence for new keys | ||
| - **When in doubt**: Keep both versions and add a comment noting the merge | ||
|
|
||
| ### Task type: `standalone_template` | ||
|
|
||
| Fields: | ||
| - `targetFile`: relative path for the output file | ||
| - `templateContent`: raw `.template.mjs` source code | ||
|
|
||
| Extract the template literal content from the `contents` function. The template typically looks like: | ||
|
|
||
| ```javascript | ||
| const contents = () => ` | ||
| ...file content here... | ||
| `; | ||
| export default contents; | ||
| ``` | ||
|
|
||
| Extract the string inside the template literal, replace any `${...}` template expressions with reasonable defaults, and write the result to `targetFile`. If the target file exists, merge intelligently. Templates may use `withDefaults` -- ignore that wrapper and focus on the content inside the backticks. | ||
|
|
||
| ## How .args.mjs Exports Work | ||
|
|
||
| Extensions use `.args.mjs` files to modify existing project files. Each file exports named constants that correspond to specific insertion points in the target file's `.template.mjs` in the create-eth repo. | ||
|
|
||
| **Instead of relying on hardcoded pattern docs, use sub-agents to dynamically fetch template sources and docs from create-eth. This keeps the main context clean and always uses up-to-date information.** | ||
|
|
||
| ### Step 2a: Fetch Template Context (use `fetch-template-context` sub-agent) | ||
|
|
||
| **Collect all `targetFile` values from `args_merge` tasks** and delegate to the `fetch-template-context` sub-agent. It fetches the create-eth template registry, finds each matching `.template.mjs`, and returns the template source with an explanation of how each arg is consumed. | ||
|
|
||
| Invoke it via the Task tool: | ||
| ``` | ||
| subagent_type: "fetch-template-context" | ||
| prompt: "Fetch template context for these target files: packages/nextjs/components/Header.tsx, packages/nextjs/app/page.tsx, packages/nextjs/scaffold.config.ts" | ||
| ``` | ||
|
|
||
| The sub-agent returns for each file: | ||
| 1. **The `.template.mjs` source** -- shows the template function and how each arg is interpolated | ||
| 2. **An example `.args.mjs`** -- a real-world example of how extensions provide values | ||
| 3. **How each arg is used** -- where it's inserted and what type it expects | ||
|
|
||
| Use this information to apply the args from the extension's `.args.mjs` to the existing project file. | ||
|
|
||
| ### Step 2b: Fetch General Docs (use `fetch-extension-docs` sub-agent, run once if needed) | ||
|
|
||
| If the template source is unclear, or if you encounter `withDefaults`, `$$` expressions, or unfamiliar patterns, delegate to the `fetch-extension-docs` sub-agent for deeper context: | ||
|
|
||
| ``` | ||
| subagent_type: "fetch-extension-docs" | ||
| prompt: "Fetch the create-eth templating and extension docs" | ||
| ``` | ||
|
|
||
| It fetches and distills `TEMPLATING.md` and `THIRD-PARTY-EXTENSION.md` from create-eth, returning key information about: | ||
| - How template files and args files relate to each other | ||
| - The `withDefaults` utility and argument passing | ||
| - Rules for template args (string args, object overrides, `$$` convention) | ||
| - How `package.json` merging works | ||
| - Extension folder anatomy | ||
|
|
||
| ### When No Template Is Found | ||
|
|
||
| If the sub-agent reports no template for a target file, use your best judgment: | ||
| - If the export name suggests it's a string to prepend/append, do that | ||
| - If the name suggests it's a configuration value, merge it into the appropriate config object | ||
| - `fullContentOverride` always means: replace the entire file content | ||
| - If unsure, show the user the export and ask how to apply it | ||
|
|
||
| ## The `$$` Convention | ||
|
|
||
| In `.args.mjs` files, `$$expr$$` marks a raw JavaScript expression that should NOT be quoted in the output. | ||
|
|
||
| Example in `.args.mjs`: | ||
| ```javascript | ||
| export const configOverrides = { | ||
| targetNetworks: ["$$chains.baseSepolia$$"] | ||
| }; | ||
| ``` | ||
|
|
||
| When you apply this to `scaffold.config.ts`, write: | ||
| ```typescript | ||
| targetNetworks: [chains.baseSepolia], | ||
| ``` | ||
|
|
||
| **NOT**: | ||
| ```typescript | ||
| targetNetworks: ["chains.baseSepolia"], // WRONG - don't quote it | ||
| ``` | ||
|
|
||
| The `$$` markers are delimiters only — strip them and use the expression as bare code. If the target file needs the corresponding import (e.g., `import { chains } from ...`), check if it already exists and add it if missing. | ||
|
|
||
| ## Handling Missing Target Files | ||
|
|
||
| If an `args_merge` task targets a file that does not exist in the project: | ||
| - For `fullContentOverride`: create the file with the provided content | ||
| - For other exports: create the file with a reasonable default structure, then apply the exports | ||
technophile-04 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - If unsure, inform the user and ask how to proceed | ||
|
|
||
| ## Step 3: Post-Merge | ||
|
|
||
| After processing all AI merge tasks: | ||
|
|
||
| 1. Run `yarn install` if any dependencies were added | ||
| 2. Run `yarn next:build` to verify the build succeeds | ||
| 3. If the build fails, read the error and fix it | ||
| 4. Summarize what was added: | ||
| - New files copied | ||
| - Files modified via .args.mjs | ||
| - Dependencies added | ||
| - Any manual steps needed | ||
|
|
||
| ## Options | ||
|
|
||
| | Flag | Short | Description | | ||
| |------|-------|-------------| | ||
| | `--dry-run` | `-d` | Preview changes without applying | | ||
| | `--verbose` | `-v` | Show detailed error messages | | ||
| | `--local <path>` | `-l` | Use local extension path (for development) | | ||
| | `--solidity-framework <fw>` | `-s` | Choose hardhat/foundry | | ||
|
|
||
| ## Framework Selection | ||
|
|
||
| - Project has hardhat -> uses hardhat files (no prompt) | ||
| - Project has foundry -> uses foundry files (no prompt) | ||
| - Project has neither -> prompts or uses `-s` flag | ||
| - Flag mismatch with project -> errors | ||
|
|
||
| ## On Failure | ||
|
|
||
| - If extension not found: suggest `--list` to see available extensions | ||
| - If not SE-2 project: confirm user is in project root | ||
| - If build fails after merge: read errors, fix issues, try again | ||
|
|
||
| ## Notes | ||
|
|
||
| - Temp files are in `.agents/skills/add-extension/.tmp` | ||
| - The script cleans up temp files after outputting JSON (argsContent is inlined) | ||
| - Don't ask permission for `.tmp` folder operations | ||
|
|
||
| ## Examples | ||
|
|
||
| ```bash | ||
| /add-extension erc-20 | ||
| /add-extension subgraph --dry-run | ||
| /add-extension ponder -s hardhat | ||
| ``` | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.