From d80014099c8c59abe280a095af7ad6198d8dbe33 Mon Sep 17 00:00:00 2001 From: Marcin Piniarski Date: Fri, 10 Jul 2026 23:51:08 +0200 Subject: [PATCH 01/22] Add tasks first-party template from agent-native-tasks. Import the task-list-first app as templates/tasks, register it as a hidden template for monorepo dev and CLI scaffolding, and trim framework boilerplate pages so the UI focuses on inbox, tasks, and fields. Co-authored-by: Cursor --- .changeset/tasks-template.md | 5 + packages/core/src/cli/templates-meta.ts | 13 + packages/core/src/client/org/OrgSwitcher.tsx | 2 + .../src/renderer/components/Sidebar.tsx | 2 + packages/shared-app-config/templates.ts | 13 + pnpm-lock.yaml | 238 +++++ .../tasks/.agents/commands/visual-plan.md | 9 + .../tasks/.agents/commands/visual-recap.md | 8 + .../tasks/.agents/skills/actions/SKILL.md | 435 +++++++++ .../.agents/skills/adding-a-feature/SKILL.md | 178 ++++ .../.agents/skills/agent-native-docs/SKILL.md | 63 ++ .../.agents/skills/agent-watchdog/README.md | 43 + .../.agents/skills/agent-watchdog/SKILL.md | 110 +++ .../skills/agent-watchdog/agents/openai.yaml | 4 + .../.agents/skills/capture-learnings/SKILL.md | 76 ++ .../.agents/skills/change-summary/SKILL.md | 83 ++ .../.agents/skills/create-skill/SKILL.md | 214 +++++ .../.agents/skills/delegate-to-agent/SKILL.md | 262 ++++++ .../.agents/skills/efficient-fable/README.md | 75 ++ .../.agents/skills/efficient-fable/SKILL.md | 95 ++ .../assets/fable-orchestrator-dark.png | Bin 0 -> 111350 bytes .../assets/fable-orchestrator.excalidraw | 833 ++++++++++++++++++ .../assets/fable-orchestrator.png | Bin 0 -> 111227 bytes .../skills/efficient-frontier/README.md | 53 ++ .../skills/efficient-frontier/SKILL.md | 73 ++ .../.agents/skills/frontend-design/SKILL.md | 109 +++ .../.agents/skills/plan-arbiter/README.md | 41 + .../.agents/skills/plan-arbiter/SKILL.md | 114 +++ .../skills/plan-arbiter/agents/openai.yaml | 4 + .../tasks/.agents/skills/plow-ahead/README.md | 42 + .../tasks/.agents/skills/plow-ahead/SKILL.md | 104 +++ .../skills/plow-ahead/agents/openai.yaml | 4 + .../.agents/skills/quick-recap/README.md | 55 ++ .../tasks/.agents/skills/quick-recap/SKILL.md | 54 ++ .../skills/read-the-damn-docs/README.md | 54 ++ .../skills/read-the-damn-docs/SKILL.md | 126 +++ .../read-the-damn-docs/agents/openai.yaml | 4 + .../.agents/skills/real-time-sync/SKILL.md | 216 +++++ .../tasks/.agents/skills/security/SKILL.md | 262 ++++++ .../skills/self-modifying-code/SKILL.md | 81 ++ .../tasks/.agents/skills/shadcn-ui/SKILL.md | 94 ++ .../skills/stay-within-limits/README.md | 61 ++ .../skills/stay-within-limits/SKILL.md | 83 ++ .../tasks/.agents/skills/visual-plan/SKILL.md | 547 ++++++++++++ .../visual-plan/agent-native-skill.json | 12 + .../skills/visual-plan/references/canvas.md | 121 +++ .../references/document-quality.md | 178 ++++ .../skills/visual-plan/references/exemplar.md | 62 ++ .../visual-plan/references/wireframe.md | 264 ++++++ .../.agents/skills/visual-recap/SKILL.md | 634 +++++++++++++ .../visual-recap/agent-native-skill.json | 12 + .../visual-recap/references/wireframe.md | 264 ++++++ .../tasks/.claude/commands/visual-plan.md | 9 + .../tasks/.claude/commands/visual-recap.md | 8 + templates/tasks/.claude/skills | 1 + templates/tasks/.env.example | 5 + templates/tasks/.gitignore | 55 ++ templates/tasks/.ignore | 0 templates/tasks/.prettierrc | 5 + templates/tasks/AGENTS.md | 125 +++ templates/tasks/CLAUDE.md | 1 + templates/tasks/DEVELOPING.md | 185 ++++ templates/tasks/README.md | 15 + .../actions/bulk-delete-inbox-items.test.ts | 58 ++ .../tasks/actions/bulk-delete-inbox-items.ts | 21 + .../tasks/actions/bulk-delete-tasks.test.ts | 56 ++ templates/tasks/actions/bulk-delete-tasks.ts | 21 + .../tasks/actions/bulk-update-tasks.test.ts | 104 +++ templates/tasks/actions/bulk-update-tasks.ts | 35 + .../tasks/actions/create-custom-field.test.ts | 45 + .../tasks/actions/create-custom-field.ts | 21 + .../tasks/actions/create-inbox-item.test.ts | 56 ++ templates/tasks/actions/create-inbox-item.ts | 29 + templates/tasks/actions/create-task.ts | 32 + .../tasks/actions/delete-custom-field.ts | 18 + .../tasks/actions/delete-inbox-item.test.ts | 48 + templates/tasks/actions/delete-inbox-item.ts | 16 + templates/tasks/actions/delete-task.ts | 15 + .../actions/lib/boolean-query-param.test.ts | 36 + .../tasks/actions/lib/boolean-query-param.ts | 29 + templates/tasks/actions/list-custom-fields.ts | 17 + .../tasks/actions/list-inbox-items.test.ts | 53 ++ templates/tasks/actions/list-inbox-items.ts | 15 + templates/tasks/actions/list-tasks.ts | 36 + .../tasks/actions/list-visible-task-fields.ts | 17 + .../actions/mark-inbox-item-ready.test.ts | 60 ++ .../tasks/actions/mark-inbox-item-ready.ts | 30 + templates/tasks/actions/navigate.test.ts | 66 ++ templates/tasks/actions/navigate.ts | 49 ++ .../tasks/actions/reorder-custom-fields.ts | 24 + .../tasks/actions/reorder-inbox-items.test.ts | 71 ++ .../tasks/actions/reorder-inbox-items.ts | 21 + templates/tasks/actions/reorder-tasks.ts | 26 + templates/tasks/actions/run.ts | 2 + .../tasks/actions/update-custom-field.test.ts | 78 ++ .../tasks/actions/update-custom-field.ts | 31 + .../tasks/actions/update-inbox-item.test.ts | 73 ++ templates/tasks/actions/update-inbox-item.ts | 22 + templates/tasks/actions/update-task.test.ts | 196 +++++ templates/tasks/actions/update-task.ts | 80 ++ .../actions/update-visible-task-fields.ts | 26 + .../tasks/actions/view-screen-helpers.ts | 70 ++ templates/tasks/actions/view-screen.test.ts | 757 ++++++++++++++++ templates/tasks/actions/view-screen.ts | 210 +++++ .../custom-fields/FieldCreateBar.tsx | 119 +++ .../components/custom-fields/FieldListRow.tsx | 115 +++ .../components/custom-fields/FieldsList.tsx | 216 +++++ .../custom-fields/FieldsListSkeleton.tsx | 18 + .../components/custom-fields/FieldsPage.tsx | 41 + .../editor/FieldEditorSidebar.tsx | 67 ++ .../editor/FieldTitleSection.tsx | 30 + .../editor/config/CurrencyConfigControl.tsx | 48 + .../editor/config/FieldConfigControl.tsx | 55 ++ .../editor/config/NumberConfigControl.tsx | 51 ++ .../editor/config/PercentConfigControl.tsx | 32 + .../editor/config/SelectConfigControl.tsx | 130 +++ .../editor/config/select-colors.ts | 23 + .../custom-fields/editor/config/utils.ts | 52 ++ .../components/custom-fields/editor/types.ts | 7 + .../tasks/app/components/dnd/SortableItem.tsx | 57 ++ .../tasks/app/components/dnd/SortableList.tsx | 129 +++ .../dnd/reorder-moving-items.test.ts | 126 +++ .../components/dnd/reorder-moving-items.ts | 160 ++++ .../app/components/dnd/sortable-drag-props.ts | 62 ++ .../tasks/app/components/inbox/InboxList.tsx | 206 +++++ .../app/components/inbox/InboxListPage.tsx | 38 + .../app/components/inbox/InboxListRow.tsx | 126 +++ .../tasks/app/components/layout/Header.tsx | 55 ++ .../app/components/layout/HeaderActions.tsx | 84 ++ .../tasks/app/components/layout/Layout.tsx | 134 +++ .../tasks/app/components/layout/Sidebar.tsx | 211 +++++ .../components/shared/AddListItemInput.tsx | 57 ++ .../app/components/shared/AlertDialog.tsx | 52 ++ .../components/shared/BulkDeleteDialog.tsx | 73 ++ .../app/components/shared/ChipSelect.tsx | 235 +++++ .../components/shared/DeleteItemDialog.tsx | 39 + .../app/components/shared/InlineEditable.tsx | 165 ++++ .../app/components/shared/ListEmptyState.tsx | 13 + .../components/shared/ListErrorMessage.tsx | 15 + .../app/components/shared/ListViewHeader.tsx | 74 ++ .../app/components/shared/PageHeader.tsx | 25 + .../app/components/shared/RowActionsMenu.tsx | 41 + .../tasks/app/components/shared/SidePanel.tsx | 98 +++ .../components/shared/dnd/ListRowPreview.tsx | 97 ++ .../tasks/app/components/shared/list/List.tsx | 63 ++ .../app/components/shared/list/ListRow.tsx | 162 ++++ .../shared/list/ListRowDragHandle.tsx | 51 ++ .../shared/list/ListSkeletonRows.tsx | 11 + .../shared/list/SortableListItemShell.tsx | 31 + .../tasks/app/components/shared/list/types.ts | 1 + .../shared/selection/ListSelectionBar.tsx | 170 ++++ .../selection/ListSelectionHeaderToggle.tsx | 27 + .../shared/selection/ListSelectionToolbar.tsx | 76 ++ .../selection/get-list-row-selection-ui.ts | 40 + .../shared/selection/use-list-selection.ts | 193 ++++ .../tasks/app/components/tasks/TaskList.tsx | 330 +++++++ .../components/tasks/TaskListHeaderRow.tsx | 42 + .../app/components/tasks/TaskListPage.tsx | 96 ++ .../app/components/tasks/TaskListRow.css | 49 ++ .../app/components/tasks/TaskListRow.tsx | 312 +++++++ .../tasks/fields/TaskFieldsSidebar.tsx | 100 +++ .../tasks/fields/TaskTitleSection.tsx | 25 + .../fields/controls/DateValueControl.tsx | 21 + .../fields/controls/FieldValueControl.tsx | 75 ++ .../controls/MultiSelectValueControl.tsx | 52 ++ .../fields/controls/NumberValueControl.tsx | 56 ++ .../fields/controls/RichTextValueControl.tsx | 114 +++ .../controls/SingleSelectValueControl.tsx | 61 ++ .../fields/controls/TextValueControl.tsx | 20 + .../use-task-row-completion-animation.ts | 176 ++++ .../tasks/app/components/ui/accordion.tsx | 56 ++ .../tasks/app/components/ui/alert-dialog.tsx | 139 +++ templates/tasks/app/components/ui/alert.tsx | 59 ++ .../tasks/app/components/ui/aspect-ratio.tsx | 5 + templates/tasks/app/components/ui/avatar.tsx | 48 + templates/tasks/app/components/ui/badge.tsx | 37 + .../tasks/app/components/ui/breadcrumb.tsx | 115 +++ templates/tasks/app/components/ui/button.tsx | 59 ++ .../tasks/app/components/ui/calendar.tsx | 68 ++ templates/tasks/app/components/ui/card.tsx | 86 ++ .../tasks/app/components/ui/carousel.tsx | 260 ++++++ templates/tasks/app/components/ui/chart.tsx | 375 ++++++++ .../tasks/app/components/ui/checkbox.tsx | 28 + .../tasks/app/components/ui/collapsible.tsx | 9 + templates/tasks/app/components/ui/command.tsx | 154 ++++ .../tasks/app/components/ui/context-menu.tsx | 202 +++++ templates/tasks/app/components/ui/dialog.tsx | 130 +++ templates/tasks/app/components/ui/drawer.tsx | 116 +++ .../tasks/app/components/ui/dropdown-menu.tsx | 212 +++++ templates/tasks/app/components/ui/form.tsx | 177 ++++ .../tasks/app/components/ui/hover-card.tsx | 27 + .../tasks/app/components/ui/input-otp.tsx | 69 ++ templates/tasks/app/components/ui/input.tsx | 22 + templates/tasks/app/components/ui/label.tsx | 24 + templates/tasks/app/components/ui/menubar.tsx | 234 +++++ .../app/components/ui/navigation-menu.tsx | 128 +++ .../tasks/app/components/ui/pagination.tsx | 121 +++ templates/tasks/app/components/ui/popover.tsx | 44 + .../tasks/app/components/ui/progress.tsx | 26 + .../tasks/app/components/ui/radio-group.tsx | 42 + .../tasks/app/components/ui/resizable.tsx | 43 + .../tasks/app/components/ui/scroll-area.tsx | 46 + templates/tasks/app/components/ui/select.tsx | 158 ++++ .../tasks/app/components/ui/separator.tsx | 29 + templates/tasks/app/components/ui/sheet.tsx | 160 ++++ templates/tasks/app/components/ui/sidebar.tsx | 782 ++++++++++++++++ .../tasks/app/components/ui/skeleton.tsx | 15 + templates/tasks/app/components/ui/slider.tsx | 26 + templates/tasks/app/components/ui/sonner.tsx | 58 ++ templates/tasks/app/components/ui/spinner.tsx | 17 + templates/tasks/app/components/ui/switch.tsx | 27 + templates/tasks/app/components/ui/table.tsx | 117 +++ templates/tasks/app/components/ui/tabs.tsx | 53 ++ .../tasks/app/components/ui/textarea.tsx | 23 + templates/tasks/app/components/ui/toast.tsx | 127 +++ templates/tasks/app/components/ui/toaster.tsx | 33 + .../tasks/app/components/ui/toggle-group.tsx | 59 ++ templates/tasks/app/components/ui/toggle.tsx | 43 + templates/tasks/app/components/ui/tooltip.tsx | 30 + .../tasks/app/components/ui/use-toast.ts | 3 + templates/tasks/app/entry.client.tsx | 19 + templates/tasks/app/entry.server.tsx | 10 + templates/tasks/app/global.css | 92 ++ templates/tasks/app/hooks/cache.test.ts | 32 + templates/tasks/app/hooks/cache.ts | 31 + .../tasks/app/hooks/use-custom-fields.ts | 85 ++ .../tasks/app/hooks/use-inbox-items.test.ts | 38 + templates/tasks/app/hooks/use-inbox-items.ts | 165 ++++ templates/tasks/app/hooks/use-mobile.tsx | 21 + .../tasks/app/hooks/use-navigation-state.ts | 49 ++ templates/tasks/app/hooks/use-tasks.ts | 271 ++++++ templates/tasks/app/hooks/use-toast.ts | 188 ++++ .../app/hooks/use-visible-task-fields.ts | 114 +++ templates/tasks/app/lib/app-config.ts | 11 + templates/tasks/app/lib/tab-id.ts | 1 + templates/tasks/app/lib/utils.ts | 1 + templates/tasks/app/root.tsx | 130 +++ templates/tasks/app/routes.ts | 4 + templates/tasks/app/routes/_index.tsx | 20 + .../tasks/app/routes/extensions.$id.$slug.tsx | 2 + templates/tasks/app/routes/extensions.$id.tsx | 10 + .../tasks/app/routes/extensions._index.tsx | 10 + templates/tasks/app/routes/extensions.tsx | 5 + templates/tasks/app/routes/fields.tsx | 16 + templates/tasks/app/routes/inbox.tsx | 16 + templates/tasks/app/routes/tasks.tsx | 16 + templates/tasks/app/routes/team.tsx | 16 + templates/tasks/app/vite-env.d.ts | 6 + templates/tasks/components.json | 20 + templates/tasks/data/.gitkeep | 0 templates/tasks/data/sync-config.json | 1 + templates/tasks/docs/features/README.md | 81 ++ templates/tasks/docs/features/f1-tasks.mdx | 63 ++ .../tasks/docs/features/f2-custom-fields.mdx | 68 ++ templates/tasks/docs/features/f3-projects.mdx | 33 + templates/tasks/docs/features/f4-inbox.mdx | 52 ++ templates/tasks/docs/features/f5-views.mdx | 65 ++ templates/tasks/docs/features/f6-search.mdx | 43 + .../tasks/docs/features/f7-first-run.mdx | 66 ++ templates/tasks/docs/features/f8-chat.mdx | 120 +++ templates/tasks/e2e/global-setup.ts | 41 + templates/tasks/e2e/helpers/inbox.ts | 66 ++ templates/tasks/e2e/helpers/tasks.ts | 97 ++ templates/tasks/e2e/inbox-crud.spec.ts | 85 ++ templates/tasks/e2e/tasks-bulk.spec.ts | 132 +++ templates/tasks/e2e/tasks-crud.spec.ts | 429 +++++++++ templates/tasks/e2e/tasks-navigation.spec.ts | 58 ++ templates/tasks/e2e/tasks-reorder.spec.ts | 203 +++++ templates/tasks/learnings.defaults.md | 5 + templates/tasks/netlify.toml | 10 + templates/tasks/package.json | 103 +++ templates/tasks/playwright.config.ts | 34 + .../tasks/public/agent-native-icon-dark.svg | 10 + .../tasks/public/agent-native-icon-light.svg | 10 + .../tasks/public/agent-native-logo-dark.svg | 21 + .../tasks/public/agent-native-logo-light.svg | 21 + templates/tasks/public/favicon.svg | 1 + templates/tasks/public/icon-180.svg | 1 + templates/tasks/public/icon-192.svg | 1 + templates/tasks/public/icon-512.svg | 1 + templates/tasks/public/manifest.json | 21 + templates/tasks/react-router.config.ts | 7 + .../tasks/server/custom-fields/normalize.ts | 158 ++++ templates/tasks/server/custom-fields/parse.ts | 93 ++ .../tasks/server/custom-fields/schema.ts | 122 +++ .../tasks/server/custom-fields/store.test.ts | 373 ++++++++ templates/tasks/server/custom-fields/store.ts | 313 +++++++ .../tasks/server/custom-fields/task-fields.ts | 102 +++ templates/tasks/server/custom-fields/types.ts | 98 +++ .../tasks/server/custom-fields/validate.ts | 205 +++++ .../server/custom-fields/values/store.ts | 234 +++++ templates/tasks/server/db/index.ts | 4 + templates/tasks/server/db/record-utils.ts | 7 + templates/tasks/server/db/schema.ts | 51 ++ templates/tasks/server/db/test-tasks-table.ts | 61 ++ templates/tasks/server/db/transaction.ts | 11 + templates/tasks/server/inbox/store.test.ts | 182 ++++ templates/tasks/server/inbox/store.ts | 103 +++ templates/tasks/server/middleware/auth.ts | 15 + templates/tasks/server/plugins/agent-chat.ts | 45 + templates/tasks/server/plugins/auth.ts | 17 + templates/tasks/server/plugins/db.ts | 90 ++ .../tasks/server/routes/[...page].get.ts | 5 + templates/tasks/server/stored-items/store.ts | 375 ++++++++ templates/tasks/server/tasks/store.test.ts | 195 ++++ templates/tasks/server/tasks/store.ts | 105 +++ .../tasks/server/user-config/store.test.ts | 181 ++++ templates/tasks/server/user-config/store.ts | 129 +++ templates/tasks/shared/boolean-param.test.ts | 36 + templates/tasks/shared/boolean-param.ts | 22 + .../tasks/shared/list-screen-snapshot.ts | 90 ++ templates/tasks/shared/navigation.test.ts | 35 + templates/tasks/shared/navigation.ts | 84 ++ templates/tasks/shared/visible-task-fields.ts | 2 + templates/tasks/ssr-entry.ts | 15 + templates/tasks/tsconfig.json | 24 + templates/tasks/vite.config.ts | 19 + templates/tasks/vitest.config.ts | 8 + 318 files changed, 27176 insertions(+) create mode 100644 .changeset/tasks-template.md create mode 100644 templates/tasks/.agents/commands/visual-plan.md create mode 100644 templates/tasks/.agents/commands/visual-recap.md create mode 100644 templates/tasks/.agents/skills/actions/SKILL.md create mode 100644 templates/tasks/.agents/skills/adding-a-feature/SKILL.md create mode 100644 templates/tasks/.agents/skills/agent-native-docs/SKILL.md create mode 100644 templates/tasks/.agents/skills/agent-watchdog/README.md create mode 100644 templates/tasks/.agents/skills/agent-watchdog/SKILL.md create mode 100644 templates/tasks/.agents/skills/agent-watchdog/agents/openai.yaml create mode 100644 templates/tasks/.agents/skills/capture-learnings/SKILL.md create mode 100644 templates/tasks/.agents/skills/change-summary/SKILL.md create mode 100644 templates/tasks/.agents/skills/create-skill/SKILL.md create mode 100644 templates/tasks/.agents/skills/delegate-to-agent/SKILL.md create mode 100644 templates/tasks/.agents/skills/efficient-fable/README.md create mode 100644 templates/tasks/.agents/skills/efficient-fable/SKILL.md create mode 100644 templates/tasks/.agents/skills/efficient-fable/assets/fable-orchestrator-dark.png create mode 100644 templates/tasks/.agents/skills/efficient-fable/assets/fable-orchestrator.excalidraw create mode 100644 templates/tasks/.agents/skills/efficient-fable/assets/fable-orchestrator.png create mode 100644 templates/tasks/.agents/skills/efficient-frontier/README.md create mode 100644 templates/tasks/.agents/skills/efficient-frontier/SKILL.md create mode 100644 templates/tasks/.agents/skills/frontend-design/SKILL.md create mode 100644 templates/tasks/.agents/skills/plan-arbiter/README.md create mode 100644 templates/tasks/.agents/skills/plan-arbiter/SKILL.md create mode 100644 templates/tasks/.agents/skills/plan-arbiter/agents/openai.yaml create mode 100644 templates/tasks/.agents/skills/plow-ahead/README.md create mode 100644 templates/tasks/.agents/skills/plow-ahead/SKILL.md create mode 100644 templates/tasks/.agents/skills/plow-ahead/agents/openai.yaml create mode 100644 templates/tasks/.agents/skills/quick-recap/README.md create mode 100644 templates/tasks/.agents/skills/quick-recap/SKILL.md create mode 100644 templates/tasks/.agents/skills/read-the-damn-docs/README.md create mode 100644 templates/tasks/.agents/skills/read-the-damn-docs/SKILL.md create mode 100644 templates/tasks/.agents/skills/read-the-damn-docs/agents/openai.yaml create mode 100644 templates/tasks/.agents/skills/real-time-sync/SKILL.md create mode 100644 templates/tasks/.agents/skills/security/SKILL.md create mode 100644 templates/tasks/.agents/skills/self-modifying-code/SKILL.md create mode 100644 templates/tasks/.agents/skills/shadcn-ui/SKILL.md create mode 100644 templates/tasks/.agents/skills/stay-within-limits/README.md create mode 100644 templates/tasks/.agents/skills/stay-within-limits/SKILL.md create mode 100644 templates/tasks/.agents/skills/visual-plan/SKILL.md create mode 100644 templates/tasks/.agents/skills/visual-plan/agent-native-skill.json create mode 100644 templates/tasks/.agents/skills/visual-plan/references/canvas.md create mode 100644 templates/tasks/.agents/skills/visual-plan/references/document-quality.md create mode 100644 templates/tasks/.agents/skills/visual-plan/references/exemplar.md create mode 100644 templates/tasks/.agents/skills/visual-plan/references/wireframe.md create mode 100644 templates/tasks/.agents/skills/visual-recap/SKILL.md create mode 100644 templates/tasks/.agents/skills/visual-recap/agent-native-skill.json create mode 100644 templates/tasks/.agents/skills/visual-recap/references/wireframe.md create mode 100644 templates/tasks/.claude/commands/visual-plan.md create mode 100644 templates/tasks/.claude/commands/visual-recap.md create mode 120000 templates/tasks/.claude/skills create mode 100644 templates/tasks/.env.example create mode 100644 templates/tasks/.gitignore create mode 100644 templates/tasks/.ignore create mode 100644 templates/tasks/.prettierrc create mode 100644 templates/tasks/AGENTS.md create mode 120000 templates/tasks/CLAUDE.md create mode 100644 templates/tasks/DEVELOPING.md create mode 100644 templates/tasks/README.md create mode 100644 templates/tasks/actions/bulk-delete-inbox-items.test.ts create mode 100644 templates/tasks/actions/bulk-delete-inbox-items.ts create mode 100644 templates/tasks/actions/bulk-delete-tasks.test.ts create mode 100644 templates/tasks/actions/bulk-delete-tasks.ts create mode 100644 templates/tasks/actions/bulk-update-tasks.test.ts create mode 100644 templates/tasks/actions/bulk-update-tasks.ts create mode 100644 templates/tasks/actions/create-custom-field.test.ts create mode 100644 templates/tasks/actions/create-custom-field.ts create mode 100644 templates/tasks/actions/create-inbox-item.test.ts create mode 100644 templates/tasks/actions/create-inbox-item.ts create mode 100644 templates/tasks/actions/create-task.ts create mode 100644 templates/tasks/actions/delete-custom-field.ts create mode 100644 templates/tasks/actions/delete-inbox-item.test.ts create mode 100644 templates/tasks/actions/delete-inbox-item.ts create mode 100644 templates/tasks/actions/delete-task.ts create mode 100644 templates/tasks/actions/lib/boolean-query-param.test.ts create mode 100644 templates/tasks/actions/lib/boolean-query-param.ts create mode 100644 templates/tasks/actions/list-custom-fields.ts create mode 100644 templates/tasks/actions/list-inbox-items.test.ts create mode 100644 templates/tasks/actions/list-inbox-items.ts create mode 100644 templates/tasks/actions/list-tasks.ts create mode 100644 templates/tasks/actions/list-visible-task-fields.ts create mode 100644 templates/tasks/actions/mark-inbox-item-ready.test.ts create mode 100644 templates/tasks/actions/mark-inbox-item-ready.ts create mode 100644 templates/tasks/actions/navigate.test.ts create mode 100644 templates/tasks/actions/navigate.ts create mode 100644 templates/tasks/actions/reorder-custom-fields.ts create mode 100644 templates/tasks/actions/reorder-inbox-items.test.ts create mode 100644 templates/tasks/actions/reorder-inbox-items.ts create mode 100644 templates/tasks/actions/reorder-tasks.ts create mode 100644 templates/tasks/actions/run.ts create mode 100644 templates/tasks/actions/update-custom-field.test.ts create mode 100644 templates/tasks/actions/update-custom-field.ts create mode 100644 templates/tasks/actions/update-inbox-item.test.ts create mode 100644 templates/tasks/actions/update-inbox-item.ts create mode 100644 templates/tasks/actions/update-task.test.ts create mode 100644 templates/tasks/actions/update-task.ts create mode 100644 templates/tasks/actions/update-visible-task-fields.ts create mode 100644 templates/tasks/actions/view-screen-helpers.ts create mode 100644 templates/tasks/actions/view-screen.test.ts create mode 100644 templates/tasks/actions/view-screen.ts create mode 100644 templates/tasks/app/components/custom-fields/FieldCreateBar.tsx create mode 100644 templates/tasks/app/components/custom-fields/FieldListRow.tsx create mode 100644 templates/tasks/app/components/custom-fields/FieldsList.tsx create mode 100644 templates/tasks/app/components/custom-fields/FieldsListSkeleton.tsx create mode 100644 templates/tasks/app/components/custom-fields/FieldsPage.tsx create mode 100644 templates/tasks/app/components/custom-fields/editor/FieldEditorSidebar.tsx create mode 100644 templates/tasks/app/components/custom-fields/editor/FieldTitleSection.tsx create mode 100644 templates/tasks/app/components/custom-fields/editor/config/CurrencyConfigControl.tsx create mode 100644 templates/tasks/app/components/custom-fields/editor/config/FieldConfigControl.tsx create mode 100644 templates/tasks/app/components/custom-fields/editor/config/NumberConfigControl.tsx create mode 100644 templates/tasks/app/components/custom-fields/editor/config/PercentConfigControl.tsx create mode 100644 templates/tasks/app/components/custom-fields/editor/config/SelectConfigControl.tsx create mode 100644 templates/tasks/app/components/custom-fields/editor/config/select-colors.ts create mode 100644 templates/tasks/app/components/custom-fields/editor/config/utils.ts create mode 100644 templates/tasks/app/components/custom-fields/editor/types.ts create mode 100644 templates/tasks/app/components/dnd/SortableItem.tsx create mode 100644 templates/tasks/app/components/dnd/SortableList.tsx create mode 100644 templates/tasks/app/components/dnd/reorder-moving-items.test.ts create mode 100644 templates/tasks/app/components/dnd/reorder-moving-items.ts create mode 100644 templates/tasks/app/components/dnd/sortable-drag-props.ts create mode 100644 templates/tasks/app/components/inbox/InboxList.tsx create mode 100644 templates/tasks/app/components/inbox/InboxListPage.tsx create mode 100644 templates/tasks/app/components/inbox/InboxListRow.tsx create mode 100644 templates/tasks/app/components/layout/Header.tsx create mode 100644 templates/tasks/app/components/layout/HeaderActions.tsx create mode 100644 templates/tasks/app/components/layout/Layout.tsx create mode 100644 templates/tasks/app/components/layout/Sidebar.tsx create mode 100644 templates/tasks/app/components/shared/AddListItemInput.tsx create mode 100644 templates/tasks/app/components/shared/AlertDialog.tsx create mode 100644 templates/tasks/app/components/shared/BulkDeleteDialog.tsx create mode 100644 templates/tasks/app/components/shared/ChipSelect.tsx create mode 100644 templates/tasks/app/components/shared/DeleteItemDialog.tsx create mode 100644 templates/tasks/app/components/shared/InlineEditable.tsx create mode 100644 templates/tasks/app/components/shared/ListEmptyState.tsx create mode 100644 templates/tasks/app/components/shared/ListErrorMessage.tsx create mode 100644 templates/tasks/app/components/shared/ListViewHeader.tsx create mode 100644 templates/tasks/app/components/shared/PageHeader.tsx create mode 100644 templates/tasks/app/components/shared/RowActionsMenu.tsx create mode 100644 templates/tasks/app/components/shared/SidePanel.tsx create mode 100644 templates/tasks/app/components/shared/dnd/ListRowPreview.tsx create mode 100644 templates/tasks/app/components/shared/list/List.tsx create mode 100644 templates/tasks/app/components/shared/list/ListRow.tsx create mode 100644 templates/tasks/app/components/shared/list/ListRowDragHandle.tsx create mode 100644 templates/tasks/app/components/shared/list/ListSkeletonRows.tsx create mode 100644 templates/tasks/app/components/shared/list/SortableListItemShell.tsx create mode 100644 templates/tasks/app/components/shared/list/types.ts create mode 100644 templates/tasks/app/components/shared/selection/ListSelectionBar.tsx create mode 100644 templates/tasks/app/components/shared/selection/ListSelectionHeaderToggle.tsx create mode 100644 templates/tasks/app/components/shared/selection/ListSelectionToolbar.tsx create mode 100644 templates/tasks/app/components/shared/selection/get-list-row-selection-ui.ts create mode 100644 templates/tasks/app/components/shared/selection/use-list-selection.ts create mode 100644 templates/tasks/app/components/tasks/TaskList.tsx create mode 100644 templates/tasks/app/components/tasks/TaskListHeaderRow.tsx create mode 100644 templates/tasks/app/components/tasks/TaskListPage.tsx create mode 100644 templates/tasks/app/components/tasks/TaskListRow.css create mode 100644 templates/tasks/app/components/tasks/TaskListRow.tsx create mode 100644 templates/tasks/app/components/tasks/fields/TaskFieldsSidebar.tsx create mode 100644 templates/tasks/app/components/tasks/fields/TaskTitleSection.tsx create mode 100644 templates/tasks/app/components/tasks/fields/controls/DateValueControl.tsx create mode 100644 templates/tasks/app/components/tasks/fields/controls/FieldValueControl.tsx create mode 100644 templates/tasks/app/components/tasks/fields/controls/MultiSelectValueControl.tsx create mode 100644 templates/tasks/app/components/tasks/fields/controls/NumberValueControl.tsx create mode 100644 templates/tasks/app/components/tasks/fields/controls/RichTextValueControl.tsx create mode 100644 templates/tasks/app/components/tasks/fields/controls/SingleSelectValueControl.tsx create mode 100644 templates/tasks/app/components/tasks/fields/controls/TextValueControl.tsx create mode 100644 templates/tasks/app/components/tasks/use-task-row-completion-animation.ts create mode 100644 templates/tasks/app/components/ui/accordion.tsx create mode 100644 templates/tasks/app/components/ui/alert-dialog.tsx create mode 100644 templates/tasks/app/components/ui/alert.tsx create mode 100644 templates/tasks/app/components/ui/aspect-ratio.tsx create mode 100644 templates/tasks/app/components/ui/avatar.tsx create mode 100644 templates/tasks/app/components/ui/badge.tsx create mode 100644 templates/tasks/app/components/ui/breadcrumb.tsx create mode 100644 templates/tasks/app/components/ui/button.tsx create mode 100644 templates/tasks/app/components/ui/calendar.tsx create mode 100644 templates/tasks/app/components/ui/card.tsx create mode 100644 templates/tasks/app/components/ui/carousel.tsx create mode 100644 templates/tasks/app/components/ui/chart.tsx create mode 100644 templates/tasks/app/components/ui/checkbox.tsx create mode 100644 templates/tasks/app/components/ui/collapsible.tsx create mode 100644 templates/tasks/app/components/ui/command.tsx create mode 100644 templates/tasks/app/components/ui/context-menu.tsx create mode 100644 templates/tasks/app/components/ui/dialog.tsx create mode 100644 templates/tasks/app/components/ui/drawer.tsx create mode 100644 templates/tasks/app/components/ui/dropdown-menu.tsx create mode 100644 templates/tasks/app/components/ui/form.tsx create mode 100644 templates/tasks/app/components/ui/hover-card.tsx create mode 100644 templates/tasks/app/components/ui/input-otp.tsx create mode 100644 templates/tasks/app/components/ui/input.tsx create mode 100644 templates/tasks/app/components/ui/label.tsx create mode 100644 templates/tasks/app/components/ui/menubar.tsx create mode 100644 templates/tasks/app/components/ui/navigation-menu.tsx create mode 100644 templates/tasks/app/components/ui/pagination.tsx create mode 100644 templates/tasks/app/components/ui/popover.tsx create mode 100644 templates/tasks/app/components/ui/progress.tsx create mode 100644 templates/tasks/app/components/ui/radio-group.tsx create mode 100644 templates/tasks/app/components/ui/resizable.tsx create mode 100644 templates/tasks/app/components/ui/scroll-area.tsx create mode 100644 templates/tasks/app/components/ui/select.tsx create mode 100644 templates/tasks/app/components/ui/separator.tsx create mode 100644 templates/tasks/app/components/ui/sheet.tsx create mode 100644 templates/tasks/app/components/ui/sidebar.tsx create mode 100644 templates/tasks/app/components/ui/skeleton.tsx create mode 100644 templates/tasks/app/components/ui/slider.tsx create mode 100644 templates/tasks/app/components/ui/sonner.tsx create mode 100644 templates/tasks/app/components/ui/spinner.tsx create mode 100644 templates/tasks/app/components/ui/switch.tsx create mode 100644 templates/tasks/app/components/ui/table.tsx create mode 100644 templates/tasks/app/components/ui/tabs.tsx create mode 100644 templates/tasks/app/components/ui/textarea.tsx create mode 100644 templates/tasks/app/components/ui/toast.tsx create mode 100644 templates/tasks/app/components/ui/toaster.tsx create mode 100644 templates/tasks/app/components/ui/toggle-group.tsx create mode 100644 templates/tasks/app/components/ui/toggle.tsx create mode 100644 templates/tasks/app/components/ui/tooltip.tsx create mode 100644 templates/tasks/app/components/ui/use-toast.ts create mode 100644 templates/tasks/app/entry.client.tsx create mode 100644 templates/tasks/app/entry.server.tsx create mode 100644 templates/tasks/app/global.css create mode 100644 templates/tasks/app/hooks/cache.test.ts create mode 100644 templates/tasks/app/hooks/cache.ts create mode 100644 templates/tasks/app/hooks/use-custom-fields.ts create mode 100644 templates/tasks/app/hooks/use-inbox-items.test.ts create mode 100644 templates/tasks/app/hooks/use-inbox-items.ts create mode 100644 templates/tasks/app/hooks/use-mobile.tsx create mode 100644 templates/tasks/app/hooks/use-navigation-state.ts create mode 100644 templates/tasks/app/hooks/use-tasks.ts create mode 100644 templates/tasks/app/hooks/use-toast.ts create mode 100644 templates/tasks/app/hooks/use-visible-task-fields.ts create mode 100644 templates/tasks/app/lib/app-config.ts create mode 100644 templates/tasks/app/lib/tab-id.ts create mode 100644 templates/tasks/app/lib/utils.ts create mode 100644 templates/tasks/app/root.tsx create mode 100644 templates/tasks/app/routes.ts create mode 100644 templates/tasks/app/routes/_index.tsx create mode 100644 templates/tasks/app/routes/extensions.$id.$slug.tsx create mode 100644 templates/tasks/app/routes/extensions.$id.tsx create mode 100644 templates/tasks/app/routes/extensions._index.tsx create mode 100644 templates/tasks/app/routes/extensions.tsx create mode 100644 templates/tasks/app/routes/fields.tsx create mode 100644 templates/tasks/app/routes/inbox.tsx create mode 100644 templates/tasks/app/routes/tasks.tsx create mode 100644 templates/tasks/app/routes/team.tsx create mode 100644 templates/tasks/app/vite-env.d.ts create mode 100644 templates/tasks/components.json create mode 100644 templates/tasks/data/.gitkeep create mode 100644 templates/tasks/data/sync-config.json create mode 100644 templates/tasks/docs/features/README.md create mode 100644 templates/tasks/docs/features/f1-tasks.mdx create mode 100644 templates/tasks/docs/features/f2-custom-fields.mdx create mode 100644 templates/tasks/docs/features/f3-projects.mdx create mode 100644 templates/tasks/docs/features/f4-inbox.mdx create mode 100644 templates/tasks/docs/features/f5-views.mdx create mode 100644 templates/tasks/docs/features/f6-search.mdx create mode 100644 templates/tasks/docs/features/f7-first-run.mdx create mode 100644 templates/tasks/docs/features/f8-chat.mdx create mode 100644 templates/tasks/e2e/global-setup.ts create mode 100644 templates/tasks/e2e/helpers/inbox.ts create mode 100644 templates/tasks/e2e/helpers/tasks.ts create mode 100644 templates/tasks/e2e/inbox-crud.spec.ts create mode 100644 templates/tasks/e2e/tasks-bulk.spec.ts create mode 100644 templates/tasks/e2e/tasks-crud.spec.ts create mode 100644 templates/tasks/e2e/tasks-navigation.spec.ts create mode 100644 templates/tasks/e2e/tasks-reorder.spec.ts create mode 100644 templates/tasks/learnings.defaults.md create mode 100644 templates/tasks/netlify.toml create mode 100644 templates/tasks/package.json create mode 100644 templates/tasks/playwright.config.ts create mode 100644 templates/tasks/public/agent-native-icon-dark.svg create mode 100644 templates/tasks/public/agent-native-icon-light.svg create mode 100644 templates/tasks/public/agent-native-logo-dark.svg create mode 100644 templates/tasks/public/agent-native-logo-light.svg create mode 100644 templates/tasks/public/favicon.svg create mode 100644 templates/tasks/public/icon-180.svg create mode 100644 templates/tasks/public/icon-192.svg create mode 100644 templates/tasks/public/icon-512.svg create mode 100644 templates/tasks/public/manifest.json create mode 100644 templates/tasks/react-router.config.ts create mode 100644 templates/tasks/server/custom-fields/normalize.ts create mode 100644 templates/tasks/server/custom-fields/parse.ts create mode 100644 templates/tasks/server/custom-fields/schema.ts create mode 100644 templates/tasks/server/custom-fields/store.test.ts create mode 100644 templates/tasks/server/custom-fields/store.ts create mode 100644 templates/tasks/server/custom-fields/task-fields.ts create mode 100644 templates/tasks/server/custom-fields/types.ts create mode 100644 templates/tasks/server/custom-fields/validate.ts create mode 100644 templates/tasks/server/custom-fields/values/store.ts create mode 100644 templates/tasks/server/db/index.ts create mode 100644 templates/tasks/server/db/record-utils.ts create mode 100644 templates/tasks/server/db/schema.ts create mode 100644 templates/tasks/server/db/test-tasks-table.ts create mode 100644 templates/tasks/server/db/transaction.ts create mode 100644 templates/tasks/server/inbox/store.test.ts create mode 100644 templates/tasks/server/inbox/store.ts create mode 100644 templates/tasks/server/middleware/auth.ts create mode 100644 templates/tasks/server/plugins/agent-chat.ts create mode 100644 templates/tasks/server/plugins/auth.ts create mode 100644 templates/tasks/server/plugins/db.ts create mode 100644 templates/tasks/server/routes/[...page].get.ts create mode 100644 templates/tasks/server/stored-items/store.ts create mode 100644 templates/tasks/server/tasks/store.test.ts create mode 100644 templates/tasks/server/tasks/store.ts create mode 100644 templates/tasks/server/user-config/store.test.ts create mode 100644 templates/tasks/server/user-config/store.ts create mode 100644 templates/tasks/shared/boolean-param.test.ts create mode 100644 templates/tasks/shared/boolean-param.ts create mode 100644 templates/tasks/shared/list-screen-snapshot.ts create mode 100644 templates/tasks/shared/navigation.test.ts create mode 100644 templates/tasks/shared/navigation.ts create mode 100644 templates/tasks/shared/visible-task-fields.ts create mode 100644 templates/tasks/ssr-entry.ts create mode 100644 templates/tasks/tsconfig.json create mode 100644 templates/tasks/vite.config.ts create mode 100644 templates/tasks/vitest.config.ts diff --git a/.changeset/tasks-template.md b/.changeset/tasks-template.md new file mode 100644 index 0000000000..eb4ea6c5f9 --- /dev/null +++ b/.changeset/tasks-template.md @@ -0,0 +1,5 @@ +--- +"@agent-native/core": minor +--- + +Add the Tasks first-party template to the CLI template catalog. diff --git a/packages/core/src/cli/templates-meta.ts b/packages/core/src/cli/templates-meta.ts index 5e88172b74..f17b6cf304 100644 --- a/packages/core/src/cli/templates-meta.ts +++ b/packages/core/src/cli/templates-meta.ts @@ -214,6 +214,19 @@ export const TEMPLATES: TemplateMeta[] = [ requiredPackages: ["embedding"], core: true, }, + { + name: "tasks", + label: "Tasks", + hint: "Task-list-first workspace — inbox capture, custom fields, and drag-and-drop ordering", + icon: "ListCheck", + color: "#6366F1", + colorRgb: "99 102 241", + devPort: 8091, + prodUrl: "https://tasks.agent-native.com", + defaultMode: "prod", + hidden: true, + core: false, + }, { name: "macros", label: "Macros", diff --git a/packages/core/src/client/org/OrgSwitcher.tsx b/packages/core/src/client/org/OrgSwitcher.tsx index c33356fe2c..71caa9a9a2 100644 --- a/packages/core/src/client/org/OrgSwitcher.tsx +++ b/packages/core/src/client/org/OrgSwitcher.tsx @@ -14,6 +14,7 @@ import { IconCode, IconFileText, IconLayoutBoard, + IconListCheck, IconLoader2, IconLogout, IconMail, @@ -122,6 +123,7 @@ const APP_ICON_MAP: Record = { CalendarTime: IconCalendarTime, Globe: IconWorld, Photo: IconPhoto, + ListCheck: IconListCheck, }; function appMenuIcon(app: OrgSwitcherAppLink): typeof IconApps { diff --git a/packages/desktop-app/src/renderer/components/Sidebar.tsx b/packages/desktop-app/src/renderer/components/Sidebar.tsx index e74396145a..1dac46e7b3 100644 --- a/packages/desktop-app/src/renderer/components/Sidebar.tsx +++ b/packages/desktop-app/src/renderer/components/Sidebar.tsx @@ -24,6 +24,7 @@ import { IconRoute, IconWorld, IconPhoto, + IconListCheck, } from "@tabler/icons-react"; import { UpdateIndicator } from "./UpdateIndicator.js"; @@ -56,6 +57,7 @@ const ICON_MAP: Record>> = { CalendarTime: IconCalendarTime, Globe: IconWorld, Photo: IconPhoto, + ListCheck: IconListCheck, }; interface SidebarProps { diff --git a/packages/shared-app-config/templates.ts b/packages/shared-app-config/templates.ts index 639f137bf3..a944dbe98f 100644 --- a/packages/shared-app-config/templates.ts +++ b/packages/shared-app-config/templates.ts @@ -217,6 +217,19 @@ export const TEMPLATES: TemplateMeta[] = [ alwaysAvailable: true, core: true, }, + { + name: "tasks", + label: "Tasks", + hint: "Task-list-first workspace — inbox capture, custom fields, and drag-and-drop ordering", + icon: "ListCheck", + color: "#6366F1", + colorRgb: "99 102 241", + devPort: 8091, + prodUrl: "https://tasks.agent-native.com", + defaultMode: "prod", + hidden: true, + core: false, + }, { name: "macros", label: "Macros", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1e94466f62..28e7979524 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4703,6 +4703,244 @@ importers: specifier: 'catalog:' version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@24.13.2)(happy-dom@20.10.6)(jsdom@29.1.1(@noble/hashes@2.2.0)(canvas@3.2.3))(vite@8.1.0(@types/node@24.13.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + templates/tasks: + dependencies: + '@agent-native/core': + specifier: workspace:* + version: link:../../packages/core + '@dnd-kit/core': + specifier: ^6.3.1 + version: 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@dnd-kit/sortable': + specifier: ^10.0.0 + version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) + '@dnd-kit/utilities': + specifier: ^3.2.2 + version: 3.2.2(react@19.2.7) + '@fontsource-variable/inter': + specifier: ^5.2.8 + version: 5.2.8 + '@libsql/client': + specifier: ^0.15.8 + version: 0.15.15 + '@tabler/icons-react': + specifier: 'catalog:' + version: 3.44.0(react@19.2.7) + h3: + specifier: ^2.0.1-rc.20 + version: 2.0.1-rc.22(crossws@0.4.6(srvx@0.11.17)) + isbot: + specifier: ^5 + version: 5.1.44 + node-pty: + specifier: ^1.1.0 + version: 1.1.0 + postgres: + specifier: ^3.4.9 + version: 3.4.9 + zod: + specifier: ^4.4.3 + version: 4.4.3 + devDependencies: + '@playwright/test': + specifier: ^1.61.0 + version: 1.61.1 + '@radix-ui/react-accordion': + specifier: ^1.2.12 + version: 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-alert-dialog': + specifier: ^1.1.15 + version: 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-aspect-ratio': + specifier: ^1.1.8 + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-avatar': + specifier: ^1.1.11 + version: 1.2.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-checkbox': + specifier: ^1.3.2 + version: 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collapsible': + specifier: ^1.1.12 + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context-menu': + specifier: ^2.2.16 + version: 2.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dialog': + specifier: ^1.1.14 + version: 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dropdown-menu': + specifier: ^2.1.15 + version: 2.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-hover-card': + specifier: ^1.1.15 + version: 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-label': + specifier: ^2.1.7 + version: 2.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-menubar': + specifier: ^1.1.16 + version: 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-navigation-menu': + specifier: ^1.2.14 + version: 1.2.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popover': + specifier: ^1.1.14 + version: 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-progress': + specifier: ^1.1.8 + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-radio-group': + specifier: ^1.3.7 + version: 1.4.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-scroll-area': + specifier: ^1.2.9 + version: 1.2.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-select': + specifier: ^2.2.5 + version: 2.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-separator': + specifier: ^1.1.7 + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slider': + specifier: ^1.3.5 + version: 1.4.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': + specifier: ^1.2.3 + version: 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-switch': + specifier: ^1.2.5 + version: 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tabs': + specifier: ^1.1.12 + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toast': + specifier: ^1.2.15 + version: 1.2.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle': + specifier: ^1.1.10 + version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle-group': + specifier: ^1.1.11 + version: 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tooltip': + specifier: ^1.2.7 + version: 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@react-router/dev': + specifier: ^8.1.0 + version: 8.1.0(@typescript/typescript6@6.0.2)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@8.1.0(@types/node@24.13.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(wrangler@4.81.0) + '@react-router/fs-routes': + specifier: ^8.1.0 + version: 8.1.0(@react-router/dev@8.1.0(@typescript/typescript6@6.0.2)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(vite@8.1.0(@types/node@24.13.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(wrangler@4.81.0))(@typescript/typescript6@6.0.2) + '@swc/core': + specifier: ^1.13.3 + version: 1.15.43 + '@tailwindcss/vite': + specifier: 'catalog:' + version: 4.3.1(vite@8.1.0(@types/node@24.13.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@tanstack/react-query': + specifier: ^5.99.2 + version: 5.101.1(react@19.2.7) + '@types/better-sqlite3': + specifier: ^7.6.13 + version: 7.6.13 + '@types/node': + specifier: ^24.2.1 + version: 24.13.2 + '@types/react': + specifier: ^19.2.14 + version: 19.2.17 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.17) + '@vitejs/plugin-react-swc': + specifier: ^4.0.0 + version: 4.3.1(vite@8.1.0(@types/node@24.13.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@xterm/addon-fit': + specifier: ^0.11.0 + version: 0.11.0 + '@xterm/addon-web-links': + specifier: ^0.12.0 + version: 0.12.0 + '@xterm/xterm': + specifier: ^6.0.0 + version: 6.0.0 + better-sqlite3: + specifier: ^12.11.1 + version: 12.11.1 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + cmdk: + specifier: ^1.1.1 + version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + drizzle-orm: + specifier: ^0.45.2 + version: 0.45.2(@libsql/client@0.15.15)(@neondatabase/serverless@1.1.0)(@opentelemetry/api@1.9.1)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.11.1)(kysely@0.28.17)(postgres@3.4.9) + embla-carousel-react: + specifier: ^8.6.0 + version: 8.6.0(react@19.2.7) + input-otp: + specifier: ^1.4.2 + version: 1.4.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next-themes: + specifier: ^0.4.6 + version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + oxfmt: + specifier: 'catalog:' + version: 0.56.0 + react: + specifier: 19.2.7 + version: 19.2.7 + react-day-picker: + specifier: ^9.14.0 + version: 9.14.0(react@19.2.7) + react-dom: + specifier: 19.2.7 + version: 19.2.7(react@19.2.7) + react-hook-form: + specifier: ^7.71.2 + version: 7.80.0(react@19.2.7) + react-resizable-panels: + specifier: ^4.10.0 + version: 4.12.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: + specifier: ^8.1.0 + version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + recharts: + specifier: ^3.8.1 + version: 3.9.2(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react-is@19.2.7)(react@19.2.7)(redux@5.0.1) + sonner: + specifier: ^2.0.7 + version: 2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + tailwind-merge: + specifier: ^3.5.0 + version: 3.6.0 + tailwindcss: + specifier: 'catalog:' + version: 4.3.1 + tsx: + specifier: ^4.20.3 + version: 4.21.0 + typescript: + specifier: 'catalog:' + version: '@typescript/typescript6@6.0.2' + typescript-7: + specifier: 'catalog:' + version: typescript@7.0.2 + vaul: + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + vite: + specifier: 'catalog:' + version: 8.1.0(@types/node@24.13.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vitest: + specifier: 'catalog:' + version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@24.13.2)(happy-dom@20.10.6)(jsdom@29.1.1(@noble/hashes@2.2.0)(canvas@3.2.3))(vite@8.1.0(@types/node@24.13.2)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + packages: '@ai-sdk/anthropic@3.0.86': diff --git a/templates/tasks/.agents/commands/visual-plan.md b/templates/tasks/.agents/commands/visual-plan.md new file mode 100644 index 0000000000..69f336b9f9 --- /dev/null +++ b/templates/tasks/.agents/commands/visual-plan.md @@ -0,0 +1,9 @@ +--- +description: Create an interactive Agent-Native visual plan for the current task. +argument-hint: [optional request or scope] +--- + +Use the visual-plan skill for this task. Treat any arguments as the user's +requested plan scope or focus: + +$ARGUMENTS diff --git a/templates/tasks/.agents/commands/visual-recap.md b/templates/tasks/.agents/commands/visual-recap.md new file mode 100644 index 0000000000..77eac7038e --- /dev/null +++ b/templates/tasks/.agents/commands/visual-recap.md @@ -0,0 +1,8 @@ +--- +description: Create an interactive Agent-Native visual recap for a PR, branch, commit, or diff. +argument-hint: [PR, branch, commit, diff, or scope] +--- + +Use the visual-recap skill. Treat any arguments as the recap target or focus: + +$ARGUMENTS diff --git a/templates/tasks/.agents/skills/actions/SKILL.md b/templates/tasks/.agents/skills/actions/SKILL.md new file mode 100644 index 0000000000..1e30ecadd8 --- /dev/null +++ b/templates/tasks/.agents/skills/actions/SKILL.md @@ -0,0 +1,435 @@ +--- +name: actions +description: >- + How to create and run agent actions. Actions are the single source of truth + for app operations — the agent calls them as tools and frontend code calls + them through client hooks. Use when creating a new action, adding an API + integration, or wiring up frontend data fetching. +metadata: + internal: true +--- + +# Agent Actions + +## Rule + +Actions in `actions/` are the **single source of truth** for app operations. The agent calls them as tools, and the frontend calls them through `useActionQuery` / `useActionMutation`. The framework owns the HTTP transport behind those hooks. No duplicate `/api/` routes needed. + +Before creating any custom REST/API route for app data, inspect `actions/` and the action table in `AGENTS.md`. If an action already exists, call it directly from the agent or with `useActionQuery` / `useActionMutation` from the UI. If the capability is missing, create or update a `defineAction`. Do not add `/api/*`, `server/routes/*`, or other pass-through endpoints whose main job is to call, repackage, or re-export an action. + +## Why + +Actions give the agent callable tools with structured input/output, AND they give the frontend a typed client contract through hooks. One implementation serves both the agent and the UI. They keep the agent's chat context clean, they're reusable, and they can be tested independently. + +## Keep the Action Surface Small and Orthogonal + +Every agent-exposed action is a tool in the model's context window. There is a real cost to each one: more tools means more for the model to read, disambiguate, and choose between, which degrades tool-selection quality. Treat the action list like an API you have to maintain — add the fewest, most orthogonal actions that cover the capability, not one per UI affordance. + +- **Prefer one CRUD-style `update` over N per-field actions.** A single `update-` that takes a patch of optional fields beats `update--name`, `update--order`, `update--color`, … The agent (and the UI) pass only the fields that change. Same for `create`/`delete` — one orthogonal action per resource, not one per code path. +- **Reach for a generic query / escape hatch before minting a new read action.** If the agent needs more or different data, do not add `get--by-x`, `list--filtered-by-y`, etc. For provider data, expose the shared `provider-api-catalog` / `provider-api-docs` / `provider-api-request` trio (see `templates/dispatch/actions/`) so the agent can hit any endpoint or filter without a new action each time. For app data in dev, the `db-query` tool already answers arbitrary read questions. +- **Hide UI-only or purely programmatic actions from the model with `agentTool: false`.** An action that only the frontend or an HTTP/cron caller needs should not spend a slot in the model's tool list. `agentTool: false` keeps it callable from `useActionMutation` / `callAction` / `/_agent-native/actions/` while removing it from every agent tool surface (in-app assistant, MCP, A2A). +- **`agentTool: false` is NOT `toolCallable: false`.** They are different switches: + - `agentTool: false` → hidden from the **model entirely** (it is no longer a tool the agent can see or call). Still frontend/HTTP-callable. + - `toolCallable: false` → only blocks the **sandboxed extension ("tools") iframe bridge** (`appAction(...)`). The action stays fully visible to the model, the UI, the CLI, MCP, and A2A. Use it for high-blast-radius operations (account/org/auth changes), not for trimming the tool list. +- **Remove or hide stale actions.** When the UI stops using an action, delete it or set `agentTool: false` — do not leave it exposed to the model as dead tool weight. The advisory audit below helps you spot these. + +### Audit Script (Advisory) + +`pnpm actions:audit [template ...]` (or `node scripts/audit-template-actions.mjs`) statically scans a template's `actions/` and prints two kinds of suggestions: + +1. **Likely UI-dead** — HTTP-exposed mutating actions whose name is never referenced under `app/` (candidates to delete or mark `agentTool: false`). +2. **Likely redundant clusters** — groups like `update-foo-name` / `update-foo-order` that could collapse into one orthogonal `update-foo`. + +It is **advisory only**: it always exits 0, never fails CI, and uses conservative heuristics, so expect some false positives (e.g. an action the agent calls but the UI doesn't). Use it as a prompt to review, not a gate. + +## How to Create an Action + +Use `defineAction` with a Zod schema (required for new actions): + +```ts +// actions/list-meals.ts +import { z } from "zod"; +import { defineAction } from "@agent-native/core/action"; +import { getDb } from "../server/db/index.js"; +import { meals } from "../server/db/schema.js"; + +export default defineAction({ + description: "List all meals", + schema: z.object({ + date: z.string().describe("Filter by date (YYYY-MM-DD)"), + }), + http: { method: "GET" }, + run: async (args) => { + // args is fully typed: { date: string } + const db = getDb(); + const rows = await db.select().from(meals); + return rows; // Return objects/arrays, NOT JSON.stringify() + }, +}); +``` + +The `schema` field accepts a Zod schema (or any Standard Schema-compatible library). It provides runtime validation with clear error messages (400 for HTTP, error result for agent), full TypeScript type inference for `run()` args, and auto-generated JSON Schema for the agent's tool definition. `zod` is a dependency of all templates. + +When an action reads or writes app data, use Drizzle's query builder and portable operators from `drizzle-orm`. Do not use raw SQL, `getDbExec()`, or dialect-specific schema imports in normal actions unless there is a documented reason Drizzle cannot express the query. + +When an action calls an external service, never hardcode API keys, bearer +tokens, webhook URLs, signing secrets, OAuth refresh tokens, private +Builder/internal data, or customer data. Read user/org/workspace credentials +from `readAppSecret`, `resolveCredential`, OAuth token helpers, or the provider +API credential adapter. Use `process.env` only for explicitly deploy-level +configuration, and keep examples to obvious placeholders. + +Tips: +- Use `.describe()` for parameter descriptions +- Use `.optional()` for optional params +- Use `z.coerce.number()` for numeric params that arrive as strings from HTTP. + For booleans, use an explicit string parser/helper instead of + `z.coerce.boolean()` because JavaScript treats any non-empty string, + including `"false"`, as truthy. +- Use `z.enum(["draft", "published"])` for constrained values + +The legacy `parameters` field (plain JSON Schema object) still works as a fallback but does not provide runtime validation or type inference. + +## Decision Order + +When you need app data or a mutation: + +1. **Use an existing action** if one already performs the operation. +2. **Create or extend a `defineAction`** when the agent and UI both need a new operation. +3. **Create a custom route only for route-only concerns** such as uploads, streaming, webhooks, OAuth callbacks, or a non-JSON protocol. + +Do not build an umbrella REST API to make actions "easier" to call. Actions are already callable by agents, CLIs, React hooks, HTTP, MCP/A2A exposure, and external hosts through the framework. + +## Flexible Provider APIs + +For provider integrations used in ad hoc analysis, querying, reporting, or +cross-source research, do not hardcode every provider endpoint as a separate +rigid action and do not encode one lookback window, filter shape, or pagination +strategy as the only path the agent can take. Expose the shared provider API +action trio instead: + +- `provider-api-catalog`: lists provider base URLs, auth style, credential keys, + docs/spec URLs, placeholders, and examples without exposing secrets. +- `provider-api-docs`: fetches public provider docs/spec/changelog URLs when + the exact endpoint, filter operator, payload shape, or pagination contract is + uncertain. Registered docs URLs are curated starting points. Use + `responseMode: "markdown"` for clean readable docs, or + `responseMode: "matches"` with `search: { query | terms | regex }` for + compact snippets instead of flooding context with raw HTML. +- `provider-api-request`: makes a constrained authenticated HTTP request to the + provider host, injects configured credentials, blocks private/internal URLs, + and redacts secrets. + +Use `@agent-native/core/provider-api` as the shared substrate. A template should +only add a thin credential adapter when it has app-specific credential lookup +rules. If the app stores a built-in provider's OAuth grant under a narrower +local provider id, use the runtime's `oauthProviderOverrides` instead of +duplicating the provider config. If credentials are stored on shareable/resource +rows rather than in the shared credential or OAuth-token stores, build a resolver +that enforces those access checks before exposing raw provider requests. Keep +`provider-api-request` `http: false` unless you have a separate UI permission +model for arbitrary provider writes. Specific actions such as `search-records`, +`search-emails`, or `sync-source` are convenience shortcuts, not capability +limits; agents should fall back to the provider API trio when a question +requires an endpoint or filter that the shortcut does not model. + +This is a framework tenet. The safety boundary should be provider host +allow-listing, credential scoping, auth injection, private-network blocking, +secret redaction, and user/org access checks, not an artificially small set of +hand-authored read actions. If the upstream provider API supports a capability, +the agent should normally be able to reach it through `provider-api-request` +with the user's configured credentials. For large responses, expose staging +(`stageAs`, `itemsPath`, pagination, and `query-staged-dataset`) or sandboxed +code execution so the agent can reduce data without flooding context. + +For broad provider questions, cross-source joins, corpus-wide mention/search +work, classification, or any answer where absence matters, design the action +surface for full coverage instead of convenience-only samples. The agent should +be able to fetch every relevant page or an explicitly bounded cohort, stage or +save the raw provider response outside chat, and then use +`query-staged-dataset`, `run-code`, or provider-side search to count, join, +grep, classify, and aggregate. Tool descriptions and AGENTS.md guidance should +teach agents to report source, filters, time window, row/record counts, +pagination status, truncation, failed pages, and uncovered gaps. They must not +turn default limits, sampled rows, truncated excerpts, or aborted calls into a +confident "none found", "all records", or exhaustive conclusion. + +For public web pages and docs, prefer the token-efficient path: `web-search` +to find likely URLs, `web-request` or `provider-api-docs` with clean +`responseMode` output to read a page, and `run-code` with `webRead()` / +`webFetch()` when you need to grep, aggregate, or compare many pages before +returning a small result. + +### The `http` Option + +Controls how the action is exposed as an HTTP endpoint: + +| Value | Behavior | Use for | +| ------------------------- | ----------------------------------------------------------- | -------------------------------- | +| _(omitted)_ | Auto-exposed as `POST /_agent-native/actions/:name` | Write operations (default) | +| `{ method: "GET" }` | Auto-exposed as `GET /_agent-native/actions/:name` | Read-only queries | +| `{ method: "PUT" }` | Auto-exposed as `PUT /_agent-native/actions/:name` | Update operations | +| `{ method: "DELETE" }` | Auto-exposed as `DELETE /_agent-native/actions/:name` | Delete operations | +| `{ method: "GET", path: "custom" }` | Auto-exposed as `GET /_agent-native/actions/custom` | Custom route path | +| `false` | Agent-only, never exposed as HTTP | `navigate`, `view-screen`, internal actions | + +### Screen Refresh (automatic) + +The framework auto-refreshes the UI after any successful mutating action. On completion of a non-`GET` action, the framework emits a change event with `source: "action"` that the client's `useDbSync` picks up and uses to invalidate `["action"]` React Query keys — so `list-*` / `get-*` hooks refetch without a full page reload. In-process calls emit directly; dev-mode `pnpm action ...` calls also write a durable marker so the web server sees child-process action changes. + +Rules: + +- `http: { method: "GET" }` → read-only, does NOT trigger a refresh (inferred automatically). +- Any other action (default `POST`, `PUT`, `DELETE`, or `http: false`) → treated as mutating, triggers a refresh on success. +- To override the inference on an unusual action (e.g. a `POST` that only reads), pass `readOnly: true` on the action definition. +- To let a mutating action run concurrently with other same-turn tool calls, pass `parallelSafe: true`. Only do this when the action is internally concurrency-safe and order-independent (for example, it uses an app-level lock or idempotent upsert semantics). Mutating actions remain serialized by default. + +Agents do NOT need to call `refresh-screen` after a normal action — it's already handled. `refresh-screen` is only needed when the agent mutates data via a path the framework can't see (e.g. writing to an external system the app mirrors) or when the agent wants to pass a `scope` hint for narrower invalidation. + +### Return Values + +Actions should return **structured data** (objects, arrays) — not `JSON.stringify()`. The framework serializes the response automatically. If you return a string, the framework tries to parse it as JSON for a clean response. + +```ts +// Good — return structured data +run: async (args) => { + const events = await fetchEvents(args.from, args.to); + return events; +} + +// Bad — don't stringify +run: async (args) => { + const events = await fetchEvents(args.from, args.to); + return JSON.stringify(events, null, 2); +} +``` + +### Validating Return Values (`outputSchema`) + +`schema` validates inputs; `outputSchema` validates what the action **returns**. Pass any Standard Schema-compatible schema (Zod, Valibot, ArkType) and the framework validates the result _after_ `run()` resolves — input validated before `run`, output after. + +```ts +export default defineAction({ + description: "Summarize a thread.", + schema: z.object({ threadId: z.string() }), + outputSchema: z.object({ summary: z.string(), messageCount: z.number() }), + outputErrorStrategy: "warn", // default; "strict" | "fallback" + // outputFallback: { summary: "", messageCount: 0 }, // used only by "fallback" + run: async ({ threadId }) => { + /* ... */ + }, +}); +``` + +- `"warn"` (default) — `console.warn` the issues and return the **original** result unchanged. Non-breaking. +- `"strict"` — throw a clear error so a buggy action surfaces loudly. +- `"fallback"` — return `outputFallback` in place of the invalid result. + +On success the validated value is returned, so coercion/defaults on `outputSchema` apply. Omit `outputSchema` and behavior is byte-for-byte unchanged (no wrapping). + +### Human-in-the-Loop Approval (`needsApproval`) + +For high-consequence, outward-facing, hard-to-undo actions (sending an email, charging a card, deleting an account), set `needsApproval` so the agent **cannot** run the action without a human approving the specific call: + +```ts +export default defineAction({ + description: "Send an email via Gmail.", + schema: z.object({ to: z.string(), subject: z.string(), body: z.string() }), + needsApproval: true, // boolean, or (args, ctx) => boolean | Promise + run: async (args) => { + /* ...actually send... */ + }, +}); +``` + +When the gate is truthy and the call isn't yet approved, the loop emits an `approval_required` event and **stops the turn — `run()` never executes**. A predicate gates conditionally (e.g. only external recipients) and **fails closed**: a throw is treated as "approval required". The human approves via the chat UI's Approve affordance, which re-issues the turn with the call's `approvalKey`, and only then does the action run. + +**Keep approvals rare** — the default is off and almost every action should leave it off. The canonical example is Mail's `send-email` (`needsApproval: true`). See the `security` skill and the Human Approval doc. + +## Frontend Hooks + +The frontend calls actions using React Query hooks from `@agent-native/core/client`. Components should not hand-write `fetch("/_agent-native/actions/...")`; add or reuse a client hook/helper instead. Use `callAction` from the same package for imperative cases that do not fit a hook, such as debounced search, prefetching, or non-React event handlers. + +### `useActionQuery` — for GET actions + +```ts +import { useActionQuery } from "@agent-native/core/client"; + +function MealList() { + // Types are auto-inferred from the action's schema + return type — no manual generic needed + const { data: meals } = useActionQuery("list-meals", { + date: "2025-01-01", + }); + return
    {meals?.map((m) =>
  • {m.name}
  • )}
; +} +``` + +### `useActionMutation` — for POST/PUT/DELETE actions + +```ts +import { useActionMutation } from "@agent-native/core/client"; + +function AddMealButton() { + // Types are auto-inferred — no manual generic needed + const { mutate } = useActionMutation("log-meal"); + return ( + + ); +} +``` + +**Do NOT use manual type generics** like `useActionQuery(...)`. Types are inferred automatically from `.generated/action-types.d.ts`, which is auto-generated by a Vite plugin. + +Mutations automatically invalidate all `["action"]` query keys on success, so GET queries refetch. + +### `callAction` — for imperative client code + +```ts +import { callAction } from "@agent-native/core/client"; + +const people = await callAction("search-people", { query }, { method: "GET" }); +``` + +Prefer hooks in React data flows. Use `callAction` when a hook would be awkward; +do not hand-write action route fetches in components. + +## How to Run (Agent) + +```bash +pnpm action my-action --input data/source.json --output data/result.json +``` + +## Action Dispatcher + +The default template uses core's `runScript()` in `actions/run.ts`: + +```ts +import { runScript } from "@agent-native/core"; +runScript(); +``` + +This is the canonical approach for new apps. Action names must be lowercase with hyphens only (e.g., `my-action`). + +## When You Still Need Custom `/api/` Routes + +Most operations should be actions. You only need custom routes in `server/routes/api/` for: + +- **File uploads** — actions receive JSON params, not multipart form data +- **Streaming responses** — SSE or chunked responses that need direct H3 control +- **Webhooks** — external services POST to a specific URL +- **OAuth callbacks** — redirect-based flows that need specific URL patterns + +If it's a standard CRUD operation, data query, or a wrapper around an action, use the action instead. + +## Legacy Pattern (bare export) + +Older actions use a bare async function export with `parseArgs`: + +```ts +import { parseArgs, loadEnv, fail } from "@agent-native/core"; + +export default async function myAction(args: string[]) { + loadEnv(); + const parsed = parseArgs(args); + // ... +} +``` + +This still works but is not auto-exposed as HTTP. Prefer `defineAction` for all new actions. + +## Guidelines + +- **One action, one job.** Keep actions focused on a single operation. The agent composes multiple action calls for complex operations. +- **Return structured data.** Return objects/arrays, not `JSON.stringify()`. +- **Use `http: { method: "GET" }`** for read-only actions. Default is POST. +- **Use `http: false`** for agent-only actions (`navigate`, `view-screen`). +- **Use `agentTool: false`** for UI-only / programmatic actions that should NOT be a tool in the model's context window. It stays frontend/HTTP-callable but is hidden from the agent. Distinct from `toolCallable: false`, which only blocks the sandboxed extension iframe bridge. +- **Document reusable actions.** If a new action should be called by agents outside one narrow screen, update `AGENTS.md` with when to use it, important args, and which return fields to preserve. +- **Promote workflow-heavy actions to skills.** If the action is part of a provider-backed, cross-app, MCP/A2A, or multi-step workflow, create or update a skill in `.agents/skills/` and add app-skill visibility (`internal`, `exported`, or `both`) when it should ship through a marketplace. +- **Use `loadEnv()`** only for deploy-level configuration. User/org/workspace + credentials belong in the encrypted secrets/credential/OAuth stores, never as + hardcoded literals or shared env fallbacks. +- **Use `fail()`** for user-friendly error messages (exits with message, no stack trace). +- **Import action primitives from `@agent-native/core/action`** and CLI helpers such as `parseArgs()` from `@agent-native/core` — do not redefine framework utilities locally. +- **Do not re-export actions as REST.** The mounted `/_agent-native/actions/:name` endpoint is the REST surface; duplicating it under `/api/*` creates drift and hides the operation from agents. + +## Common Patterns + +**Read action (GET):** + +```ts +import { z } from "zod"; +import { defineAction } from "@agent-native/core/action"; + +export default defineAction({ + description: "List calendar events", + schema: z.object({ + from: z.string().describe("Start date"), + to: z.string().describe("End date"), + }), + http: { method: "GET" }, + run: async (args) => { + return await fetchEvents(args.from, args.to); + }, +}); +``` + +**Write action (POST, default):** + +```ts +import { z } from "zod"; +import { defineAction } from "@agent-native/core/action"; + +export default defineAction({ + description: "Log a meal", + schema: z.object({ + name: z.string().describe("Meal name"), + calories: z.coerce.number().describe("Calorie count"), + }), + run: async (args) => { + // args.calories is a number — z.coerce.number() handles string-to-number conversion from HTTP + const meal = await insertMeal(args); + return meal; + }, +}); +``` + +**Agent-only action:** + +```ts +import { z } from "zod"; +import { defineAction } from "@agent-native/core/action"; + +export default defineAction({ + description: "Navigate the UI to a view", + schema: z.object({ + view: z.string().describe("Target view"), + }), + http: false, + run: async (args) => { + await writeAppState("navigate", { command: "go", view: args.view }); + return "Navigated"; + }, +}); +``` + +## Troubleshooting + +- **Action not found** — Check that the filename matches the command name exactly. `pnpm action foo-bar` looks for `actions/foo-bar.ts`. +- **Args not parsing** — Ensure args use `--key value` or `--key=value` format. Boolean flags use `--flag` (sets value to `"true"`). +- **Frontend getting 405** — The action's `http.method` doesn't match the hook. Use `useActionQuery` for GET actions, `useActionMutation` for POST/PUT/DELETE. +- **Frontend getting undefined** — Make sure the action returns structured data, not `JSON.stringify()`. + +## Related Skills + +- **storing-data** — Actions read/write data in SQL +- **delegate-to-agent** — The agent invokes actions via `pnpm action ` +- **real-time-sync** — Database writes from actions trigger change events to update the UI +- **adding-a-feature** — Actions are area 2 of the four-area checklist +- **client-methods** — Client code uses named helpers/hooks instead of raw REST calls + +## Change summary (required) + +After any code change in this repo, follow the [`change-summary`](../change-summary/SKILL.md) skill: end the response with a **Change summary** split into **Code**, **Tests**, **Config**, and **Docs**, with per-file `+` / `−` line counts from `git diff --numstat`. diff --git a/templates/tasks/.agents/skills/adding-a-feature/SKILL.md b/templates/tasks/.agents/skills/adding-a-feature/SKILL.md new file mode 100644 index 0000000000..b3133d4fe2 --- /dev/null +++ b/templates/tasks/.agents/skills/adding-a-feature/SKILL.md @@ -0,0 +1,178 @@ +--- +name: adding-a-feature +description: >- + The four-area checklist every new feature must complete. Use when adding any + feature, integration, or capability to ensure the agent and UI stay in parity. +metadata: + internal: true +--- + +# Adding a Feature — The Four-Area Checklist + +## Rule + +Every new feature MUST update all four areas. Skipping any one breaks the agent-native contract — the agent and UI must always be equal partners. + +## Why + +Agent-native apps are defined by parity: everything the UI can do, the agent can do, and vice versa. A feature that only has UI is invisible to the agent. A feature that only has scripts is invisible to the user. A feature without app-state sync means the agent is blind to what the user is doing. + +## The Checklist + +When you add a new feature, work through these four areas in order: + +### 1. UI Component + +Build the user-facing interface — a page, component, dialog, or route. Use `useActionQuery` and `useActionMutation` from `@agent-native/core/client` to call actions for data fetching and mutations. Do not create a custom REST endpoint just so React can call action-backed data; the action endpoint already exists. + +**Auto-refresh on agent writes is non-negotiable** — when the agent mutates data, the UI must reflect the change without a manual refresh. There are two paths, and you must pick the right one: + +- **`useActionQuery` / `useActionMutation`** — covered automatically. The framework's `useDbSync` invalidates `["action"]` on every change event, so every `useActionQuery` hook refetches on agent activity. No extra wiring required. **Prefer this path.** +- **Raw `useQuery` with custom keys** — needs explicit wiring. Fold `useChangeVersions([, "action"])` from `@agent-native/core/client` into the `queryKey` and set `placeholderData: (prev) => prev`. The `action` source is the reliable signal (the agent runner emits it after every successful tool call); the resource-specific source (`"dashboards"`, `"analyses"`, `"settings"`, etc.) is bonus when emitted. Without this wiring, agent writes will be invisible until manual refresh — that breaks the framework's #1 promise. + + ```tsx + import { useChangeVersions } from "@agent-native/core/client"; + import { useQuery } from "@tanstack/react-query"; + + const v = useChangeVersions(["dashboards", "action"]); + useQuery({ + queryKey: ["dashboard", id, v], + queryFn: () => fetchDashboard(id), + placeholderData: (prev) => prev, // no flicker on refetch + }); + ``` + + See the `real-time-sync` skill for the full pattern and source catalog. + +### 2. Action + +Create an action in `actions/` using `defineAction`. This serves double duty: the agent calls it as a tool, and the UI calls it through `useActionQuery` / `useActionMutation` while the framework owns the HTTP transport. Set `http: { method: "GET" }` for read actions, leave default for writes, or set `http: false` for agent-only actions like `navigate` and `view-screen`. + +Before adding a new route or endpoint, inspect the existing actions. Reuse an +action if it already covers the business operation, extend it if the shared +contract is incomplete, or create a new `defineAction` if the agent and UI both +need the capability. Do not add pass-through `/api/*` routes that re-export +actions. If client code needs a new framework/app route, expose a named helper +or hook first and use that helper from components and docs. + +For provider-backed analysis/query/reporting integrations, do not turn every +provider endpoint or filter into a rigid action. Prefer the shared +`provider-api-catalog` / `provider-api-docs` / `provider-api-request` pattern +from `@agent-native/core/provider-api`, then add narrow convenience actions only +for workflows that truly deserve a first-class shortcut. Treat this as a +capability requirement, not a nice-to-have: convenience actions must not become +the ceiling of what the agent can ask the provider to do. Pair broad provider +access with staging or sandboxed code execution when responses may be too large +for chat context. If provider credentials live on resource/share rows, add the +scoped resolver first so broad access preserves the same ownership boundary as +the app UI. + +If the feature needs credentials, design the credential path in the same change. +Never hardcode API keys, tokens, webhook URLs, signing secrets, private +Builder/internal data, or customer data in the action, UI, seed data, fixtures, +docs, prompts, or generated extension/app content. Register required secrets, +use OAuth helpers, or read scoped values from the vault/credential store. + +**If the action produces or lists a navigable resource**, add a `link` builder that returns `{ url: buildDeepLink({ app, view, params }), label }`. External coding agents and MCP hosts (Claude / ChatGPT / Claude Code / Cowork / Codex, over MCP/A2A) then surface an "Open in … →" deep link that drops the user back into the running UI focused on the record — for free. If a compatible MCP host should render an inline review/edit surface, also add `mcpApp` with `embedApp()` so the action embeds the real React app route instead of a one-off HTML UI. The `link` builder and `mcpApp` metadata must be pure and synchronous (no I/O). Any external-agent read/ingest action must be `http: { method: "GET" }` + `readOnly: true` + `publicAgent: { expose: true, readOnly: true, requiresAuth: true }`. See the `external-agents` skill. + +### 3. Skills / Instructions + +Update `AGENTS.md` and/or create a skill in `.agents/skills/` if the feature introduces patterns the agent needs to know. At minimum, add the new actions to the action table in the template's `AGENTS.md`. + +Reusable actions are part of the app contract, not just implementation detail. When an action is useful outside one screen, update agent instructions in the same change so app agents know when to call it, which arguments matter, and what output to preserve. If the capability is workflow-heavy, cross-app, provider-backed, or has a non-obvious sequence of actions, add or update a skill instead of burying the behavior in one long `AGENTS.md` paragraph. + +Instruction examples may name secret keys like `SLACK_WEBHOOK`, but must use +placeholders such as `${keys.SLACK_WEBHOOK}` or ``. Do not paste +real keys, internal data, or customer data into instructions as examples. + +For app-backed skills, declare skill visibility in the app-skill manifest: + +- `internal` — only the app's own agents should use it. +- `exported` — marketplace installs receive it, but the app does not need it loaded internally. +- `both` — shared between the app's internal agents and exported marketplace bundles. + +### 4. Application State Sync + +Expose navigation and selection state so the agent knows what the user is looking at. Write to the `navigation` app-state key on route changes. Update the `view-screen` action to fetch relevant data for the new feature. Add a `navigate` command if the agent needs to open the new view. + +## Examples + +### Adding "compose email" to a mail app + +| Area | What to build | +| --------------- | ---------------------------------------------------------------------------------------- | +| UI | Compose panel with tabs, to/cc/bcc fields, body editor. Use `useActionQuery`/`useActionMutation` for data. | +| Action | `manage-draft` action (create/update/delete drafts), `send-email` action | +| Skills/AGENTS | Document compose state shape, draft lifecycle, action args in AGENTS.md | +| App-state sync | `compose-{id}` keys for each draft tab, `navigation` includes compose state | + +### Adding "create form" to a forms app + +| Area | What to build | +| --------------- | ---------------------------------------------------------------------------------------- | +| UI | Form builder page with drag-and-drop fields, preview, settings. Use `useActionQuery` for lists. | +| Action | `create-form` action, `update-form` action, `list-forms` action (GET) | +| Skills/AGENTS | Document form schema shape, field types, validation rules in AGENTS.md | +| App-state sync | `navigation` includes `{ view: "form-builder", formId: "..." }`, `view-screen` fetches form data | + +### Adding "chart type" to an analytics app + +| Area | What to build | +| --------------- | ---------------------------------------------------------------------------------------- | +| UI | New chart component, chart type selector in dashboard | +| Action | `create-chart` or `update-dashboard` action that sets chart type and config | +| Skills/AGENTS | Document supported chart types, config options, data requirements | +| App-state sync | `navigation` includes selected chart/dashboard, `view-screen` returns chart config | + +## Adding a new route + +Templates are single-page apps with client-side routing. The app shell (AgentSidebar + top-level nav) MUST persist across navigation — it is mounted once, either in `root.tsx` around `` or via a pathless `_app.tsx` layout route that all authed routes nest under. + +**Never wrap each new route in its own `` / ``.** That causes React to unmount the entire app shell on every navigation, reloading the agent sidebar and destroying in-progress work. + +- If the template has `` in `root.tsx` — just render page content in your new route file, nothing else. +- If the template has `app/routes/_app.tsx` (pathless layout) — name your new route `_app..tsx` to inherit the shell, or bare `.tsx` for public routes that should NOT have the shell. +- If a page needs per-route data (e.g. highlighting the active item in the sidebar), read it in the layout from `useParams()` / `useLocation()`. Don't pass it as a prop through every route file. + +See the "Client-Side Routing" section in the root `CLAUDE.md` for full details. + +## Anti-Patterns + +- **Per-route `` wrappers** — Every route file wraps its content in `` or ``. React sees a different component at the outlet on each nav and unmounts the whole shell, causing the agent sidebar to reload on every click. Mount the shell once above `` (root.tsx or `_app.tsx` pathless layout). +- **UI without actions** — The user can create forms but the agent cannot. The agent says "I don't have access to that" when it should be able to do it. +- **Actions without AGENTS.md** — The actions exist but the agent doesn't know about them because they're not documented. The agent reinvents solutions instead of using the actions. +- **Duplicate API routes** — Creating `/api/` routes for operations that actions already handle, including pass-through routes that just call or repackage an action. Use `useActionQuery`/`useActionMutation` instead. +- **Raw client route calls** — Teaching or adding `fetch("/_agent-native/...")`, + `fetch(agentNativePath(...))`, or template `/api/*` calls in components for + normal app work. Add a named client helper/hook and call that instead. +- **Features without app-state** — The agent cannot see that the user is looking at a specific form, email, or chart. It asks "which one?" instead of acting on the current selection. +- **Actions without UI** — The agent can do something the user cannot. This is less common but still breaks parity. + +## Verification + +After completing all four areas, verify: + +1. Can the user perform the operation from the UI? +2. Can the agent perform the same operation via actions? +3. Does `pnpm action view-screen` show the relevant state when the user is using the feature? +4. Can the agent navigate to the feature view via the `navigate` action? +5. Is the feature documented in AGENTS.md with action names and args? +6. Are credentials and sensitive data supplied only through approved runtime + channels, with no hardcoded real keys, tokens, webhook URLs, Builder/internal + data, or customer data? + +## One more area — sharing + +If the feature stores **user-authored resources** (documents, dashboards, forms, decks, etc.), make them ownable so they get private-by-default semantics and a share dialog for free. See the `sharing` skill. + +TL;DR: spread `ownableColumns()` into the resource table, pair it with `createSharesTable(...)`, call `registerShareableResource(...)`, wrap list/read queries with `accessFilter`, guard writes with `assertAccess`, and drop `` in the resource header. The `share-resource`, `unshare-resource`, `list-resource-shares`, and `set-resource-visibility` actions are auto-mounted framework-wide. + +## Related Skills + +- **sharing** — How to make a new resource ownable (private by default, share with users/orgs/public) +- **context-awareness** — How to expose UI state to the agent (area 4 in detail) +- **actions** — How to create actions with `defineAction` and the `http` option (area 2 in detail) +- **external-agents** — Add a `link` builder so external agents (MCP/A2A) get an "Open in … →" deep link +- **create-skill** — How to create skills for new patterns (area 3 in detail) +- **storing-data** — Where to store the feature's data +- **real-time-sync** — How the UI stays in sync when the agent writes data diff --git a/templates/tasks/.agents/skills/agent-native-docs/SKILL.md b/templates/tasks/.agents/skills/agent-native-docs/SKILL.md new file mode 100644 index 0000000000..8cd8dc9abe --- /dev/null +++ b/templates/tasks/.agents/skills/agent-native-docs/SKILL.md @@ -0,0 +1,63 @@ +--- +name: agent-native-docs +description: >- + How to find version-matched Agent Native framework docs bundled in + node_modules. Use before implementing or answering questions about + @agent-native/core APIs, generated apps, workspaces, or advanced features. +metadata: + internal: true +--- + +# Agent Native Docs Lookup + +## Rule + +Before implementing or explaining non-trivial Agent Native behavior, read the +version-matched docs installed with `@agent-native/core`. + +## Why + +Generated apps and workspaces may be on a different framework version than the +public docs or model memory. The installed package is the source that matches +the app in front of you. + +## How + +From a generated app directory: + +```bash +pnpm action docs-search --query "" +pnpm action docs-search --slug +pnpm action docs-search --list +``` + +The headless `pnpm agent` loop and built-in app agent also expose a read-only +`docs-search` tool with the same `query`, `slug`, and `list` options. + +If the action runner is unavailable, search the package docs directly: + +```bash +rg -n "actions|automations|a2a|sharing" node_modules/@agent-native/core/docs +``` + +Then read `node_modules/@agent-native/core/docs/AGENTS.md` or the matching file +under `node_modules/@agent-native/core/docs/content/`. + +## Useful Slugs + +| Need | Slugs | +| --- | --- | +| Actions and typed client calls | `actions`, `client` | +| SQL, auth, access, sharing | `database`, `authentication`, `security`, `sharing` | +| UI state visible to the agent | `context-awareness` | +| Headless and chat-first apps | `pure-agent-apps`, `agent-surfaces`, `using-your-agent` | +| Automations and schedules | `automations`, `recurring-jobs` | +| Cross-app and external agents | `a2a-protocol`, `external-agents`, `mcp-protocol`, `mcp-apps` | +| Skills and instructions | `skills-guide`, `writing-agent-instructions` | + +## Don't + +- Do not rely on memory for framework APIs when package docs are present. +- Do not add custom REST wrappers for app data before reading `actions`. +- Do not add inline LLM calls before reading `using-your-agent` and + `agent-surfaces`. diff --git a/templates/tasks/.agents/skills/agent-watchdog/README.md b/templates/tasks/.agents/skills/agent-watchdog/README.md new file mode 100644 index 0000000000..01c4eac2a7 --- /dev/null +++ b/templates/tasks/.agents/skills/agent-watchdog/README.md @@ -0,0 +1,43 @@ +# /agent-watchdog + +Audit another agent's work from a session, transcript, PR, branch, or run +summary. + +`/agent-watchdog` is for the common handoff where one agent needs to watch +another agent finish, then check whether the work actually matches what was +asked. + +## What It Does + +- Resolves Codex, Claude Code, PR, branch, transcript, or pasted run artifacts. +- Waits for completion when the user asks to watch. +- Reconstructs the original request and later scope changes. +- Checks the agent's claims against diffs, files, tests, CI, screenshots, and + review state. +- Reports gaps, weak verification, scope drift, and residual risk. +- Makes narrow fixes when the user explicitly authorizes repair. + +## When To Use It + +Use it when a user says things like: + +- "Watch this session until done and audit it." +- "Claude finished, check what it did and fix gaps." +- "Compare these two agents' work." +- "Babysit this PR and make sure the agent actually handled the feedback." + +Skip it for normal code review when there is no other agent session or run to +audit. + +## Modes + +- Watch only: monitor and report, no edits. +- Audit: inspect the work and produce a gap report, no edits. +- Audit and fix: repair clear gaps with targeted validation. +- Compare: evaluate multiple sessions against the same request. + +## Install + +```sh +npx @agent-native/skills@latest add --skill agent-watchdog +``` diff --git a/templates/tasks/.agents/skills/agent-watchdog/SKILL.md b/templates/tasks/.agents/skills/agent-watchdog/SKILL.md new file mode 100644 index 0000000000..bd0c6af97b --- /dev/null +++ b/templates/tasks/.agents/skills/agent-watchdog/SKILL.md @@ -0,0 +1,110 @@ +--- +name: agent-watchdog +description: Use when asked to watch, babysit, audit, review, compare, or fix another agent's work from a Codex session ID, Claude Code session/transcript, chat/thread link, PR, branch, log, or pasted run summary. Monitor until the other agent is done or blocked, reconstruct what the user asked, inspect what the agent actually changed and verified, report gaps, and optionally make scoped fixes when the user authorizes repair. +--- + +# Agent Watchdog + +Watch another agent's work like a reviewer with a pager: wait for completion +when needed, reconstruct the request, verify the evidence, and close the gap +between what was asked and what actually happened. + +## Choose The Mode + +Infer the mode from the user's wording: + +- **Watch only:** monitor a session, PR, branch, CI run, or transcript until it + reaches a terminal state. Do not edit files. +- **Audit:** read the prompt, transcript, diff, tests, CI, comments, screenshots, + or final claims and return a gap report. Do not edit files. +- **Audit and fix:** audit first, then make narrow fixes for clear gaps. Avoid + broad rewrites, branch movement, or speculative changes. +- **Compare:** when given multiple sessions or agents, compare their work against + the same original request and reconcile the important differences. + +If authority is unclear, default to audit-only and say what you would fix. + +## Resolve The Target + +1. Identify every artifact the user supplied: session ID, transcript path, + thread URL, PR, branch, commit, CI run, issue, Slack link, or pasted summary. +2. Use the host's native thread/history tools, local transcript files, repo + logs, GitHub tools, or pasted content to resolve the artifact. Prefer the + most direct source over summaries. +3. If the artifact is still running and the user asked to watch, poll at a + reasonable interval until it is done, blocked, stale, or clearly waiting on a + human/external system. +4. If the artifact cannot be resolved, ask for the missing identifier or path. + +## Reconstruct The Contract + +Build a compact contract before judging the work: + +- Original user request and any later changes in scope. +- Explicit constraints: branch rules, no-edit requests, deadlines, package + versions, validation expectations, design requirements, or security/privacy + limits. +- Implied acceptance criteria: user-visible behavior, tests, CI, docs, deploys, + screenshots, review replies, or status updates. +- The other agent's final claims and any "could not do" caveats. + +Treat the user's request as the source of truth, not the other agent's summary. + +## Audit The Evidence + +Inspect evidence, not vibes: + +- Read changed files and relevant unchanged files around the touched paths. +- Check git status/diff without reverting unrelated work. +- Compare commands the agent claimed to run with actual output when available. +- Inspect failed or skipped tests, CI logs, browser screenshots, review + comments, deploy output, and error traces. +- For PR/review work, verify unresolved threads and CI state from the source + system when tools are available. +- For UI work, prefer screenshots or browser checks over prose claims. + +Classify each issue as: + +- **Gap:** requested behavior is missing or incomplete. +- **Bug:** the implementation likely fails or regresses behavior. +- **Verification miss:** the work may be right but the evidence is weak. +- **Scope drift:** the agent changed something unrelated or skipped a constraint. +- **No issue:** the concern is already handled, with evidence. + +## Fix Narrowly + +When the user authorized repair: + +1. Fix only gaps with clear evidence. +2. Preserve unrelated local changes and do not move branches unless explicitly + asked for that branch operation. +3. Use existing repo patterns and targeted tests. +4. Re-run the smallest useful validation after each meaningful fix. +5. If a fix would require a product decision, credential, destructive action, or + broad rewrite, stop and report the decision instead of guessing. + +## Report + +Lead with the outcome. Keep the report short enough to scan: + +```md +Status +- Done, blocked, stale, or still running. + +Requested +- What the user asked the watched agent to do. + +Observed +- What the watched agent changed, claimed, and verified. + +Gaps +- Missing behavior, bugs, weak verification, or scope drift. + +Fixes made +- Files changed and validation run. Omit this section for audit-only work. + +Remaining risk +- Anything still unverified or waiting on CI/review/deploy/human input. +``` + +Name exact files, commands, PRs, or thread IDs when they matter. diff --git a/templates/tasks/.agents/skills/agent-watchdog/agents/openai.yaml b/templates/tasks/.agents/skills/agent-watchdog/agents/openai.yaml new file mode 100644 index 0000000000..860b5a4a99 --- /dev/null +++ b/templates/tasks/.agents/skills/agent-watchdog/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Agent Watchdog" + short_description: "Audit another agent session and fix gaps." + default_prompt: "Use $agent-watchdog to watch or audit an agent session, then report or fix gaps." diff --git a/templates/tasks/.agents/skills/capture-learnings/SKILL.md b/templates/tasks/.agents/skills/capture-learnings/SKILL.md new file mode 100644 index 0000000000..88511805c4 --- /dev/null +++ b/templates/tasks/.agents/skills/capture-learnings/SKILL.md @@ -0,0 +1,76 @@ +--- +name: capture-learnings +description: >- + Capture and apply accumulated knowledge via structured memory. Use when the + user gives feedback, shares preferences, corrects a mistake, or when you + discover something worth remembering for future conversations. +user-invocable: false +metadata: + internal: true +--- + +# Capture Learnings + +This is background knowledge, not a slash command. **Your memory index is loaded at the start of every conversation.** Use `save-memory` proactively when you learn something worth remembering. + +## How to Read & Write Memories + +Memories are stored as **resources** in the SQL database (personal scope), not as files on disk. + +- **Save a memory:** `save-memory --name --type --description "..." --content "..."` +- **Read a memory:** `resource-read --path memory/.md` +- **Delete a memory:** `delete-memory --name ` +- **List all memories:** `resource-list --prefix memory/` + +## Memory Types + +| Type | Use for | +|------|---------| +| `user` | Preferences, role, personal context, contacts | +| `feedback` | Corrections, confirmed approaches, things to avoid or repeat | +| `project` | Ongoing work context, decisions, deadlines, status | +| `reference` | Pointers to external systems, URLs, API details | + +## When to Capture + +### User Preferences & Memory (`user`) +- **Tone and style** — "I prefer casual tone", "don't use emojis", "keep replies short" +- **Personal context** — contacts, relationships, habits ("my wife's email is...", "I'm in PST timezone") +- **Workflow preferences** — "always CC my assistant", "I like to review before sending" +- **Role and expertise** — "I'm a data scientist", "new to React" + +### Feedback & Corrections (`feedback`) +- **Corrections** — user says "no, do it this way" → capture the right way +- **Confirmed approaches** — user validates a non-obvious choice ("yes, that's perfect") +- **Repeated friction** — you hit the same issue twice; save it + +### Project Context (`project`) +- **Ongoing work** — who is doing what, why, by when +- **Decisions** — why something is done a certain way +- **Status** — current state of initiatives + +### References (`reference`) +- **External systems** — "bugs are tracked in Linear project INGEST" +- **URLs** — dashboards, documentation, tools +- **API quirks** — undocumented behavior, version-specific gotchas + +### Don't Capture +- Things obvious from reading the code +- Standard language/framework behavior +- Temporary debugging notes +- Anything already in AGENTS.md or skills +- Ephemeral task details (use tasks/plans instead) + +## Key Rules + +1. **Save proactively — don't ask permission.** When you learn something, save it immediately. +2. **One memory per topic** — e.g. `coding-style`, `project-alpha`, not one giant dump +3. **Read before updating** — if a memory exists, read it first and merge, don't overwrite +4. **Keep descriptions concise** — the index is loaded every conversation +5. **Memories are SQL-backed** — safe for personal info, persist across sessions, not in git + +## Graduation + +When a memory is referenced repeatedly, it may belong in AGENTS.md or a skill: +- Saving a memory is lightweight (auto-apply, personal scope) +- Updating AGENTS.md or a skill is heavier (affects all users/agents) diff --git a/templates/tasks/.agents/skills/change-summary/SKILL.md b/templates/tasks/.agents/skills/change-summary/SKILL.md new file mode 100644 index 0000000000..f7a02f0751 --- /dev/null +++ b/templates/tasks/.agents/skills/change-summary/SKILL.md @@ -0,0 +1,83 @@ +--- +name: change-summary +description: >- + End agent responses that include code changes with a Change summary table + (Code / Tests / Config / Docs) and per-file line counts from git diff. +metadata: + internal: true +--- + +# Change Summary + +When you modify this repo, **end every response that includes code changes** with a **Change summary** section. + +## Required format + +Split the summary into four buckets. Omit a bucket only when nothing changed in it. + +```md +## Change summary + +### Code +| File | + | − | +|------|---|---| +| `path/to/file.ts` | 12 | 3 | + +**Subtotal:** +N −M across K files + +### Tests +| File | + | − | +|------|---|---| +| `path/to/file.test.ts` | 4 | 1 | + +**Subtotal:** +N −M across K files + +### Config +| File | + | − | +|------|---|---| +| `server/plugins/db.ts` | 1 | 1 | + +**Subtotal:** +N −M across K files + +### Docs +| File | + | − | +|------|---|---| +| `AGENTS.md` | 2 | 2 | + +**Subtotal:** +N −M across K files + +**Total:** +N −M across K files +``` + +## How to classify files + +| Bucket | Include | +|--------|---------| +| **Code** | `actions/*.ts` (non-test), `app/**`, `server/**` except test-only helpers | +| **Tests** | `**/*.test.ts`, `**/test-*.ts`, in-memory test DB SQL in `server/db/test-tasks-table.ts` | +| **Config** | DB migrations and table defs (`server/plugins/db.ts`, `server/db/schema.ts`), `package.json`, `tsconfig*.json`, `vite.config.*`, CI/workflow YAML, env templates | +| **Docs** | `*.md`, skill files under `.agents/skills/` and `.claude/skills/` | + +New untracked files: list under the right bucket with **+lines 0** (all lines are additions). + +Renames/deletes: show the old path with deletions and the new path with additions, or one row with net +/−. + +## How to compute counts + +Prefer git over guessing: + +```bash +git diff --numstat +git status --short # for untracked files +wc -l # line count for new files +``` + +Use line counts from `git diff --numstat` (added, removed). Do not approximate from memory. + +## When to include it + +- Always after implementation, refactor, or fix commits in the working tree. +- Skip only for pure Q&A with **no** file edits. +- If the user asked only for a review with no changes, skip the summary. + +Place the change summary **before** the final 🟢/🟡/🔴 status line. diff --git a/templates/tasks/.agents/skills/create-skill/SKILL.md b/templates/tasks/.agents/skills/create-skill/SKILL.md new file mode 100644 index 0000000000..1aa0698bc6 --- /dev/null +++ b/templates/tasks/.agents/skills/create-skill/SKILL.md @@ -0,0 +1,214 @@ +--- +name: create-skill +description: >- + How to create new skills for an agent-native app. Use when adding a new + skill, documenting a pattern the agent should follow, or creating reusable + guidance for the agent. +metadata: + internal: true +--- + +# Create a Skill + +## When to Use + +Create a new skill when: + +- There's a pattern the agent should follow repeatedly. +- A multi-step workflow needs reliable, step-by-step guidance. +- You want to scaffold files from a template. + +Don't create a skill when: + +- The guidance already exists in another skill — extend it instead. +- You're documenting something the agent already knows (e.g., how to write + TypeScript). +- It's a one-off — put it in `AGENTS.md` (for everyone) or `memory/MEMORY.md` + (personal, per-user). See **capture-learnings**. + +## Interview + +Before writing the skill, answer these: + +1. **What should this skill enable?** — The core purpose in one sentence. +2. **Which of the four areas does it serve?** — UI, actions, skills/instructions, + or application state (see the **adding-a-feature** skill). Most skills are + about how to touch one or more of these correctly. +3. **When should it trigger?** — Describe the situations in natural language. + Be slightly pushy — over-triggering is better than under-triggering. +4. **Does it involve context awareness?** — Does the agent need to know what the + user is looking at? If so, reference the `navigation` application-state key and + the `view-screen` action pattern. See the **context-awareness** skill. +5. **What type of skill?** — Pattern, Workflow, or Generator (see below). +6. **Does it need supporting files?** — References (read-only context) or none. + Keep it minimal; push depth into `references/`. + +## Skill Types and Templates + +### Pattern (architectural rule) + +For documenting how things should be done: + +```markdown +--- +name: my-pattern +description: >- + [Under 40 words. What it covers AND when it should trigger.] +--- + +# [Pattern Name] + +## Rule + +[One sentence: what must be true] + +## Why + +[Why this rule exists] + +## How + +[How to follow it, with code examples] + +## Don't + +[Common violations] + +## Related Skills + +[Which skills compose with this one] +``` + +### Workflow (step-by-step) + +For multi-step implementation tasks: + +```markdown +--- +name: my-workflow +description: >- + [Under 40 words. What it covers AND when it should trigger.] +--- + +# [Workflow Name] + +## Prerequisites + +[What must be in place first] + +## Steps + +[Numbered steps with code examples] + +## Verification + +[How to confirm it worked] + +## Troubleshooting + +[Common issues and fixes] + +## Related Skills +``` + +### Generator (scaffolding) + +For creating files from templates: + +```markdown +--- +name: my-generator +description: >- + [Under 40 words. What it covers AND when it should trigger.] +--- + +# [Generator Name] + +## Usage + +[How to invoke — what args/inputs are needed] + +## What Gets Created + +[List of files and their purpose] + +## Template + +[The template content with placeholders] + +## After Generation + +[What to do next — wire up sync, add routes, register the action, etc.] + +## Related Skills +``` + +## Naming Conventions + +- Hyphen-case only: `[a-z0-9-]`, max 64 characters. +- Pattern skills: descriptive names (`storing-data`, `delegate-to-agent`). +- Workflow/generator skills: verb-noun (`create-skill`, `capture-learnings`). +- The directory name must match the `name` in frontmatter. + +## Skill Scope (runtime vs dev) + +An optional `scope` frontmatter field controls which agent loads the skill: + +- `both` (default when omitted) — loaded by the in-app runtime agent. Use for + any skill the runtime agent should follow. +- `runtime` — loaded only by the in-app runtime agent. +- `dev` — meant for the human's coding agent (e.g. Claude Code) only. **Excluded + from the runtime agent everywhere**: not in the system-prompt skills block and + not in `docs-search` results. + +```markdown +--- +name: release-checklist +description: >- + Steps for cutting a release. Use when preparing or publishing a new version. +scope: dev +--- +``` + +Leave `scope` off for normal skills — the default (`both`) keeps them loading at +runtime, so this is fully backward compatible. To make a dev-only skill visible +to your coding agent but hidden from the runtime agent, mark it `scope: dev` and +optionally mirror it under `.claude/skills//SKILL.md` (Claude Code reads +`.claude/skills/` independently of the runtime's `.agents/skills/`). + +## Tips + +- **Keep descriptions under 40 words** — they load into context on every + conversation. State what the skill does AND when to trigger it. +- **Keep SKILL.md lean (under ~500 lines)** — move detailed content to + `references/` files (progressive disclosure). +- **Use standard markdown headings** — no XML tags or custom formats. + +## Anti-Patterns + +- **Inline LLM calls** — skills must not call LLMs directly. All AI work goes + through the agent chat (see **delegate-to-agent**). +- **Introducing databases** — data lives in SQL via Drizzle (see **storing-data**). +- **Ignoring sync** — if a skill creates data, mention wiring `useDbSync` / + `useActionQuery` so the UI updates (see **real-time-sync**). +- **Vague descriptions** — "Helps with development" won't trigger. Be specific + about _when_. +- **Pure documentation** — skills should guide action, not just explain concepts. + +## File Structure + +``` +.agents/skills/my-skill/ +├── SKILL.md # Main skill (required) +└── references/ # Optional supporting context + └── detailed-guide.md +``` + +## Related Skills + +- **adding-a-feature** — The four-area model every skill ultimately serves. +- **writing-agent-instructions** — How to write AGENTS.md and skills well for + apps and templates you ship to others. +- **capture-learnings** — When a learning graduates to reusable guidance, create + a skill; one-offs go to `AGENTS.md` or `memory/MEMORY.md`. +- **self-modifying-code** — The agent can create new skills (Tier 2 modification). diff --git a/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md b/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md new file mode 100644 index 0000000000..80acc7570d --- /dev/null +++ b/templates/tasks/.agents/skills/delegate-to-agent/SKILL.md @@ -0,0 +1,262 @@ +--- +name: delegate-to-agent +description: >- + How to delegate all AI work to the agent chat. Use when delegating AI work + from UI or scripts to the agent, when a user asks for agent behavior or + LLM-powered features, when tempted to add inline LLM calls, or when sending + messages to the agent from application code. +metadata: + internal: true +--- + +# Delegate All AI to the Agent + +## Rule + +The UI never calls an LLM directly. Product workflows are delegated to the +agent through the chat bridge so users can see, steer, and audit the work. +Server-side one-shot model calls are an explicit escape hatch for narrow text +transforms only; use `completeText()` from `@agent-native/core/server` when the +work intentionally does not need tools, chat history, or run state. + +## Why + +The agent is the single AI interface. It has context about the full project, can read/write any file, and can run scripts. Inline LLM calls bypass this — they create a shadow AI that doesn't know what the agent knows and can't coordinate with it. + +## How + +**From the UI (client):** + +```ts +import { sendToAgentChat } from "@agent-native/core/client"; + +sendToAgentChat({ + message: "Generate a summary of this document", + context: documentContent, // optional hidden context (not shown in chat UI) + submit: true, // auto-submit to the agent +}); +``` + +**From the UI, in the background:** + +```ts +import { sendToAgentChat } from "@agent-native/core/client"; + +sendToAgentChat({ + message: "Analyze this import and create any missing records", + context: `Import batch id: ${batchId}`, + submit: true, + newTab: true, + background: true, + openSidebar: false, +}); +``` + +This is still a full agent run: tools, actions, thread state, and run tracking +all remain active. It simply does not focus or open the sidebar. + +**From scripts (Node):** + +```ts +import { agentChat } from "@agent-native/core"; + +agentChat.submit("Process the uploaded images and create thumbnails"); +``` + +**For narrow server-side text transforms:** + +```ts +import { completeText } from "@agent-native/core/server"; + +const result = await completeText({ + systemPrompt: "Return exactly one sentiment label.", + input: messageBody, + maxOutputTokens: 12, + temperature: 0, +}); +``` + +Wrap user-facing uses in actions so the UI and agent share the same operation. +Do not call provider SDKs directly. + +**From the UI, detecting when agent is done:** + +```ts +import { useAgentChatGenerating } from "@agent-native/core/client"; + +function MyComponent() { + const isGenerating = useAgentChatGenerating(); + // Show loading state while agent is working +} +``` + +## `submit` vs Prefill + +The `submit` option controls whether the message is sent automatically or placed in the chat input for user review: + +| `submit` value | Behavior | Use when | +| -------------- | --------------------------------------- | ----------------------------------------------------------------------------------- | +| `true` | Auto-submits to the agent immediately | Routine operations the user has already approved | +| `false` | Prefills the chat input for user review | High-stakes operations (deleting data, modifying code, API calls with side effects) | +| omitted | Uses the project's default setting | General-purpose delegation | + +```ts +// Auto-submit: routine operation +sendToAgentChat({ message: "Update the project summary", submit: true }); + +// Prefill: let user review before sending +sendToAgentChat({ + message: "Delete all projects older than 30 days", + submit: false, +}); +``` + +## Capture user input first when generating from a prompt + +Buttons that produce new content ("New Design", "Create Dashboard", "Make Deck", "Generate Form") need the user's prompt as input. **Never hardcode a generic message** — the result will be a generic generation the user didn't actually ask for. + +**Bad** — auto-submits a placeholder message; the user never said what they wanted: + +```tsx + +``` + +**Good** — Popover anchored to the button captures the prompt, then submits it: + +```tsx + + + + + +