From a047e48026599bcbda2c78894e11a32684288d2c Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 2 Mar 2026 22:32:24 -0800 Subject: [PATCH 1/5] First draft --- .../extension-to-functions-codebase/SKILL.md | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 skills/extension-to-functions-codebase/SKILL.md diff --git a/skills/extension-to-functions-codebase/SKILL.md b/skills/extension-to-functions-codebase/SKILL.md new file mode 100644 index 0000000..672f942 --- /dev/null +++ b/skills/extension-to-functions-codebase/SKILL.md @@ -0,0 +1,97 @@ +--- +name: extension-to-functions-codebase +description: Skill for converting an extensions repository to a functions codebase +--- + +# Extension to Functions Codebase + +## Overview + +A user likes a Firebase Extension but it doesn't do exactly what they wanted. They +want to convert the extension into a functions codebase that they can modify and +deploy as their own functions. The only problem is that the extensions namespace +doesn't express any of the IaC expected in extensions. This fixes taht. + +## Triggerrs +Activate this skill when a developer expresses that they which an extension was +a functions codebase instead of an extension. + +## Follow up +If there are any tests in the extensions codebase, be sure to run them after the migration. + +## Rules and Constraints + +# Verify you are ready for the task +If an extensions codebase has a feature that you do not know how to handle yet, such as +lifecycle hooks, panic and tell the user that you cannot handle this task yet. + +# API Enablement +For all API dependencies listed in `extension.yaml`, add a comment to index.js + +```typescript +// APIs to enable: +// - +``` + +# Parameterization +All config must be a parameter in the functions codebase. Read the list of all +parameters in the extension's `extension.yaml` file and create a parameter for each +one in the functions codebase. Be sure to keep all metadata such as label, description, type, and +valation rules and error messages. All process.env calls must be instead replaced with the +appropriate param.value() call. Be sure all process.env values referenced are defined as parameters; +be encouraged to use built-in parameters though. + +Custom events should be listed as a multiSelect parameter. For the label should be "Events to emit". +The description should be "Select the events that this function should emit from the following list:" +and then list events as options with `*[type]*: [description]\n`. The event type should be the value +in the multiSelect input list. + +params must not be called with .value() at global scope. If a global is being intialized with a +parameter, use the onInit function to initialize the global. For example: + +```typescript +const myFoo = new Foo(functions.config().foo); +``` + +should be turned into + +```typescript +const foo = defineString('FOO', { /* descriptions */ }); +let myFoo: Foo; +onInit(() => { + myFoo = new Foo(foo.value()); +}); +``` + +Never ever ever export an extension's value directly or even an accessor, even through a function. If the codebase used to use an export default, be sure to update the import to an `import * as config from` instead of `import config from` style. +Instead export the parameter as a named export from any library codebase. This allows you to use +the parameter without .value() as a functions configuration parameter in the later step. On the other hand, within a function, you may call .value() on the parameter if you need to actually use the value of the parameter. + +# Engine pinning +In `extensions.yaml` there will be a line that looks like this: + +```yaml +runtime: nodejs20 +``` + +This means that the extension is pinned to a specific runtime. If all functions +do not have the same runtime, panic and tell the user that mixed runtimes are +not yet supported. + +Learn the runtime and use that to update the customer's package.json to list the +node engine as the runtime version. + +# Switching SDK versions +For all exported functions, replace the import with "functions.extensions.foo" to just +"functions.foo". Use the other builder functions necessary to reach the same function +callback. Where those builder functions expect or allow a parameter, use the named functions +parameter for the configuration. + +# Wrapping up +If the destination directory looks like a firebase project (e.g. has a firebase.json) Offer +to the functions codebase to firebase.json for the user so that it will be included +in subsequent deploys. If the user agrees, add the functions codebase to firebase.json. + +# Testing +If there are any tests in the extensions codebase, be sure to run them after +the migration. This may require modifying the test as well to point to the functions codebase. \ No newline at end of file From 28cc3884950e1fb7db147cc62d486b072ce5652c Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Thu, 5 Mar 2026 15:22:07 -0800 Subject: [PATCH 2/5] PR feedback --- .../extension-to-functions-codebase/SKILL.md | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/skills/extension-to-functions-codebase/SKILL.md b/skills/extension-to-functions-codebase/SKILL.md index 672f942..e71d80a 100644 --- a/skills/extension-to-functions-codebase/SKILL.md +++ b/skills/extension-to-functions-codebase/SKILL.md @@ -1,6 +1,6 @@ --- name: extension-to-functions-codebase -description: Skill for converting an extensions repository to a functions codebase +description: Skill for converting a Firebase extension repository to a functions codebase --- # Extension to Functions Codebase @@ -10,7 +10,7 @@ description: Skill for converting an extensions repository to a functions codeba A user likes a Firebase Extension but it doesn't do exactly what they wanted. They want to convert the extension into a functions codebase that they can modify and deploy as their own functions. The only problem is that the extensions namespace -doesn't express any of the IaC expected in extensions. This fixes taht. +doesn't express any of the Infrastructure as Code expected in extensions. This fixes that. ## Triggerrs Activate this skill when a developer expresses that they which an extension was @@ -23,7 +23,7 @@ If there are any tests in the extensions codebase, be sure to run them after the # Verify you are ready for the task If an extensions codebase has a feature that you do not know how to handle yet, such as -lifecycle hooks, panic and tell the user that you cannot handle this task yet. +lifecycle hooks, stop and tell the user that you cannot handle this task yet. # API Enablement For all API dependencies listed in `extension.yaml`, add a comment to index.js @@ -37,17 +37,17 @@ For all API dependencies listed in `extension.yaml`, add a comment to index.js All config must be a parameter in the functions codebase. Read the list of all parameters in the extension's `extension.yaml` file and create a parameter for each one in the functions codebase. Be sure to keep all metadata such as label, description, type, and -valation rules and error messages. All process.env calls must be instead replaced with the -appropriate param.value() call. Be sure all process.env values referenced are defined as parameters; +valation rules and error messages. All `process.env` calls must be instead replaced with the +appropriate `param.value()` call. Be sure all `process.env` values referenced are defined as parameters; be encouraged to use built-in parameters though. -Custom events should be listed as a multiSelect parameter. For the label should be "Events to emit". +Custom events should be listed as a `multiSelect` parameter with the label "Events to emit". The description should be "Select the events that this function should emit from the following list:" and then list events as options with `*[type]*: [description]\n`. The event type should be the value -in the multiSelect input list. +in the `multiSelect` input list. -params must not be called with .value() at global scope. If a global is being intialized with a -parameter, use the onInit function to initialize the global. For example: +`params` must not be called with `.value()` at global scope. If a global is being intialized with a +parameter, use the `onInit` function to initialize the global. For example: ```typescript const myFoo = new Foo(functions.config().foo); @@ -63,9 +63,8 @@ onInit(() => { }); ``` -Never ever ever export an extension's value directly or even an accessor, even through a function. If the codebase used to use an export default, be sure to update the import to an `import * as config from` instead of `import config from` style. -Instead export the parameter as a named export from any library codebase. This allows you to use -the parameter without .value() as a functions configuration parameter in the later step. On the other hand, within a function, you may call .value() on the parameter if you need to actually use the value of the parameter. +Never ever ever export an extension parameter's value directly or even an accessor, even through a function. This allows you to use +the parameter without `.value()` as a functions configuration parameter in the later step. On the other hand, within a function, you may call `.value()` on the parameter if you need to actually use the value of the parameter. # Engine pinning In `extensions.yaml` there will be a line that looks like this: @@ -75,7 +74,7 @@ runtime: nodejs20 ``` This means that the extension is pinned to a specific runtime. If all functions -do not have the same runtime, panic and tell the user that mixed runtimes are +do not have the same runtime, stop and tell the user that mixed runtimes are not yet supported. Learn the runtime and use that to update the customer's package.json to list the @@ -88,9 +87,9 @@ callback. Where those builder functions expect or allow a parameter, use the nam parameter for the configuration. # Wrapping up -If the destination directory looks like a firebase project (e.g. has a firebase.json) Offer -to the functions codebase to firebase.json for the user so that it will be included -in subsequent deploys. If the user agrees, add the functions codebase to firebase.json. +If the destination directory looks like a firebase project (e.g. has a `firebase.json`) Offer +to add the functions codebase to `firebase.json` for the user so that it will be included +in subsequent deploys. If the user agrees, add the functions codebase to `firebase.json`. # Testing If there are any tests in the extensions codebase, be sure to run them after From b139d88f9f7927fae457884f76d81e112f2dfded Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Thu, 5 Mar 2026 18:31:23 -0800 Subject: [PATCH 3/5] A bit more refinement --- .../extension-to-functions-codebase/SKILL.md | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/skills/extension-to-functions-codebase/SKILL.md b/skills/extension-to-functions-codebase/SKILL.md index e71d80a..a359922 100644 --- a/skills/extension-to-functions-codebase/SKILL.md +++ b/skills/extension-to-functions-codebase/SKILL.md @@ -12,8 +12,8 @@ want to convert the extension into a functions codebase that they can modify and deploy as their own functions. The only problem is that the extensions namespace doesn't express any of the Infrastructure as Code expected in extensions. This fixes that. -## Triggerrs -Activate this skill when a developer expresses that they which an extension was +## Triggers +Activate this skill when a developer expresses that they wish an extension was a functions codebase instead of an extension. ## Follow up @@ -21,23 +21,24 @@ If there are any tests in the extensions codebase, be sure to run them after the ## Rules and Constraints -# Verify you are ready for the task +### Verify you are ready for the task If an extensions codebase has a feature that you do not know how to handle yet, such as -lifecycle hooks, stop and tell the user that you cannot handle this task yet. +lifecycle hooks or specific IAM roles via `iamRoles` in `extension.yaml`, stop and tell the user that you cannot handle this task yet. -# API Enablement -For all API dependencies listed in `extension.yaml`, add a comment to index.js +### API Enablement +For all API dependencies listed in `extension.yaml`, add a comment to index.js and inform the user in your final response to manually enable them. ```typescript // APIs to enable: // - ``` -# Parameterization +### Parameterization All config must be a parameter in the functions codebase. Read the list of all parameters in the extension's `extension.yaml` file and create a parameter for each one in the functions codebase. Be sure to keep all metadata such as label, description, type, and -valation rules and error messages. All `process.env` calls must be instead replaced with the +validation rules and error messages. If a parameter's type is `secret`, use `defineSecret('SECRET_NAME')` instead of `defineString`. +All `process.env` calls must be instead replaced with the appropriate `param.value()` call. Be sure all `process.env` values referenced are defined as parameters; be encouraged to use built-in parameters though. @@ -46,11 +47,11 @@ The description should be "Select the events that this function should emit from and then list events as options with `*[type]*: [description]\n`. The event type should be the value in the `multiSelect` input list. -`params` must not be called with `.value()` at global scope. If a global is being intialized with a +`params` must not be called with `.value()` at global scope. If a global is being initialized with a parameter, use the `onInit` function to initialize the global. For example: ```typescript -const myFoo = new Foo(functions.config().foo); +const myFoo = new Foo(process.env.FOO); ``` should be turned into @@ -66,8 +67,8 @@ onInit(() => { Never ever ever export an extension parameter's value directly or even an accessor, even through a function. This allows you to use the parameter without `.value()` as a functions configuration parameter in the later step. On the other hand, within a function, you may call `.value()` on the parameter if you need to actually use the value of the parameter. -# Engine pinning -In `extensions.yaml` there will be a line that looks like this: +### Engine pinning +In `extension.yaml` there will be a line that looks like this: ```yaml runtime: nodejs20 @@ -80,17 +81,17 @@ not yet supported. Learn the runtime and use that to update the customer's package.json to list the node engine as the runtime version. -# Switching SDK versions +### Switching SDK versions For all exported functions, replace the import with "functions.extensions.foo" to just "functions.foo". Use the other builder functions necessary to reach the same function callback. Where those builder functions expect or allow a parameter, use the named functions parameter for the configuration. -# Wrapping up +### Wrapping up If the destination directory looks like a firebase project (e.g. has a `firebase.json`) Offer to add the functions codebase to `firebase.json` for the user so that it will be included in subsequent deploys. If the user agrees, add the functions codebase to `firebase.json`. -# Testing +### Testing If there are any tests in the extensions codebase, be sure to run them after the migration. This may require modifying the test as well to point to the functions codebase. \ No newline at end of file From 79b9f499070c7c6e5600e1d94296408016272a59 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 18 Mar 2026 15:30:47 -0700 Subject: [PATCH 4/5] Update skills/extension-to-functions-codebase/SKILL.md Co-authored-by: Jeff <3759507+jhuleatt@users.noreply.github.com> --- skills/extension-to-functions-codebase/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/extension-to-functions-codebase/SKILL.md b/skills/extension-to-functions-codebase/SKILL.md index a359922..069f808 100644 --- a/skills/extension-to-functions-codebase/SKILL.md +++ b/skills/extension-to-functions-codebase/SKILL.md @@ -23,7 +23,7 @@ If there are any tests in the extensions codebase, be sure to run them after the ### Verify you are ready for the task If an extensions codebase has a feature that you do not know how to handle yet, such as -lifecycle hooks or specific IAM roles via `iamRoles` in `extension.yaml`, stop and tell the user that you cannot handle this task yet. +lifecycle hooks or specific IAM roles via `lifecycleEvents` or `iamRoles` in `extension.yaml`, stop and tell the user that you cannot handle this task yet. ### API Enablement For all API dependencies listed in `extension.yaml`, add a comment to index.js and inform the user in your final response to manually enable them. From 01b658404b084e02633cc1ca0edb3f60c754cba7 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 1 Apr 2026 11:04:53 -0700 Subject: [PATCH 5/5] Checkpoint having added more instructions on editing local codebases back --- .../extension-to-functions-codebase/SKILL.md | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/skills/extension-to-functions-codebase/SKILL.md b/skills/extension-to-functions-codebase/SKILL.md index 069f808..387accc 100644 --- a/skills/extension-to-functions-codebase/SKILL.md +++ b/skills/extension-to-functions-codebase/SKILL.md @@ -22,15 +22,30 @@ If there are any tests in the extensions codebase, be sure to run them after the ## Rules and Constraints ### Verify you are ready for the task -If an extensions codebase has a feature that you do not know how to handle yet, such as -lifecycle hooks or specific IAM roles via `lifecycleEvents` or `iamRoles` in `extension.yaml`, stop and tell the user that you cannot handle this task yet. +If an extensions codebase has a feature that you do not know how to handle +yet, such as lifecycle hooks or specific IAM roles via `iamRoles` in +`extension.yaml`, stop and tell the user that you cannot handle this task +yet. Let them decide whether or not to continue. + +### Avoid contention +When possible, avoid using the `firebase` cli, or firebase MCP server to avoid global contention. Try +to inspect `extension.yaml` and code manually. + +## Getting started +Make sure the git history is clean before proceeding because this skill uses commits. + +The user may do one of two things: ask you to convert extensions code in-place or ask you to creeate a new copy of the +extensions code. If they do the former, and the destination is in the same directory as the source, use git cp of the +code to the new location and commit with the message "Copying [extension] extension to [directory] in preparation for rewrite". + +## Steps ### API Enablement For all API dependencies listed in `extension.yaml`, add a comment to index.js and inform the user in your final response to manually enable them. ```typescript // APIs to enable: -// - +// - vision.googleapis.com ``` ### Parameterization