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
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [0.5.0] - 2026-08-20

### Changed

- Enhance with AI now selects a platform `agent_profile` directly in Notes
settings, replacing the custom `utility_agent` picker for new
configurations.
- Guided setup now distinguishes missing and ineligible agent profiles, while
preserving disabled and unconfigured legacy Utility Agent guidance.

## [0.4.0] - 2026-08-18

### Changed
Expand Down Expand Up @@ -78,4 +88,3 @@
- ci: tidy go.mod to promote direct grpc dependency (0ed27c4)
- qa: handle list toolbar on blank notes (a1c18cd)
- feat: markdown toolbar, AI-assisted proofreading, and a fixed-size note modal (4c84eb9)

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.PHONY: build run test fmt vet package package-host clean

BIN := bin/kandev-plugin-notes
VERSION := 0.4.0
VERSION := 0.5.0
STAGE := .build/stage
PKG_OUT := kandev-plugin-notes-$(VERSION).tar.gz

Expand Down
89 changes: 22 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ editor and optional AI-assisted proofreading.
them off via the checklist syntax instead.

- **Enhance with AI** — a button next to the toolbar/editor sends the note's
current markdown to your configured utility agent to proofread grammar,
current markdown to your configured agent profile to proofread grammar,
spelling, and clarity. The result is shown as a preview with
**Accept**/**Discard** before it ever replaces your note — nothing is
overwritten automatically. See "Notes are private to you" below for the
Expand Down Expand Up @@ -99,7 +99,7 @@ each see their own note; nobody else can read yours, and no task's agent can
read or write it.

**The one exception is the "Enhance with AI" button.** Clicking it sends the
note's current markdown to the utility agent configured for this plugin
note's current markdown to the agent profile configured for this plugin
(**Settings > Plugins > Notes**) via a one-shot completion
(`capabilities.agent_invoke` / `Host.InvokeUtilityAgent`) — that content
leaves the "nobody else can read it" boundary for that one request. See
Expand All @@ -112,47 +112,28 @@ description or say it in chat. This is a scratchpad, not a shared field.

## Setting up Enhance with AI

"Enhance with AI" needs **two separate settings**, both satisfied, before it
can run:

1. **Select an agent for this plugin** — Settings > Plugins > Notes,
`config_schema.utility_agent`. This is what tells the plugin which
utility agent to ask.
2. **Enable that agent, with a model** — Settings > Utility Agents. Selecting
an agent in step 1 does not enable it; a newly-added utility agent starts
disabled with no model chosen.

Both steps are required because **a disabled utility agent is usable by
kandev's own built-in features (e.g. task-create prompt enhancement) but not
by any plugin**, including this one. Kandev's own prompt-enhancement path
does not check `Enabled`; this plugin's request goes through
`Host.InvokeUtilityAgent`, which does. That asymmetry is host behavior this
plugin cannot change — clicking Enhance with an agent selected-but-disabled
fails exactly like having no agent selected at all, and the two failures now
say so explicitly rather than both pointing back at Settings > Plugins > Notes:
"Enhance with AI" needs one setting before it can run:

1. **Select an agent profile for this plugin** — Settings > Plugins > Notes,
`config_schema.agent_profile`. Pick a profile that can run utility
completions; this is the profile that proofreads the note.

The picker lists platform agent profiles directly. You do not need to create,
enable, or bind a custom **Utility Agent** for new Notes installations.

| Situation | Message points you to |
| --- | --- |
| No agent ever selected | Settings > Plugins > Notes |
| Selected agent was since deleted | Settings > Plugins > Notes |
| Selected agent exists but is disabled | **Settings > Utility Agents** ("Enable the agent") |
| Selected and enabled, but no model / agent profile bound | **Settings > Utility Agents** ("Finish setting up the agent") |
| Any other setup problem the plugin can't identify | no page named; the host's own wording is quoted instead |
| A real execution failure (the agent ran and failed) | no settings link — try again |

For the first four, **Dismiss** is joined by a second action button that
jumps straight to the right page for that cause, so there's no need to guess
which setting is missing.

The fourth row is the one most people hit, because **every built-in utility
agent ships with no model bound**: completing steps 1 and 2 above still leaves
it unconfigured. It is called out separately from "disabled" on purpose —
both are fixed on the same page but by different controls, and being told to
enable an agent you just enabled is the dead end this plugin exists to avoid.

The fifth row is the honest fallback: a `FailedPrecondition` this plugin does
not recognize (for example after a host rephrase). It names no page, because
any page it named would be a guess, and quotes the host's own wording instead.
| No profile selected | Settings > Plugins > Notes |
| Selected profile was deleted | Settings > Plugins > Notes |
| Selected profile cannot run utility completions | Settings > Plugins > Notes |
| A legacy Utility Agent is disabled | **Settings > Utility Agents** ("Enable the agent") |
| A legacy Utility Agent has no bound profile | **Settings > Utility Agents** ("Finish setting up the agent") |
| A real execution failure | no settings link — try again |

For a recognized setup problem, **Dismiss** is joined by an action that opens
the relevant settings page. The two Utility Agents messages only apply to
older configurations retained for compatibility; selecting an agent profile
in Notes is the supported setup for new installations.

## Install

Expand All @@ -168,7 +149,7 @@ curl -F "package=@kandev-plugin-notes-<version>.tar.gz" \
Sideloaded plugins register disabled/unverified; enable it in
**Settings > Plugins**. Reinstalling the same version returns 409 — bump the
version in `manifest.yaml` (and `Makefile`) first. To use "Enhance with AI",
see "Setting up Enhance with AI" above — it's a two-step setup, not one.
select an agent profile as described in "Setting up Enhance with AI" above.

## Development

Expand Down Expand Up @@ -196,29 +177,3 @@ and the [manifest reference](https://github.com/kdlbs/kandev/blob/main/docs/publ
## License

MIT — see [LICENSE](LICENSE).


The Kandev plugin SDK (`pkg/pluginsdk`) is not yet published as a standalone Go
module, so `go.mod` uses a local `replace` that expects the Kandev monorepo as a
**sibling checkout**:

```text
some-dir/
├── kandev/ # https://github.com/kdlbs/kandev — Go module at apps/backend/
└── kandev-plugin-notes/ # this repo
```

```sh
make build # host binary
make test # Go + JS unit tests
make vet
make package-host # host-platform package — fastest local loop
make package # all five platforms
```

Start with the [plugin authoring guide](https://github.com/kdlbs/kandev/blob/main/docs/public/plugins-authoring.md)
and the [manifest reference](https://github.com/kdlbs/kandev/blob/main/docs/public/plugins-manifest.md).

## License

MIT — see [LICENSE](LICENSE).
16 changes: 8 additions & 8 deletions manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# (/api/plugins/<id>/...). Keep the three in sync when bumping version.
id: "kandev-plugin-notes"
api_version: 1
version: "0.4.0"
version: "0.5.0"
display_name: "Notes"
description: "A private, per-user scratchpad note on any task or workspace: a dockview/mobile panel, a sidebar workspace-notes button, markdown editing with a formatting toolbar, AI-assisted proofreading with guided setup, and a kanban card shortcut."
author: "yattdev"
Expand All @@ -29,7 +29,7 @@ runtime:
# per-user browser-reachable storage (host.storage) for the note itself, plus
# agent_invoke so the "Enhance with AI" button can run a one-shot proofreading
# completion via Host.InvokeUtilityAgent (see server/plugin.go and the
# `utility_agent` config property below).
# `agent_profile` config property below).
capabilities:
user_state: true
agent_invoke: true
Expand All @@ -40,18 +40,18 @@ capabilities:
# and returns the improved markdown for the UI's preview/Accept/Discard flow.
webhooks:
- key: "enhance"
description: "Proofread and improve the current note's markdown via the configured utility agent"
description: "Proofread and improve the current note's markdown via the configured agent profile"
method: "POST"

# config_schema: agent_invoke requires a utility_agent picker (rendered at
# Settings > Plugins > Notes) so the operator selects which agent runs the
# enhance completion. Calling Host.InvokeUtilityAgent without a valid
# selection here returns a distinguishable FailedPrecondition (see
# config_schema: agent_invoke requires an agent_profile picker (rendered at
# Settings > Plugins > Notes) so the operator selects the platform profile
# that runs the enhance completion. Calling Host.InvokeUtilityAgent without a
# valid selection returns a distinguishable FailedPrecondition (see
# server/plugin.go), surfaced by the UI as a non-fatal message.
config_schema:
type: object
properties:
utility_agent: { type: string, format: utility-agent, title: "Utility Agent", description: "Agent used to proofread notes for the Enhance with AI button" }
agent_profile: { type: string, format: agent-profile, title: "Agent profile", description: "Agent profile used to proofread notes for the Enhance with AI button" }

# ui.bundle points at the hand-written, no-build ES module frontend half.
ui:
Expand Down
11 changes: 6 additions & 5 deletions server/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func TestManifestCapabilities_UserStateAndAgentInvoke(t *testing.T) {
require.Equal(t, map[string]any{"user_state": true, "agent_invoke": true}, m.Capabilities)
}

func TestManifest_DeclaresEnhanceWebhookAndUtilityAgentConfig(t *testing.T) {
func TestManifest_DeclaresEnhanceWebhookAndAgentProfileConfig(t *testing.T) {
m := loadManifest(t)
require.Len(t, m.Webhooks, 1)
webhook, ok := m.Webhooks[0].(map[string]any)
Expand All @@ -129,10 +129,11 @@ func TestManifest_DeclaresEnhanceWebhookAndUtilityAgentConfig(t *testing.T) {
require.NotEmpty(t, m.ConfigSchema)
properties, ok := m.ConfigSchema["properties"].(map[string]any)
require.True(t, ok, "config_schema.properties should decode as a map")
utilityAgent, ok := properties["utility_agent"].(map[string]any)
require.True(t, ok, "config_schema.properties.utility_agent should decode as a map")
require.Equal(t, "string", utilityAgent["type"])
require.Equal(t, "utility-agent", utilityAgent["format"])
agentProfile, ok := properties["agent_profile"].(map[string]any)
require.True(t, ok, "config_schema.properties.agent_profile should decode as a map")
require.Equal(t, "string", agentProfile["type"])
require.Equal(t, "agent-profile", agentProfile["format"])
require.NotContains(t, properties, "utility_agent")
}

func TestManifestUI_BundlePathHasLeadingSlash(t *testing.T) {
Expand Down
Loading
Loading