Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/contributing/community-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ the stored mark. `listFeaturedCommunityListings` feeds the onboarding page (slim
on the detail page, the admin-only toggle
(`POST /community/:listingId/feature.json`, audited), the admin-only
`community_set_featured` capability, and the onboarding "Install a starter
package" step. `community_get` exposes the effective `featured` flag.
package" step (in-place Install on each starter card, then Copy prompt for agent
setup). `community_get` exposes the effective `featured` flag.

Reports survive listing deletion via denormalized listing name and owner on the
report row.
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ available `guide` ids).
| File | Topic |
| ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| [package-authoring.md](./package-authoring.md) | General package-authoring guidance, including the README Intent section |
| [package-lifecycle.md](./package-lifecycle.md) | Choose reuse, one-off execute, direct job schedules, or a durable package; test package-owned job wrappers safely |
| [integration-bootstrap.md](./integration-bootstrap.md) | **Start here** for third-party integrations that must work before saving a dependent package or package app |
| [package-lifecycle.md](./package-lifecycle.md) | Choose reuse, one-off execute, community fork when close, direct job schedules, or a new durable package |
| [integration-bootstrap.md](./integration-bootstrap.md) | **Start here** for third-party integrations; after the smoke test, prefer a trusted community fork before building |
| [openapi-integrations.md](./openapi-integrations.md) | OpenAPI discover → summarize → scaffold or curated `openapi:<name>` binding |
| [secret-backed-integration.md](./secret-backed-integration.md) | Default recipe for non-OAuth integrations that use one or more saved secrets |
| [integration-backed-app-happy-path.md](./integration-backed-app-happy-path.md) | Default package app pattern after integration smoke test passes |
Expand Down
11 changes: 8 additions & 3 deletions docs/guides/integration-bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,15 @@ If those conditions are not met, stop and fix the integration first.
similarly small account/profile endpoint.
- Confirm the integration or secret name, token refresh behavior, and allowed
hosts all work end-to-end.
5. Only after the smoke test succeeds should you build or save the dependent
package or package app.
5. Only after the smoke test succeeds should you obtain the dependent package or
package app.
- Call `community_search` for the provider or workflow (prefer `trusted`
matches). If a listing is close to the user's goal, fork or point them at
one-click install, then adapt — do not reimplement from scratch.
- Create or save a new package only when no suitable community listing
exists.
- If the integration or tokens already exist and the smoke test passes,
proceed directly to package construction.
proceed directly to that fork-or-create step.
- Do not spend extra time exploring the local repo when the integration
state, secret names, allowed hosts, and provider contract are already clear
enough.
Expand Down
26 changes: 22 additions & 4 deletions docs/guides/package-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,31 @@ Keep the module focused and return structured evidence. Do not treat the
ephemeral module as the durable source for behavior that must be maintained,
reused, or evolved.

### Fork a close community package before creating

Community listings are excluded from general `search`. When you need durable
reusable behavior and nothing in the user's account fits, call
`community_search` and prefer **trusted** matches.

If a listing is close to the user's goal:

1. Inspect it with `community_get`.
2. Fork with `community_fork` (or point the user at one-click install on
`/onboarding` or the listing detail page).
3. Review the forked source, adapt it to the user's intent (including the README
`## Intent` section), then publish.

Do not reimplement from scratch when a trusted community package is already
close. Create a new package only when no suitable listing exists.

### Create a repo-backed package

Create or extend a saved package when behavior is reusable, expected to evolve,
or needs a named package-owned schedule that evolves with its implementation.
The repo rooted at `package.json` is the durable source of truth. Package
exports form the callable surface, while jobs, subscriptions, services,
retrievers, and apps remain package-owned behavior.
or needs a named package-owned schedule that evolves with its implementation,
and no suitable community listing (or existing saved package) covers it. The
repo rooted at `package.json` is the durable source of truth. Package exports
form the callable surface, while jobs, subscriptions, services, retrievers, and
apps remain package-owned behavior.

Scheduling alone does not require a package. Use `job_schedule` directly for a
genuinely ad hoc or one-off job, or for a simple self-contained schedule that is
Expand Down
12 changes: 7 additions & 5 deletions docs/use/community-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,13 @@ Only after publish does the package become a live saved package in your account.

## One-click install

Each listing detail page has an **Install** button for signed-in users. Install
forks the listing into your account and, when the fork passes the same publish
checks a repo session would run, publishes it immediately as a live saved
package. **Publishing activates the package right away** — declared jobs are
scheduled and `autoStart` services start.
Each listing detail page has an **Install** button for signed-in users. Featured
starter packages on `/onboarding` also support one-click install without leaving
the page: after install, **Copy prompt** gives your agent a short setup prompt.
Install forks the listing into your account and, when the fork passes the same
publish checks a repo session would run, publishes it immediately as a live
saved package. **Publishing activates the package right away** — declared jobs
are scheduled and `autoStart` services start.

- **Untrusted listings** show a warning first: no admin has reviewed the code,
and installing runs it in your account. You must explicitly confirm. Direct
Expand Down
8 changes: 7 additions & 1 deletion docs/use/first-steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,17 @@ secret reference, then run work through **execute**.
alias, `job_run_now` can trigger an existing job immediately for debugging or
catch-up runs, and `job_update` / `job_delete` let you correct or remove an
existing scheduled job by id.
- **Prefer a close community package before creating one.** Community listings
are excluded from general `search`. Use `community_search` (prefer trusted
matches) and fork or adapt when a listing is close to the goal; create a new
package only when nothing suitable exists. See
[Community packages](./community-packages.md).
- **Bootstrap integration-backed work before building.** When a package, package
app, or workflow depends on OAuth, a saved secret, or a third-party API, use
`search` and the `coding_guide_get` `integration_bootstrap` guide first.
Confirm the integration or secret exists, run a cheap authenticated smoke test
in `execute`, then build the downstream artifact.
in `execute`, then prefer a trusted community fork before building the
downstream artifact.
- **Ask for natural-language goals**, for example: “Search Kody for GitHub pull
request automation” or “Find a saved package for Cloudflare DNS helpers.”
- **Credentials use connect flows.** Use saved secrets, `/connect/oauth`,
Expand Down
57 changes: 49 additions & 8 deletions e2e/community-featured.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { expect, test } from './playwright-utils.ts'
import { seedCommunityListingInE2eDatabase } from './d1-utils.ts'
import {
executeE2eD1Command,
seedCommunityListingInE2eDatabase,
} from './d1-utils.ts'

test('admins can feature trusted listings and members see them in onboarding', async ({
page,
Expand All @@ -10,6 +13,11 @@ test('admins can feature trusted listings and members see them in onboarding', a
const trustedListingId = `e2e-featured-listing-${runId}`
const trustedListingName = `@kody/featured-package-${runId}`
const untrustedListingId = `e2e-unfeaturable-listing-${runId}`
// Clear leftover featured marks from interrupted prior runs so this test
// starts from an empty onboarding starter list.
executeE2eD1Command(
'UPDATE community_listings SET featured_at = NULL WHERE featured_at IS NOT NULL;',
)
const adminUser = await seedE2eUser({
email: `featured-admin-${runId}@example.com`,
username: `featured-admin-${runId}`,
Expand Down Expand Up @@ -77,23 +85,56 @@ test('admins can feature trusted listings and members see them in onboarding', a
page.getByRole('button', { name: 'Feature in onboarding' }),
).toBeDisabled()

// Members now see the featured listing as an onboarding starter package.
// Members now see the featured listing as an onboarding starter package
// and can install it without leaving /onboarding. Mock install JSON: local
// e2e lacks Cloudflare credentials for repo-backed source persistence.
await page.context().clearCookies()
await login({
email: memberUser.email,
password: memberUser.password,
mode: 'login',
})
await page.route(
(url) => url.pathname === `/community/${trustedListingId}/install.json`,
async (route) => {
if (route.request().method() !== 'POST') {
await route.continue()
return
}
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
ok: true,
status: 'installed',
packageId: 'pkg-e2e-onboarding-install',
sourceId: 'src-e2e-onboarding-install',
targetName: `@${memberUser.username}/featured-package-${runId}`,
agentPrompt:
'Finish setup for the installed onboarding starter package.',
}),
})
},
)
await page.goto('/onboarding')
await expect(page.getByTestId('onboarding-starter-packages')).toBeVisible()
const starterLink = page.getByTestId(`onboarding-starter-${trustedListingId}`)
await expect(starterLink).toBeVisible()
await expect(starterLink).toContainText(trustedListingName)
await starterLink.click()
await expect(page).toHaveURL(new RegExp(`/community/${trustedListingId}$`))
const starterCard = page.getByTestId(`onboarding-starter-${trustedListingId}`)
await expect(starterCard).toBeVisible()
await expect(starterCard).toContainText(trustedListingName)
await page
.getByTestId(`onboarding-starter-install-${trustedListingId}`)
.click()
await expect(
page.getByRole('button', { name: 'Install', exact: true }),
page.getByTestId(`onboarding-starter-copy-${trustedListingId}`),
).toBeVisible()
await expect(
page.getByTestId(`onboarding-starter-status-${trustedListingId}`),
).toContainText('Installed as')
await expect(
page.getByTestId(`onboarding-starter-copy-${trustedListingId}`),
).toHaveText('Copy prompt')
await starterCard.getByRole('link', { name: trustedListingName }).click()
await expect(page).toHaveURL(new RegExp(`/community/${trustedListingId}$`))

// Removing the mark pulls the listing from onboarding again.
await page.context().clearCookies()
Expand Down
Loading
Loading