Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .agents/agents/fetch-extension-docs.md
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.
66 changes: 66 additions & 0 deletions .agents/agents/fetch-template-context.md
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.
1 change: 1 addition & 0 deletions .agents/skills/add-extension/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.tmp
210 changes: 210 additions & 0 deletions .agents/skills/add-extension/SKILL.md
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
- 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
```
Loading