From 35378665e3168bd5de9e0d0766b8afb6cd917f1c Mon Sep 17 00:00:00 2001 From: Gandy2025 Date: Tue, 14 Jul 2026 19:36:59 +0800 Subject: [PATCH 1/3] feat: value-first Connect-GitHub guidance when the GitHub App is missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a `github follow` fails because the org hasn't installed the First Tree GitHub App, the agent's chat message read as a failure + chore full of mechanics jargon it had copied from its own CLI inputs ("wire the PR's activity", "no webhook path", "an org admin can enable it", "I can re-follow") — with no clickable call to action. Rewrite the two instruction-layer surfaces that shape that message so the agent leads with value and a single clickable CTA: - agent-briefing.ts `githubAttentionBlock` (injected into every agent): frame it value first, name the concrete updates that land in chat, offer one clickable ABSOLUTE `https:///settings/github` link resolved from `agent status` (a relative path renders as dead text), and explicitly ban the leaked mechanics words. Stays within the 550-line briefing budget. - first-tree-welcome/SKILL.md launcher line + non-admin branch: same value-first, jargon-free shape; resolve the host from `first-tree agent status` with a graceful "name Settings -> GitHub" fallback; drop the unsafe "~1-minute" claim (connect is a two-step flow that can hit an approval wall); stop pointing non-admins at the admin-only Settings -> GitHub surface. Lock the briefing framing with a deterministic test and bump the welcome skill VERSION per the content-change convention. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/__tests__/agent-briefing.test.ts | 14 +++++++++++++ packages/client/src/runtime/agent-briefing.ts | 12 ++++++----- skills/first-tree-welcome/SKILL.md | 21 ++++++++++++------- skills/first-tree-welcome/VERSION | 2 +- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/packages/client/src/__tests__/agent-briefing.test.ts b/packages/client/src/__tests__/agent-briefing.test.ts index a0471e48e..a80cfec1d 100644 --- a/packages/client/src/__tests__/agent-briefing.test.ts +++ b/packages/client/src/__tests__/agent-briefing.test.ts @@ -421,6 +421,20 @@ describe("buildAgentBriefing — asking humans, GitHub, and CLI overview", () => expect(briefing).not.toContain("`first-tree-github` skill"); }); + it("frames the missing-App follow failure as a value-first Connect CTA, not a chore", () => { + const briefing = buildAgentBriefing(makeOpts()); + // Value first, not an error/chore report. + expect(briefing).toContain("frame it value first, not an error or chore"); + expect(briefing).toContain("brings this PR/issue's live updates"); + // One clickable, ABSOLUTE settings link, with a real host the agent can resolve + // (a bare path or literal placeholder would render as a broken/dead link). + expect(briefing).toContain("https:///settings/github"); + expect(briefing).toContain("agent status"); + // Explicit jargon ban so the agent stops parroting CLI wording. + expect(briefing).toContain('Never say "webhook", "wire"'); + expect(briefing).toContain('"install the App"'); + }); + it("keeps chat metadata rules compact but actionable", () => { const briefing = buildAgentBriefing(makeOpts()); const chatTopic = briefing.slice(briefing.indexOf("## Chat Topic & Description")); diff --git a/packages/client/src/runtime/agent-briefing.ts b/packages/client/src/runtime/agent-briefing.ts index 9bd458911..8c0caa4f1 100644 --- a/packages/client/src/runtime/agent-briefing.ts +++ b/packages/client/src/runtime/agent-briefing.ts @@ -502,11 +502,13 @@ Creating a PR or issue **never** follows it; \`gh pr create\`, curl, GitHub MCP, Skip the follow only when the entity is clearly unrelated to this chat's task. - **If the follow fails because the org has not installed the First Tree - GitHub App**, explain the optional value upgrade in product language: live - PR/issue activity (CI, reviews, merge) can flow back into this chat after an - org admin enables **Settings → GitHub** in the First Tree web app. Do not - expose raw error codes or \`github follow\` mechanics; say the PR/issue still - works either way. + GitHub App**, frame it value first, not an error or chore: connecting GitHub + brings this PR/issue's live updates (comments, reviews, checks, merge status) + into this chat — it still works either way. Offer one clickable **absolute** + \`https:///settings/github\` link — take from + \`${bin} agent status\`, and never emit a bare path or the literal placeholder. + Never say "webhook", "wire", "re-follow", "install the App", or "action + needed". - **Unfollow only when the human explicitly asks to stop tracking** the entity (\`${bin} github unfollow \`). Do not proactively unfollow merely because a PR or Issue completed, merged, or closed. diff --git a/skills/first-tree-welcome/SKILL.md b/skills/first-tree-welcome/SKILL.md index a04d73120..82f0b1cb7 100644 --- a/skills/first-tree-welcome/SKILL.md +++ b/skills/first-tree-welcome/SKILL.md @@ -401,15 +401,20 @@ spawned task may have shown. add App-install guidance. - If tracking is blocked because the First Tree GitHub App is not installed on, or does not cover, the GitHub account/repo that owns the PR, and the human is a - confirmed **admin**, include one launcher-level install/coverage line when you - report that PR result. Keep it tied to the win, for example: "PR is open. To - have CI, review comments, and merge updates flow back here, install or cover - this repo from Settings -> GitHub." Use a product link only when you have a - stable one; otherwise name **Settings -> GitHub**. Do not fabricate raw GitHub - App install URLs. + confirmed **admin**, include one launcher-level line when you report that PR + result. Lead with value, tied to the win, for example: "Want this PR's checks, + reviews, and merge status to land in this chat as they happen? + **[Connect GitHub →](https:///settings/github)** — one-time setup + for the team." Emit a clickable **absolute** `https:///settings/github` + URL, taking `` from the `Server:` value of `first-tree agent status` + (a bare path or the literal placeholder will not render); if you cannot resolve + the host, name **Settings → GitHub** rather than emit a broken link. Do not + fabricate a raw GitHub App install URL. Say value, then the link — and never use + mechanics words ("webhook", "wire", "re-follow", "install the App", "action + needed"). - If the human is an invitee/member or role is unclear, do not route them into - an admin-only install surface. Say an organization admin can enable live PR - updates for this repo if useful. + an admin-only install surface. Say a team admin can turn on live PR updates + (checks, reviews, merge status) for this repo, if useful. - Give this App-install guidance at most once in the onboarding launcher. If the spawned task chat already mentioned the missing App, still include the short launcher-level line; do not repeat a full setup explanation. diff --git a/skills/first-tree-welcome/VERSION b/skills/first-tree-welcome/VERSION index 6085e9465..23aa83906 100644 --- a/skills/first-tree-welcome/VERSION +++ b/skills/first-tree-welcome/VERSION @@ -1 +1 @@ -1.2.1 +1.2.2 From 60ef005da6c73bea3f1d1bcb2e1cc1153dd1ac03 Mon Sep 17 00:00:00 2001 From: Gandy2025 Date: Tue, 14 Jul 2026 19:55:55 +0800 Subject: [PATCH 2/3] fix: address Connect GitHub guidance review feedback --- .../src/__tests__/agent-briefing.test.ts | 31 +++++++++++----- packages/client/src/runtime/agent-briefing.ts | 15 ++++---- skills/first-tree-welcome/SKILL.md | 36 ++++++++++--------- 3 files changed, 49 insertions(+), 33 deletions(-) diff --git a/packages/client/src/__tests__/agent-briefing.test.ts b/packages/client/src/__tests__/agent-briefing.test.ts index a80cfec1d..a66d584e3 100644 --- a/packages/client/src/__tests__/agent-briefing.test.ts +++ b/packages/client/src/__tests__/agent-briefing.test.ts @@ -423,16 +423,31 @@ describe("buildAgentBriefing — asking humans, GitHub, and CLI overview", () => it("frames the missing-App follow failure as a value-first Connect CTA, not a chore", () => { const briefing = buildAgentBriefing(makeOpts()); + const attention = briefing.slice( + briefing.indexOf("## GitHub Entity Attention"), + briefing.indexOf("## Asking Humans"), + ); // Value first, not an error/chore report. - expect(briefing).toContain("frame it value first, not an error or chore"); - expect(briefing).toContain("brings this PR/issue's live updates"); - // One clickable, ABSOLUTE settings link, with a real host the agent can resolve - // (a bare path or literal placeholder would render as a broken/dead link). - expect(briefing).toContain("https:///settings/github"); - expect(briefing).toContain("agent status"); + expect(attention).toContain("frame the optional upgrade value first"); + expect(attention).toContain("Promise only current events"); + expect(attention).toMatch(/for a PR, comments, review activity, new commits,\s+and merge\/close state/); + expect(attention).toMatch(/for an issue,\s+comments and open\/close state/); + expect(attention).not.toContain("checks"); + expect(attention).not.toContain("CI"); + // Admins alone receive the CTA; members and unknown roles stay out of the + // admin-only surface. + expect(attention).toContain("Only when the current human is a confirmed org admin"); + expect(attention).toContain("If the human is a member or their role is unknown"); + // Derive the link from the full Server URL without a scheme-breaking host + // placeholder, while preserving valid self-hosted http origins. + expect(attention).toContain("complete `Server:`"); + expect(attention).toContain("trim its trailing slash, append"); + expect(attention).toContain("preserve its `http://` or `https://` scheme"); + expect(attention).not.toContain("https:///settings/github"); + expect(attention).toContain("If the web origin cannot be resolved"); // Explicit jargon ban so the agent stops parroting CLI wording. - expect(briefing).toContain('Never say "webhook", "wire"'); - expect(briefing).toContain('"install the App"'); + expect(attention).toContain('Never say "webhook", "wire"'); + expect(attention).toContain('"install the App"'); }); it("keeps chat metadata rules compact but actionable", () => { diff --git a/packages/client/src/runtime/agent-briefing.ts b/packages/client/src/runtime/agent-briefing.ts index 8c0caa4f1..a7e991d8b 100644 --- a/packages/client/src/runtime/agent-briefing.ts +++ b/packages/client/src/runtime/agent-briefing.ts @@ -501,14 +501,13 @@ Creating a PR or issue **never** follows it; \`gh pr create\`, curl, GitHub MCP, ${bin} github follow Skip the follow only when the entity is clearly unrelated to this chat's task. -- **If the follow fails because the org has not installed the First Tree - GitHub App**, frame it value first, not an error or chore: connecting GitHub - brings this PR/issue's live updates (comments, reviews, checks, merge status) - into this chat — it still works either way. Offer one clickable **absolute** - \`https:///settings/github\` link — take from - \`${bin} agent status\`, and never emit a bare path or the literal placeholder. - Never say "webhook", "wire", "re-follow", "install the App", or "action - needed". +- **If the follow fails because the org has not installed the First Tree GitHub App**, frame the optional upgrade value first, not as an error or chore. + Promise only current events: for a PR, comments, review activity, new commits, + and merge/close state; for an issue, comments and open/close state. Make clear the PR/issue still works either way. + - Only when the current human is a confirmed org admin, offer one clickable **Connect GitHub** CTA. Build its absolute URL from the complete \`Server:\` value printed by \`${bin} agent status\`: + trim its trailing slash, append \`/settings/github\`, and preserve its \`http://\` or \`https://\` scheme. If the web origin cannot be resolved, name **Settings → GitHub** in words and do not emit a link. + - If the human is a member or their role is unknown, do not send the CTA or route them into admin-only setup; say a team admin can enable live updates. + Never say "webhook", "wire", "re-follow", "install the App", or "action needed". - **Unfollow only when the human explicitly asks to stop tracking** the entity (\`${bin} github unfollow \`). Do not proactively unfollow merely because a PR or Issue completed, merged, or closed. diff --git a/skills/first-tree-welcome/SKILL.md b/skills/first-tree-welcome/SKILL.md index 82f0b1cb7..e482801a2 100644 --- a/skills/first-tree-welcome/SKILL.md +++ b/skills/first-tree-welcome/SKILL.md @@ -1,6 +1,6 @@ --- name: first-tree-welcome -version: 1.2.1 +version: 1.2.2 description: Use for a First Tree onboarding first chat, especially natural opening messages like "welcome aboard", "Please help me get started with First Tree", or "Please help me get settled into this team on First Tree." Also covers the production-scan fix first chat ("fix the launch blockers found by my production readiness scan"). Do not use for dedicated tree setup chats, ordinary chats, PR reviews, repo scans, tree writes, or maintenance. --- @@ -386,11 +386,11 @@ map of what is in flight. ### After a value PR opens: guide App install once -A review-ready PR gives the admin a concrete reason to install or update First -Tree GitHub App coverage: CI results, review comments, and merge state can flow -back into chat. The welcome launcher owns one concise install/coverage guidance -at this moment; do not rely only on the generic PR-following failure text that a -spawned task may have shown. +A review-ready PR gives the admin a concrete reason to connect or update First +Tree GitHub App coverage: comments, review activity, new commits, and merge +state can flow back into chat. The welcome launcher owns one concise +install/coverage guidance at this moment; do not rely only on the generic +PR-following failure text that a spawned task may have shown. - The spawned value task owns following its PR in its own task chat (`first-tree github follow `) and reporting whether live tracking is @@ -402,19 +402,21 @@ spawned task may have shown. - If tracking is blocked because the First Tree GitHub App is not installed on, or does not cover, the GitHub account/repo that owns the PR, and the human is a confirmed **admin**, include one launcher-level line when you report that PR - result. Lead with value, tied to the win, for example: "Want this PR's checks, - reviews, and merge status to land in this chat as they happen? - **[Connect GitHub →](https:///settings/github)** — one-time setup - for the team." Emit a clickable **absolute** `https:///settings/github` - URL, taking `` from the `Server:` value of `first-tree agent status` - (a bare path or the literal placeholder will not render); if you cannot resolve - the host, name **Settings → GitHub** rather than emit a broken link. Do not - fabricate a raw GitHub App install URL. Say value, then the link — and never use - mechanics words ("webhook", "wire", "re-follow", "install the App", "action - needed"). + result. Lead with value, tied to the win: "Want this PR's comments, reviews, + new commits, and merge status to land in this chat as they happen?" Follow it + with one **Connect GitHub →** CTA and say it is a one-time setup for the team. + Make the CTA clickable only after deriving its URL: take the complete `Server:` + URL from `first-tree agent status`, trim its trailing slash, and append + `/settings/github`, preserving the original `http://` or `https://` scheme. + Use that resulting absolute URL directly as the link destination. If the web + origin cannot be resolved, name **Settings → GitHub** in words instead of + emitting a link. Do not fabricate a raw GitHub App install URL. Say value, + then the CTA — and never use mechanics words ("webhook", "wire", "re-follow", + "install the App", "action needed"). - If the human is an invitee/member or role is unclear, do not route them into an admin-only install surface. Say a team admin can turn on live PR updates - (checks, reviews, merge status) for this repo, if useful. + (comments, review activity, new commits, and merge status) for this repo, if + useful. - Give this App-install guidance at most once in the onboarding launcher. If the spawned task chat already mentioned the missing App, still include the short launcher-level line; do not repeat a full setup explanation. From 6bfcd4c2271c391ae103a6fd75721e5b3231e542 Mon Sep 17 00:00:00 2001 From: Gandy2025 Date: Tue, 14 Jul 2026 20:01:56 +0800 Subject: [PATCH 3/3] fix: include repository coverage failures in guidance --- packages/client/src/__tests__/agent-briefing.test.ts | 4 +++- packages/client/src/runtime/agent-briefing.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/client/src/__tests__/agent-briefing.test.ts b/packages/client/src/__tests__/agent-briefing.test.ts index a66d584e3..fa0a43644 100644 --- a/packages/client/src/__tests__/agent-briefing.test.ts +++ b/packages/client/src/__tests__/agent-briefing.test.ts @@ -421,13 +421,15 @@ describe("buildAgentBriefing — asking humans, GitHub, and CLI overview", () => expect(briefing).not.toContain("`first-tree-github` skill"); }); - it("frames the missing-App follow failure as a value-first Connect CTA, not a chore", () => { + it("frames missing-App and repo-coverage failures as a value-first Connect CTA", () => { const briefing = buildAgentBriefing(makeOpts()); const attention = briefing.slice( briefing.indexOf("## GitHub Entity Attention"), briefing.indexOf("## Asking Humans"), ); // Value first, not an error/chore report. + expect(attention).toContain("is not installed on, or does not cover"); + expect(attention).toContain("account/repo that owns the PR/issue"); expect(attention).toContain("frame the optional upgrade value first"); expect(attention).toContain("Promise only current events"); expect(attention).toMatch(/for a PR, comments, review activity, new commits,\s+and merge\/close state/); diff --git a/packages/client/src/runtime/agent-briefing.ts b/packages/client/src/runtime/agent-briefing.ts index a7e991d8b..ec6c09fed 100644 --- a/packages/client/src/runtime/agent-briefing.ts +++ b/packages/client/src/runtime/agent-briefing.ts @@ -501,7 +501,7 @@ Creating a PR or issue **never** follows it; \`gh pr create\`, curl, GitHub MCP, ${bin} github follow Skip the follow only when the entity is clearly unrelated to this chat's task. -- **If the follow fails because the org has not installed the First Tree GitHub App**, frame the optional upgrade value first, not as an error or chore. +- **If the follow fails because the First Tree GitHub App is not installed on, or does not cover, the GitHub account/repo that owns the PR/issue**, frame the optional upgrade value first, not as an error or chore. Promise only current events: for a PR, comments, review activity, new commits, and merge/close state; for an issue, comments and open/close state. Make clear the PR/issue still works either way. - Only when the current human is a confirmed org admin, offer one clickable **Connect GitHub** CTA. Build its absolute URL from the complete \`Server:\` value printed by \`${bin} agent status\`: