Skip to content
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

Add skill for editing card definitions #2192

Merged

Conversation

jurgenwerk
Copy link
Contributor

@jurgenwerk jurgenwerk commented Feb 24, 2025

This adds a basic coding skill to the base realm, and it is by default used in the AI assistant when in code mode. The user is able to ask questions about the attached file (currently tested with gts files). The skill enables the model to understand the anatomy of a boxel card definition, so that the user can submit prompts like "add a field", "remove this field", "implement template for this card", "make this editable",...

Here I am asking the assistant to add a couple of fields in the definition, check how the response looks like–it responds with steps the user must take to make it happen (related to this also check my comment #2192 (comment)):

image

Copy link

github-actions bot commented Feb 24, 2025

Host Test Results

    1 files  ±0      1 suites  ±0   23m 49s ⏱️ +37s
775 tests ±0  773 ✔️ ±0  2 💤 ±0  0 ±0 
780 runs  ±0  778 ✔️ ±0  2 💤 ±0  0 ±0 

Results for commit 37fc68e. ± Comparison against base commit 4de24d8.

♻️ This comment has been updated with latest results.

"data": {
"type": "card",
"attributes": {
"instructions": "Boxel is a platform where people can create Cards, which under the hood are built out of glimmer components and ember.\n\nCards are independent linkable items that get an ID. Fields are contained within cards, so sometimes a user wants a custom field, but usually it's creating a card (derived from CardDef).\n\nUse glimmer templating and typescript for the code. Remember the limitations of logic within glimmer templating code. Basic interaction for editing fields is handled for you by boxel, you don't need to create that (e.g. StringField has an edit template that allows a user to edit the data). Computed fields can support more complex work, and update automatically for you. Interaction (button clicks, filtering on user typed content) may require glimmer & ember functionality (see action and tracked in the example below).\n\nCards you create have three templates. If you do not specify them they are automatically created for you, but users often want custom templates. Each template is a glimmer template and can use ember functionality. These are specified as static in the card definition:\n\nimport { contains, containsMany, linksToMany, field, CardDef, Component, } from 'https://cardstack.com/base/card-api'; import StringField from 'https://cardstack.com/base/string'; import NumberField from 'https://cardstack.com/base/number'; import BooleanField from 'https://cardstack.com/base/boolean'; // Important, this is the tracked decorator import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { fn } from '@ember/helper'; import { on } from '@ember/modifier';\n\nexport class MyCustomCard extends CardDef { static displayName = 'BoxelBuddyGuestList';\n\n// linksTo and linksToMany @field linkedData = linksToMany(() => AnotherCard);\n\n// A field that is computed from other data in the card @field computedData = contains(NumberField, { computeVia: function (this: MyCustomCard) { // implementation logic here return 1; }, });\n\n// Isolated templates are used when items are viewed on their own. Default to the isolated template static isolated = class Isolated extends Component { // Use tracked and action decorators to be able to use interactivity in the templated @tracked trackedValue = []; @action interactivity(event: InputEvent) {} // Glimmer template goes here, make sure the style tag is at the top level of the template }; // Embedded is when they appear in other cards static embedded = class Embedded extends Component { };\n\n// Fitted templates should be responsive to the size of the container they appear in static fitted = class Fitted extends Component { };\n\n// Edit is for the user editing the data. Use @fields let the field render itself static edit = class Edit extends Component { }; }\n\n@fields.fieldName lets the field render itself, very useful for editable fields. @model.fieldName gets the value out of the field.\n\nImportant:\n\nIt is extremely important you use the following imports for interactivity: import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { fn, get } from '@ember/helper'; import { on } from '@ember/modifier';\n\nRemember to define a field the following syntax is used:\n\n@field fieldname = contains(FieldType); @field fieldname = containsMany(FieldType);\n\nIf user asks you to make something editable, use `contains` or `containsMany` syntax for adding a field.\n\nAnd for linking to other cards:\n\n@field fieldname = linksTo(() => CardType); @field fieldname = linksToMany(() => CardType);\n\nYou can ask followups\n\nYou can propose new/improved data structures\n\nWhen writing the glimmer template, ensure that the style tags appear within the template tag, as the last item in them. You should use useful class names and a sensible structure as you build this. Use single quotes for the class names.\n\nWhen writing this, take care to remember ember and glimmer oddities. Accessing a list by index should use this format:\n\n{{(get this.args.model.fieldWithAList index)}}\n\nValues from the model can be directly inserted with\n\n{{this.args.model.fieldName}}\n\nand you can delegate rendering to the field with\n\n<@fields.fieldName />\n\nYou must be careful with the templates, remember glimmer rules. Do not put a dollar sign ($) directly in front of the brackets.\n\nUnless otherwise instructed, use a modern but stylish theme. In responses regarding to attached files, respond with a series of code patches where you output gts code and mark it whether it's for adding or deleting, in a clear succession, so that user can quickly just copy paste and put it in the code file, or delete code. This should include the file name and line numbers where the change should happen.",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was mostly copied from the "cursor skill" that lives in the experiments realm in production that Chris uses in cursor. I was told this skill was already tested in practice and it brought some favorable results - so we thought with Ian this could be a good starting point for the coding skill.

Also yes, this is unreadable in a form like this, the way I was editing and reading it was through the interface:

image

Here's the full version:

Boxel is a platform where people can create Cards, which under the hood are built out of glimmer components and ember.

Cards are independent linkable items that get an ID. Fields are contained within cards, so sometimes a user wants a custom field, but usually it's creating a card (derived from CardDef).

Use glimmer templating and typescript for the code. Remember the limitations of logic within glimmer templating code. Basic interaction for editing fields is handled for you by boxel, you don't need to create that (e.g. StringField has an edit template that allows a user to edit the data). Computed fields can support more complex work, and update automatically for you. Interaction (button clicks, filtering on user typed content) may require glimmer & ember functionality (see action and tracked in the example below).

Cards you create have three templates. If you do not specify them they are automatically created for you, but users often want custom templates. Each template is a glimmer template and can use ember functionality. These are specified as static in the card definition:

import { contains, containsMany, linksToMany, field, CardDef, Component, } from 'https://cardstack.com/base/card-api'; import StringField from 'https://cardstack.com/base/string'; import NumberField from 'https://cardstack.com/base/number'; import BooleanField from 'https://cardstack.com/base/boolean'; // Important, this is the tracked decorator import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { fn } from '@ember/helper'; import { on } from '@ember/modifier';

export class MyCustomCard extends CardDef { static displayName = 'BoxelBuddyGuestList';

// linksTo and linksToMany @field linkedData = linksToMany(() => AnotherCard);

// A field that is computed from other data in the card @field computedData = contains(NumberField, { computeVia: function (this: MyCustomCard) { // implementation logic here return 1; }, });

// Isolated templates are used when items are viewed on their own. Default to the isolated template static isolated = class Isolated extends Component { // Use tracked and action decorators to be able to use interactivity in the templated @tracked trackedValue = []; @action interactivity(event: InputEvent) {} // Glimmer template goes here, make sure the style tag is at the top level of the template }; // Embedded is when they appear in other cards static embedded = class Embedded extends Component { };

// Fitted templates should be responsive to the size of the container they appear in static fitted = class Fitted extends Component { };

// Edit is for the user editing the data. Use @fields let the field render itself static edit = class Edit extends Component { }; }

@fields.fieldName lets the field render itself, very useful for editable fields. @model.fieldName gets the value out of the field.

Important:

It is extremely important you use the following imports for interactivity: import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { fn, get } from '@ember/helper'; import { on } from '@ember/modifier';

Remember to define a field the following syntax is used:

@field fieldname = contains(FieldType); @field fieldname = containsMany(FieldType);

If user asks you to make something editable, use contains or containsMany syntax for adding a field.

And for linking to other cards:

@field fieldname = linksTo(() => CardType); @field fieldname = linksToMany(() => CardType);

You can ask followups

You can propose new/improved data structures

When writing the glimmer template, ensure that the style tags appear within the template tag, as the last item in them. You should use useful class names and a sensible structure as you build this. Use single quotes for the class names.

When writing this, take care to remember ember and glimmer oddities. Accessing a list by index should use this format:

{{(get this.args.model.fieldWithAList index)}}

Values from the model can be directly inserted with

{{this.args.model.fieldName}}

and you can delegate rendering to the field with

<@fields.fieldName />

You must be careful with the templates, remember glimmer rules. Do not put a dollar sign ($) directly in front of the brackets.

Unless otherwise instructed, use a modern but stylish theme. In responses regarding to attached files, respond with a series of code patches where you output gts code and mark it whether it's for adding or deleting, in a clear succession, so that user can quickly just copy paste and put it in the code file, or delete code. This should include the file name and line numbers where the change should happen.

"data": {
"type": "card",
"attributes": {
"instructions": "Boxel is a platform where people can create Cards, which under the hood are built out of glimmer components and ember.\n\nCards are independent linkable items that get an ID. Fields are contained within cards, so sometimes a user wants a custom field, but usually it's creating a card (derived from CardDef).\n\nUse glimmer templating and typescript for the code. Remember the limitations of logic within glimmer templating code. Basic interaction for editing fields is handled for you by boxel, you don't need to create that (e.g. StringField has an edit template that allows a user to edit the data). Computed fields can support more complex work, and update automatically for you. Interaction (button clicks, filtering on user typed content) may require glimmer & ember functionality (see action and tracked in the example below).\n\nCards you create have three templates. If you do not specify them they are automatically created for you, but users often want custom templates. Each template is a glimmer template and can use ember functionality. These are specified as static in the card definition:\n\nimport { contains, containsMany, linksToMany, field, CardDef, Component, } from 'https://cardstack.com/base/card-api'; import StringField from 'https://cardstack.com/base/string'; import NumberField from 'https://cardstack.com/base/number'; import BooleanField from 'https://cardstack.com/base/boolean'; // Important, this is the tracked decorator import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { fn } from '@ember/helper'; import { on } from '@ember/modifier';\n\nexport class MyCustomCard extends CardDef { static displayName = 'BoxelBuddyGuestList';\n\n// linksTo and linksToMany @field linkedData = linksToMany(() => AnotherCard);\n\n// A field that is computed from other data in the card @field computedData = contains(NumberField, { computeVia: function (this: MyCustomCard) { // implementation logic here return 1; }, });\n\n// Isolated templates are used when items are viewed on their own. Default to the isolated template static isolated = class Isolated extends Component { // Use tracked and action decorators to be able to use interactivity in the templated @tracked trackedValue = []; @action interactivity(event: InputEvent) {} // Glimmer template goes here, make sure the style tag is at the top level of the template }; // Embedded is when they appear in other cards static embedded = class Embedded extends Component { };\n\n// Fitted templates should be responsive to the size of the container they appear in static fitted = class Fitted extends Component { };\n\n// Edit is for the user editing the data. Use @fields let the field render itself static edit = class Edit extends Component { }; }\n\n@fields.fieldName lets the field render itself, very useful for editable fields. @model.fieldName gets the value out of the field.\n\nImportant:\n\nIt is extremely important you use the following imports for interactivity: import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { fn, get } from '@ember/helper'; import { on } from '@ember/modifier';\n\nRemember to define a field the following syntax is used:\n\n@field fieldname = contains(FieldType); @field fieldname = containsMany(FieldType);\n\nIf user asks you to make something editable, use `contains` or `containsMany` syntax for adding a field.\n\nAnd for linking to other cards:\n\n@field fieldname = linksTo(() => CardType); @field fieldname = linksToMany(() => CardType);\n\nYou can ask followups\n\nYou can propose new/improved data structures\n\nWhen writing the glimmer template, ensure that the style tags appear within the template tag, as the last item in them. You should use useful class names and a sensible structure as you build this. Use single quotes for the class names.\n\nWhen writing this, take care to remember ember and glimmer oddities. Accessing a list by index should use this format:\n\n{{(get this.args.model.fieldWithAList index)}}\n\nValues from the model can be directly inserted with\n\n{{this.args.model.fieldName}}\n\nand you can delegate rendering to the field with\n\n<@fields.fieldName />\n\nYou must be careful with the templates, remember glimmer rules. Do not put a dollar sign ($) directly in front of the brackets.\n\nUnless otherwise instructed, use a modern but stylish theme. In responses regarding to attached files, respond with a series of code patches where you output gts code and mark it whether it's for adding or deleting, in a clear succession, so that user can quickly just copy paste and put it in the code file, or delete code. This should include the file name and line numbers where the change should happen.",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note the last part in the skill:

In responses regarding to attached files, respond with a series of code patches where you output gts code and mark it whether it's for adding or deleting, in a clear succession, so that user can quickly just copy paste and put it in the code file, or delete code. This should include the file name and line numbers where the change should happen.

The end goal is to have a patching system in place where user would apply changes (using commands), but before we have that (the approaches for patching are still in research phase), we discussed with Ian that it would be a good first step that the assistant responds with concrete instructions or steps on how to achieve the desired outcome. When we have the command, we can modify the prompt to ask the model to respond with the expected patching/diffing format.

@jurgenwerk jurgenwerk changed the title Add skill for editing code of modules and instances Add skill for editing code of code modules Feb 24, 2025
@jurgenwerk jurgenwerk changed the title Add skill for editing code of code modules Add skill for editing card definitions Feb 24, 2025
@jurgenwerk jurgenwerk marked this pull request as ready for review February 24, 2025 12:40
@jurgenwerk jurgenwerk requested review from a team and IanCal February 24, 2025 12:40
Copy link
Contributor

@IanCal IanCal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The skill is missing some newlines, maybe a copy & paste issue?

// linksTo and linksToMany @field linkedData = linksToMany(() => AnotherCard);

Also just to check, this means that just swapping mode adds this skill to the open room you're in? I think this gets a bit inconsistent, I think just having it be the default for new rooms would be fine, unless there's an issue around this.

@jurgenwerk
Copy link
Contributor Author

@IanCal Yeah it looks like it was copy&paste issue. I fixed the new lines:

image

Also just to check, this means that just swapping mode adds this skill to the open room you're in? I think this gets a bit inconsistent, I think just having it be the default for new rooms would be fine, unless there's an issue around this.

It will add the coding skill when swapping mode, and it will also add it in new rooms. If other things are good now, are you ok if we fix that inconsistency in a separate PR (I'll make a ticket)?

@jurgenwerk jurgenwerk merged commit 88e77dd into main Feb 25, 2025
48 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants