Skip to content
Merged
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
14 changes: 10 additions & 4 deletions .claude/skills/credential-create/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ Naming rules:
- Function: `New<Provider><AuthType>Credential` (PascalCase). For OAuth2/OAuth1 the suffix is `OAuth2` / `OAuth1`, not `Oauth2`.
- Credential `ID`: `<provider>_<authtype>` (e.g., `mistral_api`, `notion_oauth2`)

## Icon

A credential wears its provider's plain brand mark; the glyph badge belongs to
individual nodes, so `CredentialIcon` carries only `Brand`. The value names the
artwork under `apps/platform/public/assets/icons/brands/<brand>/{light,dark}.svg`
— reuse the provider's existing brand, or drop the two files in for a provider
that has none. Nothing registers a brand in code.

## Templates

### API key credential
Expand All @@ -64,8 +72,7 @@ func New<Provider>ApiCredential() *domain.Credential {
Name: "<DisplayName>",
Description: "<one-line description>",
Icon: domain.CredentialIcon{
Light: "<provider>",
Dark: "<provider>",
Brand: "<brand>",
},
Schema: &gjs.Schema{
Type: "object",
Expand Down Expand Up @@ -117,8 +124,7 @@ func New<Provider>OAuth2Credential(redirectURL string) *domain.Credential {
Name: "<DisplayName>",
Description: "<one-line description>",
Icon: domain.CredentialIcon{
Light: "<provider>",
Dark: "<provider>",
Brand: "<brand>",
},
Schema: &gjs.Schema{
Type: "object",
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/credential-update/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ grep -rn 'SupportedCredentials.*"<credential_id>"' internal/nodeengine/nodes/

#### Update display metadata

`Name`, `Description`, `Icon` are independent and non-breaking.
`Name`, `Description`, `Icon` are independent and non-breaking. `CredentialIcon` carries only a `Brand` — the provider's plain mark, named after a folder under the platform UI's `public/assets/icons/brands/`; the glyph badge belongs to individual nodes.

#### Disable / enable

Expand Down
54 changes: 52 additions & 2 deletions .claude/skills/node-create/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,50 @@ When **adding a new action to an existing provider**, only create `<action>/node

When **adding a new provider from scratch**, create the entire tree above, then add the provider's `Register()` call to `internal/nodeengine/nodes/nodes.go`.

## Icon and handles

A node states what it looks like and how it connects; nothing is derived from
its name or category.

**Icon** is a brand plus a glyph. `Brand` names the artwork under
`apps/platform/public/assets/icons/brands/<brand>/{light,dark}.svg` — reuse the
provider's existing brand (`gmail`, `slack`, `x`). A provider that has no mark
yet needs one dropped in that folder; no code registers it. `Glyph` names the
action badge under `.../glyphs/<glyph>.svg` — reuse an existing one where it
fits (`send`, `search`, `create`, `list`, `eye`, `trash`, `pencil`, `image`,
`film`, `speaker`, `music`, `chat`, `folder`, `file`, `table`, `record`,
`branch`, `clock`, `play`, `note`, `story`, `upload`, `organize`, `translate`,
`trending`, `target`). Two nodes of the same provider must not share a glyph —
that is the only thing telling them apart in a list.

A system primitive with no brand behind it declares only `Glyph`.

**Handles** are the connection points, and every node lists its own; there is no
default. Almost every node is one in, one out:

```go
Inputs: []nodes.NodeHandle{
{Key: "in"},
},
Outputs: []nodes.NodeHandle{
{Key: "out"},
},
```

A node with no way in declares `Inputs: []nodes.NodeHandle{}` (an empty, non-nil
slice — `nil` would mean "not declared"). A node that branches declares one
output per branch with a `Label`, which the canvas prints next to the dot:

```go
Outputs: []nodes.NodeHandle{
{Key: "true", Label: "True"},
{Key: "false", Label: "False"},
},
```

Edges record which output they leave from, so a key is part of the saved
workflow — renaming one breaks every flow that uses it.

## Annotations classification

Every node carries `Annotations: nodes.NodeAnnotations{...}` describing how the tool affects its environment. MCP clients (Claude Desktop, Cursor, etc.) use these hints to decide when to ask the user for confirmation. Pick one of these groups:
Expand Down Expand Up @@ -145,8 +189,14 @@ func New<Provider><Action>Node(nodeID string) *<Provider><Action>Node {
Name: "<DisplayName>",
Description: "<one-line description>",
Icon: nodes.NodeIcon{
Light: nodeID,
Dark: nodeID,
Brand: "<brand>",
Glyph: "<glyph>",
},
Inputs: []nodes.NodeHandle{
{Key: "in"},
},
Outputs: []nodes.NodeHandle{
{Key: "out"},
},
Categories: []string{"<Category>"},
SubCategories: []string{"<SubCategory>"},
Expand Down
4 changes: 4 additions & 0 deletions .claude/skills/node-update/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ Verify the Go type still matches the new values (e.g., changing from `int` to `f

`Name`, `Description`, `Icon`, `Categories`, `SubCategories`, `Tags` are independent. Tags must remain lowercase; categories must use one of the existing top-level values (memory: `node-create` skill lists them).

`Icon` is a `Brand` (the provider's mark) plus a `Glyph` (the action badge), both naming files under the platform UI's `public/assets/icons/`; see the `node-create` skill for the glyph vocabulary and the rule that two nodes of one provider must not share a glyph.

`Inputs`/`Outputs` are a different matter — they are not metadata. A handle `Key` is written into every edge of every saved workflow, so renaming or removing one breaks each flow that uses it. Adding an output is safe; changing an existing key is a migration, not an update.

#### Add / remove a supported credential

```go
Expand Down
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ The mechanics behind the headline features are documented next to the code:
| --- | --- |
| The canvas model — `$references`, credential references, deliberate simplicity | [`workflows`](apps/platform-api/internal/workflows/README.md) |
| AI workflow generation from chat (SSE streaming) | [`workflows`](apps/platform-api/internal/workflows/README.md) |
| One node descriptor → UI form, validation, function calling, MCP tool | [`nodeengine`](apps/platform-api/internal/nodeengine/README.md) |
| One node descriptor → UI form, icon, connection points, validation, function calling, MCP tool | [`nodeengine`](apps/platform-api/internal/nodeengine/README.md) |
| How a node becomes an MCP tool | [`mcp`](apps/platform-api/internal/mcp/README.md) |
| Task scheduling, DAG execution order, prompt layering | [`taskrunner`](apps/platform-api/internal/taskrunner/README.md) |
| Trigger types, webhook adapters, runtime config overlay | [`triggers`](apps/platform-api/internal/triggers/README.md) |
Expand Down
4 changes: 3 additions & 1 deletion apps/platform-api/internal/nodeengine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ The node engine owns the **definitions** of everything a workflow can do: each n

## Core concepts

- **Node** (`domain/nodes/node.go`): a declarative action descriptor — `ID`, `Version`, name/description, `InputSchema`/`OutputSchema` (`jsonschema-go`), `Categories`/`SubCategories`/`Tags`, `SupportedCredentials`, `Annotations`, `HasNaturalLanguage`, `Disabled`. Implements `NodeManager` (getter interface).
- **Node** (`domain/nodes/node.go`): a declarative action descriptor — `ID`, `Version`, name/description, `Icon` (a `Brand` naming the provider's mark plus a `Glyph` naming the action badge), `Inputs`/`Outputs` (the connection points, each a `Key` and an optional `Label`; every node states its own, there is no default), `InputSchema`/`OutputSchema` (`jsonschema-go`), `Categories`/`SubCategories`/`Tags`, `SupportedCredentials`, `Annotations`, `HasNaturalLanguage`, `Disabled`. Implements `NodeManager` (getter interface).

A node describes how it looks and how it connects, so the UI derives nothing: the artwork lives under `apps/platform/public/assets/icons/{brands,glyphs}/` and is composed at render time. `system_starter` declares an empty `Inputs` slice (no way in), `system_condition` declares two outputs labelled True and False. Credentials (`CredentialIcon`) and MCP servers (`ServerIcon`) name a `Brand` only — the glyph badge belongs to individual nodes.
- **Executor** (`domain/executors/executor.go`): the behavior for a node, keyed by the same ID. `ExecutorManager.ExecuteWithContext(ctx, credentials, data)` is the execution entry point.
- **Credential** (`domain/credentials/credential.go`): a provider auth descriptor — schema, `IsOAuth1`/`IsOAuth2`/`IsSupportPlatform`, `SupportedNodes`. OAuth credentials may implement `RefreshableCredential`. `IsSupportPlatform` is auto-derived at load (host-provided OAuth apps).
- **Adapter** (`domain/adapters/adapter.go`): a trigger adapter that maps a raw webhook payload into a `TriggerContext` (`source`/`sender`/`prompt`/`payload`, exposed as `$trigger.*` variables).
Expand Down