Skip to content

Commit 6ec6037

Browse files
feat: release pr context. (#54)
1 parent 57138c8 commit 6ec6037

7 files changed

Lines changed: 52 additions & 59 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,13 @@ browser acts as the runtime host for render, lint, and typecheck flows.
4141

4242
- GitHub PAT setup and usage: [docs/byot.md](docs/byot.md)
4343

44+
AI chat features remain opt-in behind
45+
`?feature-ai=true`.
46+
4447
## Fine-Grained PAT Quick Setup
4548

46-
For AI/BYOT flows, use a fine-grained GitHub PAT and follow the existing setup guide:
49+
For PR/BYOT and AI chat flows, use a fine-grained GitHub PAT and follow the
50+
existing setup guide:
4751

4852
- Full setup and behavior: [docs/byot.md](docs/byot.md)
4953
- Repository permissions screenshot: [docs/media/byot-repo-perms.png](docs/media/byot-repo-perms.png)

docs/byot.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,25 @@ This guide explains how to create and use a fine-grained GitHub Personal Access
44

55
## What BYOT does in the app
66

7-
When the AI/BYOT feature is enabled, the token is used to:
7+
BYOT controls are available by default. The token is used to:
88

99
- authenticate GitHub API requests
1010
- load repositories where you have write access
1111
- let you choose which repository to work with
12+
- use PR context features (Open PR / Push Commit flows)
1213

13-
As additional AI/PR features roll out, the same token is also used for model and repository operations that require the configured permissions.
14+
When AI chat is enabled, the same token is also used for GitHub Models requests.
1415

1516
## Privacy and storage behavior
1617

1718
- Your token is stored only in your browser `localStorage`.
1819
- The token is never sent to any service except the GitHub endpoints required by the feature.
1920
- You can remove it at any time using the delete button in the BYOT controls.
2021

21-
## Enable the BYOT feature
22+
## Enable AI chat features
2223

23-
Use one of these options:
24+
BYOT/PR controls do not require a feature flag. To enable AI chat features, use one
25+
of these options:
2426

2527
1. Add `?feature-ai=true` to the app URL.
2628
2. Set `localStorage` key `knighted:develop:feature:ai-assistant` to `true`.
@@ -57,10 +59,11 @@ Use either of these scopes depending on your needs:
5759
## Recommended setup flow
5860

5961
1. Create token with the permissions above.
60-
2. Open `@knighted/develop` with `?feature-ai=true`.
62+
2. Open `@knighted/develop`.
6163
3. Paste token into the BYOT input and click add.
6264
4. Verify repository list loads.
6365
5. Select your target repository.
66+
6. Optional: enable AI chat with `?feature-ai=true`.
6467

6568
## Screenshots
6669

docs/next-steps.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,21 @@ Focused follow-up work for `@knighted/develop`.
3131
- Add mode-aware recommendation behavior so the assistant strongly adapts suggestions to current render mode and style mode.
3232
- Add an editor update workflow where the assistant can propose structured edits and the user can apply to Component and Styles editors with explicit confirmation.
3333
- Keep behavior and constraints aligned with current implementation:
34-
- Keep everything behind the existing browser-only AI feature flag.
34+
- Keep AI chat/assistant behavior behind the existing browser-only AI feature flag.
35+
- Keep PR/BYOT controls available by default.
3536
- Preserve BYOT token semantics (localStorage persistence until user deletes).
3637
- Keep CDN-first runtime behavior and existing fallback model.
3738
- Do not add dependencies without explicit approval.
3839
- Remaining Phase 3 mini-spec (agent implementation prompt):
39-
- "Continue Issue #18 in @knighted/develop from the current baseline where PR filename/path groundwork and Open PR flow are already shipped. Implement the two remaining Phase 3 assistant deliverables. (1) Add mode-aware assistant guidance: when collecting AI context, include explicit policy hints derived from render mode and style mode, and ensure recommendations avoid incompatible patterns (for example, avoid React hook/state guidance in DOM mode unless user explicitly asks for React migration). (2) Add assistant-to-editor apply flow: support structured assistant responses that can propose edits for component and/or styles editors; render these as reviewable actions in the chat drawer, require explicit user confirmation to apply, and support a one-step undo for last applied assistant edit per editor. Keep all AI/BYOT behavior behind the existing browser-only AI feature flag and preserve current token/repo persistence semantics. Do not add dependencies. Validate with npm run lint and targeted Playwright tests covering mode-aware recommendation constraints and apply/undo editor actions."
40+
- "Continue Issue #18 in @knighted/develop from the current baseline where PR filename/path groundwork and Open PR flow are already shipped. Implement the two remaining Phase 3 assistant deliverables. (1) Add mode-aware assistant guidance: when collecting AI context, include explicit policy hints derived from render mode and style mode, and ensure recommendations avoid incompatible patterns (for example, avoid React hook/state guidance in DOM mode unless user explicitly asks for React migration). (2) Add assistant-to-editor apply flow: support structured assistant responses that can propose edits for component and/or styles editors; render these as reviewable actions in the chat drawer, require explicit user confirmation to apply, and support a one-step undo for last applied assistant edit per editor. Keep AI chat/assistant behavior behind the existing browser-only AI feature flag, keep PR/BYOT controls available by default, and preserve current token/repo persistence semantics. Do not add dependencies. Validate with npm run lint and targeted Playwright tests covering mode-aware recommendation constraints and apply/undo editor actions."
4041

4142
5. **Phase 2 UX/UI continuation: fixed editor tabs first pass (Component, Styles, App)**
4243
- Continue the tabs/editor UX work with a constrained first implementation that supports exactly three editor tabs: Component, Styles, and App.
4344
- Do not introduce arbitrary/custom tab names in this pass; treat custom naming as future scope after baseline tab behavior is stable.
4445
- Preserve existing runtime behavior and editor content semantics while adding tab switching, active tab indication, and predictable persistence/reset behavior consistent with current app patterns.
4546
- Ensure assistant/editor integration remains compatible with this model (edits should target one of the fixed tabs) without expanding to dynamic tab metadata yet.
4647
- Suggested implementation prompt:
47-
- "Implement Phase 2 UX/UI tab support in @knighted/develop with a fixed first-pass tab model: Component, Styles, and App only (no arbitrary tab names yet). Add a clear tab UI for switching editor panes, preserve existing editor behavior/content wiring, and keep render/lint/typecheck/diagnostics flows working with the selected tab context where relevant. Keep AI/BYOT feature-flag behavior unchanged, maintain CDN-first runtime constraints, and do not add dependencies. Add targeted Playwright coverage for tab switching, default/active tab behavior, and interactions with existing render/style-mode flows. Validate with npm run lint and targeted Playwright tests."
48+
- "Implement Phase 2 UX/UI tab support in @knighted/develop with a fixed first-pass tab model: Component, Styles, and App only (no arbitrary tab names yet). Add a clear tab UI for switching editor panes, preserve existing editor behavior/content wiring, and keep render/lint/typecheck/diagnostics flows working with the selected tab context where relevant. Keep AI chat feature-flag behavior unchanged while keeping PR/BYOT controls available by default, maintain CDN-first runtime constraints, and do not add dependencies. Add targeted Playwright coverage for tab switching, default/active tab behavior, and interactions with existing render/style-mode flows. Validate with npm run lint and targeted Playwright tests."
4849

4950
6. **Document implicit App strict-flow behavior (auto render)**
5051
- Add a short behavior matrix in docs that explains when implicit App wrapping is allowed versus when users must define `App` explicitly.

playwright/github-byot-ai.spec.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,32 @@ import {
1010
waitForAppReady,
1111
} from './helpers/app-test-helpers.js'
1212

13-
test('BYOT controls stay hidden when feature flag is disabled', async ({ page }) => {
13+
test('PR/BYOT controls are visible without feature flag, but chat stays hidden', async ({
14+
page,
15+
}) => {
1416
await waitForAppReady(page)
1517

16-
const byotControls = page.getByRole('group', {
17-
name: 'GitHub controls',
18+
const byotControls = page.getByRole('group', { name: 'GitHub controls' })
19+
const prToggle = page.getByRole('button', {
20+
name: 'Open pull request',
21+
exact: true,
1822
includeHidden: true,
1923
})
20-
await expect(byotControls).toHaveAttribute('hidden', '')
21-
await expect(byotControls).toBeHidden()
24+
await expect(byotControls).toBeVisible()
25+
await expect(page.getByRole('textbox', { name: 'GitHub token' })).toBeVisible()
26+
await expect(page.getByRole('button', { name: 'Add GitHub token' })).toBeVisible()
2227
await expect(page.getByRole('button', { name: 'Chat' })).toBeHidden()
2328
await expect(page.getByRole('heading', { name: 'AI Chat' })).toBeHidden()
24-
await expect(page.getByRole('button', { name: 'Open PR' })).toBeHidden()
25-
await expect(page.getByRole('heading', { name: 'Open Pull Request' })).toBeHidden()
29+
await expect(prToggle).toHaveCount(1)
30+
await expect(prToggle).toBeHidden()
31+
})
32+
33+
test('chat remains hidden without feature flag after token connect', async ({ page }) => {
34+
await waitForAppReady(page)
35+
await connectByotWithSingleRepo(page)
36+
37+
await expect(page.getByRole('button', { name: 'Open pull request' })).toBeVisible()
38+
await expect(page.getByRole('button', { name: 'Chat' })).toBeHidden()
2639
})
2740

2841
test('BYOT controls render when feature flag is enabled by query param', async ({
@@ -31,11 +44,17 @@ test('BYOT controls render when feature flag is enabled by query param', async (
3144
await waitForAppReady(page, `${appEntryPath}?feature-ai=true`)
3245

3346
const byotControls = page.getByRole('group', { name: 'GitHub controls' })
47+
const prToggle = page.getByRole('button', {
48+
name: 'Open pull request',
49+
exact: true,
50+
includeHidden: true,
51+
})
3452
await expect(byotControls).toBeVisible()
3553
await expect(page.getByRole('textbox', { name: 'GitHub token' })).toBeVisible()
3654
await expect(page.getByRole('button', { name: 'Add GitHub token' })).toBeVisible()
3755
await expect(page.getByRole('button', { name: 'Chat' })).toBeHidden()
38-
await expect(page.getByRole('button', { name: 'Open PR' })).toBeHidden()
56+
await expect(prToggle).toHaveCount(1)
57+
await expect(prToggle).toBeHidden()
3958
})
4059

4160
test('GitHub token info panel reflects missing and present token states', async ({

src/app.js

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -224,16 +224,6 @@ const setCompactAiControlsOpen = isOpen => {
224224
return
225225
}
226226

227-
if (!aiAssistantFeatureEnabled) {
228-
compactAiControlsOpen = false
229-
setGitHubTokenInfoOpen(false)
230-
aiControlsToggle.setAttribute('hidden', '')
231-
aiControlsToggle.setAttribute('aria-expanded', 'false')
232-
githubAiControls.removeAttribute('data-compact-open')
233-
githubAiControls.setAttribute('hidden', '')
234-
return
235-
}
236-
237227
aiControlsToggle.removeAttribute('hidden')
238228

239229
if (!isCompactViewport()) {
@@ -663,8 +653,15 @@ const syncAiChatTokenVisibility = token => {
663653
const hasToken = typeof token === 'string' && token.trim().length > 0
664654

665655
if (hasToken) {
666-
aiChatToggle?.removeAttribute('hidden')
656+
if (aiAssistantFeatureEnabled) {
657+
aiChatToggle?.removeAttribute('hidden')
658+
} else {
659+
aiChatToggle?.setAttribute('hidden', '')
660+
aiChatToggle?.setAttribute('aria-expanded', 'false')
661+
}
662+
667663
githubPrToggle?.removeAttribute('hidden')
664+
668665
if (githubAiContextState.activePrContext) {
669666
githubPrContextClose?.removeAttribute('hidden')
670667
} else {
@@ -688,7 +685,6 @@ const syncAiChatTokenVisibility = token => {
688685
}
689686

690687
const byotControls = createGitHubByotControls({
691-
featureEnabled: aiAssistantFeatureEnabled,
692688
controlsRoot: githubAiControls,
693689
tokenInput: githubTokenInput,
694690
tokenInfoButton: githubTokenInfo,
@@ -791,7 +787,6 @@ chatDrawerController = createGitHubChatDrawer({
791787
})
792788

793789
prDrawerController = createGitHubPrDrawer({
794-
featureEnabled: aiAssistantFeatureEnabled,
795790
toggleButton: githubPrToggle,
796791
drawer: githubPrDrawer,
797792
closeButton: githubPrClose,

src/modules/github-byot-controls.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ const createDefaultRepoOption = ({
4848
}
4949

5050
export const createGitHubByotControls = ({
51-
featureEnabled,
5251
controlsRoot,
5352
tokenInput,
5453
tokenInfoButton,
@@ -61,16 +60,6 @@ export const createGitHubByotControls = ({
6160
onTokenChange,
6261
setStatus,
6362
}) => {
64-
if (!featureEnabled) {
65-
controlsRoot?.setAttribute('hidden', '')
66-
return {
67-
getSelectedRepository: () => null,
68-
getWritableRepositories: () => [],
69-
setSelectedRepository: () => false,
70-
getToken: () => null,
71-
}
72-
}
73-
7463
let savedToken = loadGitHubToken()
7564
let currentRepoRequestAbortController = null
7665
let displayingMaskedToken = false

src/modules/github-pr-drawer.js

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,6 @@ const stripTopLevelAppWrapper = async ({ source, getTopLevelDeclarations }) => {
398398
}
399399

400400
export const createGitHubPrDrawer = ({
401-
featureEnabled,
402401
toggleButton,
403402
drawer,
404403
closeButton,
@@ -432,23 +431,6 @@ export const createGitHubPrDrawer = ({
432431
onRestoreRenderMode,
433432
onRestoreStyleMode,
434433
}) => {
435-
if (!featureEnabled) {
436-
toggleButton?.setAttribute('hidden', '')
437-
drawer?.setAttribute('hidden', '')
438-
439-
return {
440-
setOpen: () => {},
441-
isOpen: () => false,
442-
setToken: () => {},
443-
setSelectedRepository: () => {},
444-
getActivePrContext: () => null,
445-
clearActivePrContext: () => {},
446-
closeActivePullRequestOnGitHub: async () => null,
447-
syncRepositories: () => {},
448-
dispose: () => {},
449-
}
450-
}
451-
452434
let open = false
453435
let submitting = false
454436
let pendingAbortController = null

0 commit comments

Comments
 (0)