Skip to content
Open
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
8 changes: 4 additions & 4 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,21 @@ snapshot tag. They must return to `main` only as schema-v1 packages:
| Permissions | Complete the active rework and add a schema-v1 manifest |

They are not package-manager installable until a canonical package payload,
matching `runneth-package.json`, and `package-index.json` entry are merged.
matching `package.json`, and `package-index.json` entry are merged.

## Package acceptance criteria

Every package pull request must:

1. add one package payload and schema-v1 `runneth-package.json`;
1. add one package payload and schema-v1 `package.json`;
2. add a matching `package-index.json` entry;
3. reference only existing relative paths without symlinks;
4. target a supported root (`agent_apps`, `agent_brain`, `agent_skills`, or
`agent_tools`);
5. pass `node --test scripts/validate-runneth-package-index.mjs`; and
6. receive core engineering approval and the
`runneth-fleet-change-approved` label when it changes an auto-updating
package.
`runneth-fleet-change-approved` label when it changes an auto-installing or
auto-updating package.

Merge package pull requests sequentially because each one updates the central
index.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ and at the immutable `pre-cleanup-2026-07-21-with-aligned-onboarding` tag.
## Package contract

`package-index.json` is the package-manager registry. Each indexed source path
must contain a `runneth-package.json` schema-v1 manifest.
must contain a `package.json` schema-v1 manifest.

The validator enforces:

Expand All @@ -27,12 +27,12 @@ Run the contract tests with:
node --test scripts/validate-runneth-package-index.mjs
```

Changes to auto-updating packages require core engineering approval and the
`runneth-fleet-change-approved` pull-request label.
Changes to auto-installing or auto-updating packages require core engineering
approval and the `runneth-fleet-change-approved` pull-request label.

## Adding a package

1. Add the package payload and its `runneth-package.json`.
1. Add the package payload and its `package.json`.
2. Add one matching entry to `package-index.json`.
3. Run the package contract tests.
4. Open a focused pull request for that package.
Expand Down
25 changes: 23 additions & 2 deletions package-index.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
{
"schemaVersion": 1,
"indexRevision": "initial-empty",
"packages": []
"indexRevision": "connect-my-stack-1.0.4",
"packages": [
{
"id": "connect-my-stack",
"name": "Connect My Stack",
"description": "Guided integration onboarding that scans a customer's website, confirms the detected marketing and adtech stack, checks existing connections, and walks through connecting the remaining tools one at a time.",
"version": "1.0.4",
"packageManagerVersion": 1,
"categories": [
"agent-foundations"
],
"installPolicy": "manual",
"source": {
"type": "github",
"owner": "Motion-Creative",
"repo": "runneth-apps",
"path": "packages/connect-my-stack",
"ref": "main"
},
"updatePolicy": "manual",
"uninstallPolicy": "allowed"
}
]
}
38 changes: 38 additions & 0 deletions packages/connect-my-stack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Connect My Stack

Guided integration onboarding for Runneth. A customer shares their website URL,
Runneth scans the live site, confirms the detected tools in plain language,
checks what is already connected, and walks them through connecting the rest one
at a time.

## Package contents

The schema-v1 package manifest installs two complete skill directories:

- `connect-my-stack`: the guided onboarding flow.
- `tech-stack-scanner`: the scanner instructions, detection engine, and signature
library.

Installing each skill as a directory keeps the scanner's `lib/` files alongside
its `SKILL.md`, so the engine is available on every VM where the package is
installed.

## Installed paths

- `/agent/.agents/skills/connect-my-stack/`
- `/agent/.agents/skills/tech-stack-scanner/`

## Verification

After installation, confirm that the following engine files exist under
`/agent/.agents/skills/tech-stack-scanner/lib/`:

- `scan.js`
- `fetch-capture.mjs`
- `detect.mjs`
- `signatures.json`

## Customer-facing trigger

> Here's my website: [paste URL]. Scan it and tell me what tools I'm running.
> For anything that isn't already connected, help me connect it to Runneth.
32 changes: 32 additions & 0 deletions packages/connect-my-stack/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"schemaVersion": 1,
"id": "connect-my-stack",
"name": "Connect My Stack",
"version": "1.0.4",
"description": "Guided integration onboarding that scans a customer's website, confirms the detected marketing and adtech stack, checks existing connections, and walks through connecting the remaining tools one at a time.",
"installPolicy": "manual",
"updatePolicy": "manual",
"uninstallPolicy": "allowed",
"resources": [
{
"id": "connect-my-stack-skill",
"type": "directory",
"sourcePath": "skills/connect-my-stack",
"target": {
"root": "agent_skills",
"path": "connect-my-stack"
},
"executablePaths": []
},
{
"id": "tech-stack-scanner-skill",
"type": "directory",
"sourcePath": "skills/tech-stack-scanner",
"target": {
"root": "agent_skills",
"path": "tech-stack-scanner"
},
"executablePaths": []
}
]
}
221 changes: 221 additions & 0 deletions packages/connect-my-stack/skills/connect-my-stack/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
---
name: connect-my-stack
description: |
THE onboarding flow for a customer connecting their own tools to Runneth. A customer
shares their website URL and wants to see their stack and connect it. This skill owns
that intent and internally calls the tech-stack-scanner engine to do the detection.
Use this (not tech-stack-scanner) whenever the customer is talking about their OWN
site and connecting their OWN tools. Trigger on: "connect my stack", "scan my site",
"scan my website", "what tools am I running", "set up my integrations", "help me
connect", "help me connect them to Runneth", "connect my tools", or a URL shared with
intent to onboard or connect integrations. If the request is about a competitor's or
prospect's stack instead, that is tech-stack-scanner, not this.
Triggers: "connect my stack", "scan my site", "scan my website", "what tools am I running", "set up my integrations", "help me connect them to Runneth", "connect my tools"
user-invocable: false
---

# Connect My Stack

A guided integration onboarding flow. The customer drops a URL, Runneth scans
their site, confirms what it found, checks what is already connected, and walks
them through connecting the rest one at a time. The customer never sees how
connections work under the hood.

**This skill owns the customer's own-stack onboarding intent.** It uses the
tech-stack-scanner as an engine (Step 2), but this flow governs the whole
interaction. Do not hand the turn to the raw scanner; its raw output is not the
customer-facing answer.

## Non-negotiables (read first)

1. **Confirm before connecting.** Present the detected list, ask what is right and
what is missing, then STOP and wait. Never reveal connection status or start a
connection in the same message as the detected list.
2. **One connection at a time.** Never present more than one connect action or
widget in a single message. One tool, one action, wait for the result.
3. **Never expose scan mechanics.** Do not mention fetch mode, headless browser,
Playwright, HTML pattern matches, network calls, confidence internals, or any
scan plumbing to the customer. Ever.
4. **Marketer language only.** No technical or connection-mechanism terms (see
Language Rules). Ad platforms are "Meta ads", "TikTok ads", never "Pixel".

## References

- Tech stack scanner (engine this flow calls): `/agent/.agents/skills/tech-stack-scanner/SKILL.md`
- Integrations CLI (connection status, connectable-app lookup, registration): `/runneth/skills/integrations-cli/SKILL.md`
- Secret collection (secure key input, never in chat): `/runneth/skills/secret-collection/SKILL.md`

## Language Rules

These apply to every customer-facing message in this flow.

- Speak to a marketer, not a developer. Use everyday language.
- Never use these terms in customer-facing copy: OAuth, Pipedream, API key, secret, token, credential, webhook, client ID, integration type, fetch mode, headless browser, HTML, network request, pixel, tag, insight tag.
- Never show credential key names, internal IDs, account email addresses, connection tier labels, or the scanner's raw category names.
- Name ad platforms the way marketers say them: "Meta ads", "TikTok ads", "Google ads", "LinkedIn ads". Never "Meta Pixel", "TikTok Pixel", "LinkedIn Insight Tag".
- If you need to describe how a connection works, say "connect [tool] to Runneth" or "link your [tool] account." Nothing more technical than that.
- A scan is a fingerprint, not proof. If something is uncertain, say "I think I spotted [tool]" rather than asserting it as fact. Confirm before connecting.

## Step 1: Get the URL

Extract the website URL from the customer's message. If no URL is present, ask
for one. Accept a bare domain or full URL.

## Step 2: Scan the Site (silently)

Run the tech stack scanner on the domain. Follow the scanner skill's Phase 0
through Phase 2: attempt browser capture first, fall back to fetch capture if
the browser is unavailable, then run detection **in customer mode** by passing
`--customer` to `detect.mjs`. Customer mode returns Runneth's canonical
categories and ads-language names already (for example "Meta ads", not "Meta
Pixel") and drops infra noise into "Other". Present its groupings and tool names
as returned. Do not relabel them or reach back for the raw scanner categories.

**Engine check (required).** Before scanning, confirm the scanner's engine files
exist at `/agent/.agents/skills/tech-stack-scanner/lib/` (`fetch-capture.mjs`,
`detect.mjs`, `signatures.json`). If they are missing, STOP, tell the customer
you hit a setup issue on your side, and flag it for the team. Never improvise a
replacement scanner inline.

Do all of this silently. Do not narrate the scan or how it ran. If the scan was
limited in any way, do not explain the mechanism to the customer.

## Step 3: Present Findings for Confirmation (then STOP)

Present the detected tools as a simple, scannable list grouped under the
customer-mode categories (Voice of customer, Email & SMS, Paid channels, etc.).
Use the customer's language for every tool.

**Ad platforms:**
- **Meta and TikTok** are not connected here; their ad data comes through Motion
directly (see Step 4). At most, one optional plain-language aside: "I can also
see you're running Meta ads and TikTok ads. That data comes through Motion
directly, so there's nothing to connect here." Never call them pixels.
- **Google ads and LinkedIn ads** are connectable; include them under "Paid
channels" in ads language.

For each tool, use natural confidence language: name it if confident, or "I think
I spotted [tool]" if not.

Then ask the customer to confirm, and STOP:
"Does this look right? Correct me on anything I got wrong, and add any tools I
missed. Think about the platforms you log into every day, even if they are not on
your website."

**Hard stop.** This message does exactly one job: show the detected list and ask
for confirmation. Do NOT in this message: check connections, list what is already
connected, list what is ready to connect, present a widget, or suggest a first
connection. All of that waits until the customer replies. One decision at a time.

## Step 4: Check What Is Already Connected

Only after the customer confirms the list, check connection status for each tool.

**Use the right check per tool. This matters, getting it wrong reports a
connected tool as not connected:**
- **Native integrations (Slack, Google, Notion, GitHub):** check with their own
tooling (for example `slack memberships list` / `slack membership check`,
`google doctor`, the Notion and GitHub CLIs). NEVER check these with
`integrations status --app <slack|google|notion|github>`, that path errors on
native integrations and will make a connected tool look disconnected.
- **Connected apps (Pipedream):** use `integrations status --app <app>` and
`integrations accounts --app <app>`.
- **Key-based tools:** check whether a stored secret for that service exists.

**Never dead-end a tool the customer named based only on the registry.** Check
the live connectable list first
(`integrations list --with-creative-strategy --query <tool>`). If it is a native
or one-click connected app, it goes in "Ready to connect". If it is not in the
registry, check whether the service offers a usable API. When it does, put it in
"Custom setup" and connect it last through secure setup. Only say a tool cannot
be connected after confirming that it has no usable API. Explain that plainly;
never say "nothing to connect" or "no path" merely because the registry returned
no result.

**Meta ads and TikTok ads** are not part of this flow; their data connects through
Motion's data source settings. Do not put them in "Ready to connect". Google ads
and LinkedIn ads are connectable and are treated as normal tools.

Present up to three short groups: "Already connected" (one line each, do not ask
them to reconnect anything working), "Ready to connect" (native and one-click
apps, excluding Meta/TikTok ads), and "Custom setup" (tools with a usable API but
no registry entry, handled last). Never present a "no path" or dead-end group.

## Step 5: Guide Connections One at a Time

Work through the "Ready to connect" tools one at a time. Silently follow this
priority order when choosing which to offer next, and never name these tiers:

1. Native OAuth integrations (Slack, Google, Notion, GitHub)
2. Pipedream connected apps
3. API key or secret-based tools
4. Custom: tools without a native or one-click connection that have a usable API,
connected through secure setup. Always last.

**One tool, one message, one connect action.** Never present two connect actions
or two widgets at once. Offer the next tool, present its single connect action,
and wait for the result before moving on.

For each tool:

**Before offering the connect:** look up its real requirements with the
integrations CLI (`integrations list --with-creative-strategy --query <app>`) so
you guide accurately, but translate to plain steps ("I'll need you to sign in to
your [tool] account"). Never expose credential mechanics.

**Presenting the connect:** say what the tool is, ask if they want to connect it
now, and present the single appropriate connect action for the surface (native
OAuth connect, Pipedream connect, or secure secret input). Never ask the customer
to paste a key or secret into chat.

**After a successful connection:**
1. Confirm it in one short line.
2. Ask how they use that tool at their company (one natural, casual question).
3. Based on their answer, give a brief, casual "here's what that unlocks" note,
one or two sentences, specific not generic.

**After a failed connection:** say it did not go through in plain language (no
error details). Offer to try again or park it and move on. Never let a failure
block the rest. Circle back to parked tools at the end before closing.

**Between connections:** move to the next tool in the silent priority order. Ask
"Want to keep going with [next tool]?" Keep momentum without rushing.

## Step 6: Close

1. Summarize what is connected now in a short list.
2. List anything still pending (including parked failures); offer to revisit now
or next time.
3. Orient them: they can ask what Runneth can do with any connected tool anytime,
and they can connect more later via the integrations page in Motion or by just
asking in a chat.
4. Suggest one or two concrete first things to try with what was connected,
specific to their tools.

Keep the close short.

## Constraints

- Confirm the detected list first (Step 3) and STOP. Never reveal connection
status, list ready-to-connect, or present a widget in the confirmation message.
- One connection action / widget per message. Never batch multiple connects.
- Never expose scan mechanics (fetch mode, headless browser, HTML matching,
network calls, confidence internals) or connection mechanics (OAuth, Pipedream,
API key, secret, token) to the customer.
- Never show credential key names, internal IDs, email addresses, or the scanner's
raw category names.
- Check native integrations (Slack, Google, Notion, GitHub) with their own tooling,
never with `integrations status --app`. That wrong check is what reports a
connected native tool as "not connected".
- Name ad platforms in ads language. Never "Pixel", "Insight Tag", or similar.
- Meta ads and TikTok ads are never a tool to connect here; at most one optional
aside. Google ads and LinkedIn ads are connectable.
- Never ask for secrets in chat. Always use secure collection.
- Never dead-end a named tool based only on the connectable registry. For a tool
without a registry entry, check for a usable API and use Custom setup when one
exists. Only say it cannot be connected after confirming it has no usable API;
never describe a registry miss as "no path".
- If the scanner engine files are missing, stop and flag it. Never rebuild a
replacement scanner inline.
- Do not ask the customer to reconnect something already connected.
- Keep every message short. The customer is onboarding, not reading a manual.
Loading
Loading