-
-
Notifications
You must be signed in to change notification settings - Fork 98
Add agent skill for creating web framework integration packages #672
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
2chanhaeng
wants to merge
18
commits into
fedify-dev:main
Choose a base branch
from
2chanhaeng:main
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
18 commits
Select commit
Hold shift + click to select a range
a6f0fa2
Fix dev server process tree not terminated in test:init
2chanhaeng e9aeaeb
Define create-integration-package skill
2chanhaeng 8409641
Add lint and format checks step to create-integration-package skill
2chanhaeng 8522507
Remove unnecessary .gitignore guidance from create-integration-packag…
2chanhaeng 69b5bc3
Update SKILL.md to include testing prerequisites and linting instruct…
2chanhaeng afd1130
Update SKILL.md to publish package on both JSR and NPM
2chanhaeng a4eb9ae
Minor fix `/create-integration-package` skill
2chanhaeng 2c8b1f3
Fix delete method in PostStore to correctly filter timeline by URL href
2chanhaeng 8ee4524
Fix frameworkDescription property name from 'name' to 'label'
2chanhaeng ade4237
Fix module/exports fields in package template
2chanhaeng 8a7c9bf
Remove NestJS peer from package template
2chanhaeng 5eba128
Add imports section to deno.jsonc template
2chanhaeng 5ba404a
Fix hardcoded @fedify/nuxt in README template
2chanhaeng 1a2b70b
Fix optional param with default value syntax
2chanhaeng e3c3aa1
Update testing instructions in SKILL.md to include unit tests and usa…
2chanhaeng 35cad81
Fix typos in create-integration-package skill templates
2chanhaeng ee40cb9
Fix textarea resize to vertical per design spec
2chanhaeng 45ae21f
Lint
2chanhaeng 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,331 @@ | ||
| --- | ||
| name: create-integration-package | ||
| description: >- | ||
| This skill is utilized when creating a web framework integration package. | ||
| After examining the given framework, a feasibility assessment is conducted | ||
| regarding the creation of an integration package. | ||
| If implementation is feasible, the package is generated; | ||
| if it is not possible, the rationale is provided to the user. | ||
| argument-hint: "Provide the name of the web framework you want to integrate with." | ||
| --- | ||
|
|
||
| <!-- deno-fmt-ignore-file --> | ||
|
|
||
| Adding an integration package to a web framework | ||
| ================================================ | ||
|
|
||
| Follow these steps in order to implement the integration package. | ||
|
|
||
| 1. Research the web framework | ||
| 2. Implement the package | ||
| 3. Add to `@fedify/init` | ||
| 4. Test with `mise test:init` | ||
| 5. Add an example | ||
| 6. Lint, format, and final checks | ||
|
|
||
|
|
||
| Research the web framework | ||
| -------------------------- | ||
|
|
||
| Research the web framework for which the integration package will be | ||
| implemented. Fedify operates as middleware via | ||
| [`Federation.fetch`](../../../packages/fedify/src/federation/federation.ts). | ||
| The critical question is whether the given framework can act as a server | ||
| framework and supports adding middleware. Search for and investigate | ||
| whether the relevant functionality is available. Assess feasibility based | ||
| on the research. If research indicates implementation is not possible, | ||
| explain the reasons in detail to the user and stop. If feasible, proceed | ||
| to create the package. Even during package creation, it may turn out to be | ||
| infeasible. In that case as well, explain the reasons in detail to the | ||
| user and stop. | ||
|
|
||
|
|
||
| Implement the package | ||
| --------------------- | ||
|
|
||
| **Prioritize usability above all else.** The most important goal is that | ||
| the package integrates smoothly with the framework so users do not | ||
| experience friction when connecting it. | ||
|
|
||
| Create the package directory inside the `packages/` directory. For example, if | ||
| the framework is named “framework”, create the directory `packages/framework/`. | ||
|
|
||
| Unless there are significant hurdles, please set up the package to publish | ||
| on both JSR and NPM. | ||
|
|
||
| Copy the template files from <package/\*> into the directory you created. Then, | ||
| implement the package according to the framework. Since the comments in the | ||
| template are instructions for the developer to follow, please remove them once | ||
| the implementation is complete. | ||
|
|
||
| Add additional definitions as appropriate based on context. Aside from the | ||
| main integration function and the `ContextDataFactory` type, keep module | ||
| exports to a minimum to avoid confusing users. | ||
|
|
||
| ### Request flow | ||
|
|
||
| When a request arrives, the integration middleware calls | ||
| `federation.fetch()`. If Fedify has a route for the path and the client's | ||
| `Accept` header includes an ActivityPub media type such as | ||
| `application/activity+json`, Fedify generates and returns the JSON-LD | ||
| response directly. Framework-side routing does not execute. | ||
|
|
||
| ### Request conversion | ||
|
|
||
| Some frameworks define and use their own `Request` type internally instead | ||
| of the Web API `Request`. If the target framework does so, write | ||
| conversion functions within the integration package to translate between | ||
| the Web API `Request` and the framework's native `Request`. | ||
|
|
||
| ### 406 not acceptable | ||
|
|
||
| The final failure 406 response uses this form: | ||
|
|
||
| ~~~~ typescript | ||
| new Response("Not acceptable", { | ||
| status: 406, | ||
| headers: { | ||
| "Content-Type": "text/plain", | ||
| Vary: "Accept", | ||
| }, | ||
| }); | ||
| ~~~~ | ||
|
|
||
| ### Function naming conventions | ||
|
|
||
| A consistent naming convention for the main function has not yet been | ||
| established, but there is an [open naming convention issue]. If the issue | ||
| has been resolved by the time this skill is executed, update this section. | ||
| As a temporary convention, respect conventions of the framework : name it | ||
| `fedifyMiddleware` if the official documentation calls it as middleware, or | ||
| `fedifyHandler` if it's called a handler. | ||
|
|
||
| [open naming convention issue]: https://github.com/fedify-dev/fedify/issues/657 | ||
|
|
||
| ### Non-source files | ||
|
|
||
| #### README.md | ||
|
|
||
| The package README.md must include the following: | ||
|
|
||
| - Package description | ||
| - Supported framework versions, if only specific versions are supported | ||
| - Installation instructions | ||
| - Usage instructions (with example code) | ||
|
|
||
| #### `deno.json` | ||
|
|
||
| A *deno.json* is required to publish to JSR. | ||
|
|
||
| #### `package.json` | ||
|
|
||
| A *package.json* is required to publish to npm. | ||
|
|
||
| #### `tsdown.config.ts` | ||
|
|
||
| A *tsdown.config.ts* is required for the build in Node.js and Bun | ||
| environments. | ||
|
|
||
| ### Other updates | ||
|
|
||
| Refer to the “Adding a new package” section in *CONTRIBUTING.md* and | ||
| perform the required updates. Record the package addition in *CHANGES.md*. | ||
|
|
||
| ### Tests | ||
|
|
||
| You can test the integration using `mise test:init`, which will be explained | ||
| later, but write unit tests as well if possible. Import the `test` function | ||
| from `@fedify/fixture` to write runtime-agnostic tests that work across | ||
| Deno, Node.js, and Bun. Name test files with the `*.test.ts` convention | ||
| (e.g., `src/mod.test.ts`). | ||
|
|
||
| > **Warning**: `@fedify/fixture` is a **private** workspace package and | ||
| > must never be imported from published (non-test) source files. Only | ||
| > import it in `*.test.ts` files. | ||
|
|
||
| ### Implementation checklist | ||
|
|
||
| 1. Create the *packages/framework/* directory | ||
| 2. Write *src/mod.ts*: | ||
| - Export the main integration middleware/handler function | ||
| - Implement `federation.fetch()` invocation with | ||
| `onNotFound`/`onNotAcceptable` | ||
| - Export the `ContextDataFactory` type | ||
| - Write conversion functions if the framework does not natively support | ||
| Web API `Request`/`Response` | ||
| 3. Write *README.md* | ||
| 4. Write *deno.json* (if publishing to JSR is intended) | ||
| 5. Write *package.json* (if publishing to npm is intended) | ||
| 6. Write *tsdown.config.ts* (if Node.js and Bun are supported) | ||
| 7. Write tests if possible | ||
| 8. Perform remaining updates per the “Adding a new package” section in | ||
| *CONTRIBUTING.md* | ||
| 9. Record changes in *CHANGES.md* | ||
|
|
||
|
|
||
| Add to `@fedify/init` | ||
| --------------------- | ||
|
|
||
| Add the new package to the `@fedify/init` package so users can select the | ||
| new framework via the `fedify init` command. Follow these steps. | ||
|
|
||
| Steps may require code modifications not explicitly listed. For example, | ||
| if the new package needs specific configuration, utility functions in | ||
| `packages/init/src/webframeworks/utils.ts` may need updating. Make | ||
| modifications consistent with the existing code style and context. | ||
|
|
||
| ### Write the `WebFrameworkDescription` object | ||
|
|
||
| Create a `packages/init/src/webframeworks/framework.ts` file and write the | ||
| `WebFrameworkDescription` object, referring to <init/framework.ts>. Check | ||
| the specifications in the comments in `packages/init/src/types.ts` for | ||
| details. | ||
|
|
||
| ### Add to the `WEB_FRAMEWORK` array | ||
|
|
||
| Add the new framework name to the end of the `WEB_FRAMEWORK` array in | ||
| `packages/init/src/const.ts`. | ||
|
|
||
| ~~~~ typescript | ||
| export const WEB_FRAMEWORK = [ | ||
| // ... other frameworks | ||
| "framework", // Fill with the framework name | ||
| ]; | ||
| ~~~~ | ||
|
|
||
| ### Add to the `webFrameworks` object | ||
|
|
||
| Add the new `WebFrameworkDescription` object in alphabetical order to the | ||
| `webFrameworks` object in `packages/init/src/webframeworks/mod.ts`. | ||
|
|
||
| ~~~~ typescript | ||
| // packages/init/src/webframeworks/mod.ts | ||
|
|
||
| // ... other imports | ||
| import framework from "./framework.ts"; // Fill with the framework name | ||
|
|
||
| const webFrameworks: Record<string, WebFrameworkDescription> = { | ||
| // ... other frameworks | ||
| framework, // Fill with the framework name | ||
| }; | ||
| ~~~~ | ||
|
|
||
| ### Add templates in `packages/init/src/templates/framework/` | ||
|
|
||
| If additional files need to be generated, add template files under the | ||
| `packages/init/src/templates/framework/` directory. Template files must | ||
| end with the `.tpl` extension appended to their base name. Then, in | ||
| `packages/init/src/webframeworks/framework.ts`, load the templates using | ||
| the `readTemplate` function defined in `packages/init/src/lib.ts` and add | ||
| them to the `WebFrameworkDescription.init().files` object. | ||
|
|
||
|
|
||
| Test with `mise test:init` | ||
| -------------------------- | ||
|
|
||
| Run `mise test:init` to verify that the new package is generated and runs | ||
| correctly. If a test fails, the output and error file paths are printed; | ||
| read them to diagnose the issue. | ||
|
|
||
| Running `mise test:init` without arguments tests all option combinations | ||
| and can take a very long time. Use appropriate options to narrow the test | ||
| scope. | ||
|
|
||
| Immediately remove test paths after completing the tests and analyzing any | ||
| resulting errors. | ||
|
|
||
| At a minimum, test the following three combinations. | ||
|
|
||
| - `mise test:init -w framework -m in-process -k in-memory --no-dry-run`: | ||
| Tests the new framework with the in-memory KV store and in-process message | ||
| queue, which are the most basic options. This combination verify that the | ||
| newly created package can be used without issues by minimizing dependencies | ||
| on other environments. | ||
| - `mise test:init -w framework`: Tests all package manager, KV store, | ||
| and message queue combinations with the framework selected. If a | ||
| required database is not installed or running, this combinations are | ||
| useless. Therefore, if the test output indicates that the databases are | ||
| not running, don't use this combination ever again for the session. | ||
| Instead, use the previous one or the next one. | ||
2chanhaeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - `mise test:init -m in-process -k in-memory --no-dry-run`: Fixes the | ||
| KV store and message queue and tests all web framework and package | ||
| manager combinations. This test is mandatory if you modified logic | ||
| beyond just writing the `WebFrameworkDescription` object. | ||
|
|
||
| For details on options, run `mise test:init --help`. | ||
|
|
||
| Some frameworks or combinations may be untestable. Analyze the test | ||
| results; if there are impossible combinations, identify the reason and add | ||
| the combination and reason as a key-value pair to the | ||
| `BANNED_LOOKUP_REASONS` object in | ||
| `packages/init/src/test/lookup.ts`. | ||
|
|
||
|
|
||
| Add an example | ||
| -------------- | ||
|
|
||
| Create an `examples/framework/` app and write an example for the new | ||
| package. If Deno is supported, add a *deno.json* based on <example/deno.json>; | ||
| if Node.js is supported, add *package.json* based on <example/package.jsonc> | ||
| and *tsdown.config.ts*. Depending on the supported environments, | ||
| add the example path to the `workspace` field in | ||
| the root *deno.json* and to the `packages` field in | ||
| *pnpm-workspace.yaml*. | ||
|
|
||
| If the framework is backend-only and needs a frontend framework, and there | ||
| is no natural pairing like solidstart-solid, use Hono. | ||
|
|
||
| Base the example on the files under the <example/\*> path. | ||
| <example/ARCHITECTURE.md> describes the example's architecture. | ||
| <example/DESIGN.md> describes the example's design. Both documents are | ||
| references for writing the example and are not needed in the actual | ||
| generated example app — do not create these two files. Copy the remaining | ||
| files as-is and modify as needed. | ||
|
|
||
| If the framework does not have a prescribed entry point, use `src/main.ts` | ||
| as the application entry point. Define and export the framework app in | ||
| `src/app.ts`, then import and run it from the entry file. Register the | ||
| Fedify middleware in `src/app.ts`. Import `src/logging.ts` in the entry | ||
| file to initialize `@logtape/logtape`. When logging is needed, use the | ||
| `getLogger` function from `@logtape/logtape` to create a logger. | ||
|
|
||
| ### Test the example with `mise test:examples` | ||
|
|
||
| Register the new example in `examples/test-examples/mod.ts`. Read the | ||
| comments above the example registry arrays in that file to determine | ||
| which array is appropriate and what fields are required. Follow the | ||
| patterns of existing entries. | ||
|
|
||
| Before running the tests, ensure that the tunneling service is usable. | ||
| The tests use the tunneling service `pinggy.io` to make the example app | ||
| accessible to the test suite. If the tunneling service is not usable, | ||
| the tests may never finish or may fail due to a connection error. | ||
|
|
||
| While developing the example, run only the new example to iterate | ||
| quickly: | ||
|
|
||
| ~~~~ bash | ||
| mise test:examples framework | ||
| ~~~~ | ||
|
|
||
| where `framework` is the `name` field of the registered entry. Pass | ||
| `--debug` for verbose output if the test fails. | ||
|
|
||
| After the example is complete, run the full suite once to confirm nothing | ||
| is broken: | ||
|
|
||
| ~~~~ bash | ||
| mise test:examples | ||
| ~~~~ | ||
|
|
||
|
|
||
| Lint, format, and final checks | ||
| ------------------------------ | ||
|
|
||
| Add keywords related to the framework in `.hongdown.toml` and `cspell.json` in | ||
| root path. Especially, the package name `@fedify/framework` should be added to | ||
| the `.hongdown.toml`. | ||
|
|
||
| After implementation, run `mise run fmt && mise check`. | ||
| If there are lint or format errors, fix them and run the command again until | ||
| there are no errors. | ||
2chanhaeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Oops, something went wrong.
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.