the-frame-ai 0.9.6 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/init.js +15 -2
- package/src/languages.js +11 -0
- package/src/update.js +15 -1
- package/src/utils.js +25 -0
- package/templates/commands/frame:build.md +13 -0
- package/templates/commands/frame:debug.md +1 -0
- package/templates/commands/frame:fast.md +13 -0
- package/templates/commands/frame:verify-ui.md +101 -0
- package/templates/commands/frame:wave.md +7 -0
package/package.json
CHANGED
package/src/init.js
CHANGED
|
@@ -10,8 +10,10 @@ import {
|
|
|
10
10
|
writeFile,
|
|
11
11
|
applyVars,
|
|
12
12
|
mergeVscodeSettings,
|
|
13
|
+
mergeClaudeSettings,
|
|
14
|
+
mergeVscodeMcp,
|
|
13
15
|
} from './utils.js';
|
|
14
|
-
import { LANGUAGES, getLanguageInstruction, promptLanguage, promptConfig, promptCopilot } from './languages.js';
|
|
16
|
+
import { LANGUAGES, getLanguageInstruction, promptLanguage, promptConfig, promptCopilot, promptFrontend } from './languages.js';
|
|
15
17
|
import { doctor } from './doctor.js';
|
|
16
18
|
|
|
17
19
|
const PLANNING_DIRS = [
|
|
@@ -85,7 +87,14 @@ export async function init(target, flags = {}) {
|
|
|
85
87
|
const commandCount = readdirSync(commandsSrc).filter((f) => f.endsWith('.md')).length;
|
|
86
88
|
logSuccess(`${commandCount} commands → .claude/commands/`);
|
|
87
89
|
|
|
88
|
-
// 2b.
|
|
90
|
+
// 2b. Frontend: Playwright MCP
|
|
91
|
+
const frontend = await promptFrontend(flags.yes);
|
|
92
|
+
if (frontend) {
|
|
93
|
+
mergeClaudeSettings(join(target, '.claude', 'settings.json'));
|
|
94
|
+
logSuccess(`Playwright MCP → .claude/settings.json`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 2c. Copilot Chat support
|
|
89
98
|
const copilot = await promptCopilot(flags.yes);
|
|
90
99
|
if (copilot) {
|
|
91
100
|
const promptsDest = join(target, '.github', 'prompts');
|
|
@@ -96,7 +105,9 @@ export async function init(target, flags = {}) {
|
|
|
96
105
|
const vscodeDest = join(target, '.vscode');
|
|
97
106
|
ensureDir(vscodeDest);
|
|
98
107
|
mergeVscodeSettings(join(vscodeDest, 'settings.json'));
|
|
108
|
+
if (frontend) mergeVscodeMcp(join(vscodeDest, 'mcp.json'));
|
|
99
109
|
logSuccess(`${commandCount} Copilot prompts → .github/prompts/`);
|
|
110
|
+
if (frontend) logSuccess(`Playwright MCP → .vscode/mcp.json`);
|
|
100
111
|
}
|
|
101
112
|
|
|
102
113
|
// 3. Copy agents and apply quality.commands substitution
|
|
@@ -163,6 +174,7 @@ export async function init(target, flags = {}) {
|
|
|
163
174
|
if (fileExists(configPath)) {
|
|
164
175
|
resolvedConfig.language = language;
|
|
165
176
|
resolvedConfig.copilot = copilot;
|
|
177
|
+
resolvedConfig.frontend = frontend;
|
|
166
178
|
writeFile(configPath, JSON.stringify(resolvedConfig, null, 2));
|
|
167
179
|
}
|
|
168
180
|
|
|
@@ -184,6 +196,7 @@ export async function init(target, flags = {}) {
|
|
|
184
196
|
log(` Planning: files in .planning/`);
|
|
185
197
|
log(` Config: .frame/config.json`);
|
|
186
198
|
if (copilot) log(` Copilot: ${commandCount} prompts in .vscode/`);
|
|
199
|
+
if (frontend) log(` Playwright MCP: .claude/settings.json${copilot ? ' + .vscode/mcp.json' : ''}`);
|
|
187
200
|
log('');
|
|
188
201
|
|
|
189
202
|
// 11. Auto-run doctor
|
package/src/languages.js
CHANGED
|
@@ -140,3 +140,14 @@ export async function promptCopilot(yes = false) {
|
|
|
140
140
|
});
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
|
+
|
|
144
|
+
export async function promptFrontend(yes = false) {
|
|
145
|
+
if (!process.stdin.isTTY || yes) return false;
|
|
146
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
147
|
+
return new Promise((resolve) => {
|
|
148
|
+
rl.question('\n? Is this a frontend project? Adds Playwright MCP for UI verification (y/N): ', (answer) => {
|
|
149
|
+
rl.close();
|
|
150
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
}
|
package/src/update.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { readdirSync, writeFileSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { TEMPLATES_DIR, VERSION, log, logSuccess } from './manifest.js';
|
|
4
|
-
import { copyDir, makeExecutable, fileExists, applyVars, writeFile, ensureDir, mergeVscodeSettings } from './utils.js';
|
|
4
|
+
import { copyDir, makeExecutable, fileExists, applyVars, writeFile, ensureDir, mergeVscodeSettings, mergeClaudeSettings, mergeVscodeMcp } from './utils.js';
|
|
5
|
+
import { promptFrontend } from './languages.js';
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
export async function update(target, flags = {}) {
|
|
@@ -99,6 +100,19 @@ export async function update(target, flags = {}) {
|
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
// 5. Update Playwright MCP (frontend projects)
|
|
104
|
+
if (config.frontend === undefined || config.frontend === false) {
|
|
105
|
+
if (process.stdin.isTTY && !flags.yes) {
|
|
106
|
+
const frontend = await promptFrontend(false);
|
|
107
|
+
config.frontend = frontend;
|
|
108
|
+
writeFileSync(join(target, '.frame', 'config.json'), JSON.stringify(config, null, 2), 'utf-8');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (config.frontend) {
|
|
112
|
+
mergeClaudeSettings(join(target, '.claude', 'settings.json'));
|
|
113
|
+
if (config.copilot) mergeVscodeMcp(join(target, '.vscode', 'mcp.json'));
|
|
114
|
+
}
|
|
115
|
+
|
|
102
116
|
// 5. Write new version
|
|
103
117
|
writeFileSync(join(target, '.frame', '.frame-version'), VERSION, 'utf-8');
|
|
104
118
|
|
package/src/utils.js
CHANGED
|
@@ -63,3 +63,28 @@ export function mergeVscodeSettings(settingsPath) {
|
|
|
63
63
|
settings['chat.promptFiles'] = true;
|
|
64
64
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
|
|
65
65
|
}
|
|
66
|
+
|
|
67
|
+
const PLAYWRIGHT_MCP = {
|
|
68
|
+
command: 'npx',
|
|
69
|
+
args: ['@playwright/mcp@latest'],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export function mergeClaudeSettings(settingsPath) {
|
|
73
|
+
let settings = {};
|
|
74
|
+
if (existsSync(settingsPath)) {
|
|
75
|
+
try { settings = JSON.parse(readFileSync(settingsPath, 'utf-8')); } catch {}
|
|
76
|
+
}
|
|
77
|
+
settings.mcpServers = settings.mcpServers ?? {};
|
|
78
|
+
settings.mcpServers.playwright = PLAYWRIGHT_MCP;
|
|
79
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function mergeVscodeMcp(mcpPath) {
|
|
83
|
+
let config = {};
|
|
84
|
+
if (existsSync(mcpPath)) {
|
|
85
|
+
try { config = JSON.parse(readFileSync(mcpPath, 'utf-8')); } catch {}
|
|
86
|
+
}
|
|
87
|
+
config.servers = config.servers ?? {};
|
|
88
|
+
config.servers.playwright = PLAYWRIGHT_MCP;
|
|
89
|
+
writeFileSync(mcpPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
90
|
+
}
|
|
@@ -140,6 +140,19 @@ If unclosed tasks exist — return and complete them or report to user.
|
|
|
140
140
|
|
|
141
141
|
**D-step**: All checks must pass.
|
|
142
142
|
|
|
143
|
+
### Step 6.5: UI Verification (if UI tasks present)
|
|
144
|
+
|
|
145
|
+
**Detect UI tasks**: any task changed files with `.tsx`, `.vue`, `.css`, `component`, `page`, `layout`, `style`.
|
|
146
|
+
|
|
147
|
+
If UI tasks exist AND Playwright MCP is available:
|
|
148
|
+
1. `browser_navigate: {dev server URL}`
|
|
149
|
+
2. `browser_screenshot`
|
|
150
|
+
3. Compare with spec from `docs/specs/{feature}/spec.md`
|
|
151
|
+
4. **PASS** → proceed to Step 7
|
|
152
|
+
5. **FAIL** → describe problem, fix, re-run quality gates, re-verify
|
|
153
|
+
|
|
154
|
+
If Playwright MCP is not available — skip and note: "UI not verified (no browser tool)".
|
|
155
|
+
|
|
143
156
|
### Step 7: Update STATE.md (COMPLETE)
|
|
144
157
|
|
|
145
158
|
```markdown
|
|
@@ -91,6 +91,7 @@ Update STATE.md: `Current Phase: 3/4`
|
|
|
91
91
|
- **D-step**: Regression test must PASS
|
|
92
92
|
- Run all tests: `{quality.commands.test}`
|
|
93
93
|
- Type check: `{quality.commands.typecheck}`
|
|
94
|
+
- **If UI bug** (visual, layout, component): open browser via Playwright MCP → `browser_navigate` → `browser_screenshot` → confirm the bug is gone visually
|
|
94
95
|
|
|
95
96
|
### Phase 4: Review and Knowledge Capture
|
|
96
97
|
|
|
@@ -45,6 +45,19 @@ Execute:
|
|
|
45
45
|
4. Run quality gates: `{quality.commands.typecheck} && {quality.commands.test} && {quality.commands.lint}`
|
|
46
46
|
5. Git commit: `git add {files} && git commit -m "{type}({scope}): {description}"`
|
|
47
47
|
|
|
48
|
+
### Step 3.5: UI Verification (if UI task)
|
|
49
|
+
|
|
50
|
+
**Detect UI task**: task description or changed files contain `.tsx`, `.vue`, `.css`, `component`, `page`, `layout`, `style`, `UI`, or `interface`.
|
|
51
|
+
|
|
52
|
+
If this is a UI task AND Playwright MCP is available (`browser_navigate` tool exists):
|
|
53
|
+
1. `browser_navigate: {dev server URL from .frame/config.json or ask user}`
|
|
54
|
+
2. `browser_screenshot`
|
|
55
|
+
3. Compare screenshot with task description
|
|
56
|
+
4. **PASS** → continue to Step 4
|
|
57
|
+
5. **FAIL** → describe the problem, return to Step 3 and fix, then re-verify
|
|
58
|
+
|
|
59
|
+
If Playwright MCP is not available — skip this step and note: "UI not verified (no browser tool)".
|
|
60
|
+
|
|
48
61
|
### Step 4: Update STATE.md and wins
|
|
49
62
|
|
|
50
63
|
Update `.planning/STATE.md`:
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# /frame:verify -- Browser UI Verification
|
|
2
|
+
|
|
3
|
+
Verifies UI result in a real browser via Playwright MCP. Use when the agent claims "done" but the interface looks wrong.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
Verify the UI for: **$ARGUMENTS** (if empty — verify the last completed task from STATE.md)
|
|
8
|
+
|
|
9
|
+
### Step 1: Determine what to verify
|
|
10
|
+
|
|
11
|
+
If `$ARGUMENTS` is provided — use it as the verification target.
|
|
12
|
+
Otherwise read `.planning/STATE.md` → find the last completed task/feature.
|
|
13
|
+
|
|
14
|
+
### Step 2: Determine the URL
|
|
15
|
+
|
|
16
|
+
Check in order:
|
|
17
|
+
1. `$ARGUMENTS` contains a URL → use it
|
|
18
|
+
2. `.frame/config.json` → `devServer.url` field
|
|
19
|
+
3. Ask the user: "What URL should I open to verify? (e.g. http://localhost:3000)"
|
|
20
|
+
|
|
21
|
+
### Step 3: Open browser and take screenshot
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
browser_navigate: {url}
|
|
25
|
+
browser_screenshot
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Describe what you see: layout, content, interactive elements.
|
|
29
|
+
|
|
30
|
+
### Step 4: Compare with expected result
|
|
31
|
+
|
|
32
|
+
Compare the screenshot with:
|
|
33
|
+
- The task description from `$ARGUMENTS` or STATE.md
|
|
34
|
+
- The spec from `docs/specs/{feature}/spec.md` (if exists)
|
|
35
|
+
|
|
36
|
+
**Verdict:**
|
|
37
|
+
- **PASS** — the interface matches the expected result → report success
|
|
38
|
+
- **FAIL** — something is wrong → describe exactly what is wrong and what was expected
|
|
39
|
+
|
|
40
|
+
### Step 5: On FAIL — interact to dig deeper
|
|
41
|
+
|
|
42
|
+
If the initial screenshot shows a problem, try to reproduce it:
|
|
43
|
+
```
|
|
44
|
+
browser_click: {element} # click buttons, links
|
|
45
|
+
browser_type: {selector} {text} # fill forms
|
|
46
|
+
browser_screenshot # screenshot after interaction
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Describe the exact problem: what element, what behavior, what was expected.
|
|
50
|
+
|
|
51
|
+
### Step 6: Report
|
|
52
|
+
|
|
53
|
+
**On PASS:**
|
|
54
|
+
```
|
|
55
|
+
✅ UI verified: {what was checked}
|
|
56
|
+
- URL: {url}
|
|
57
|
+
- Result: matches expected behavior
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**On FAIL:**
|
|
61
|
+
```
|
|
62
|
+
❌ UI verification failed: {what was checked}
|
|
63
|
+
- URL: {url}
|
|
64
|
+
- Problem: {exact description}
|
|
65
|
+
- Expected: {what should be}
|
|
66
|
+
- Actual: {what is}
|
|
67
|
+
- Suggested fix: {where to look}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
On FAIL — do NOT auto-fix. Report to the user and wait for a decision:
|
|
71
|
+
- Fix and re-verify → run `/frame:fast {fix description}`, then `/frame:verify` again
|
|
72
|
+
- It's acceptable → mark as known issue
|
|
73
|
+
|
|
74
|
+
## Rules
|
|
75
|
+
|
|
76
|
+
- **Never auto-fix** — verify only, report problems
|
|
77
|
+
- **Be specific** — describe exact elements, not "looks wrong"
|
|
78
|
+
- **Screenshot first** — always take a screenshot before interacting
|
|
79
|
+
- **Check mobile too** — if the project has responsive design, mention it
|
|
80
|
+
|
|
81
|
+
## When to Use
|
|
82
|
+
|
|
83
|
+
- After `/frame:fast`, `/frame:build`, `/frame:wave` on UI tasks
|
|
84
|
+
- When the agent says "done" but visually something is off
|
|
85
|
+
- Before `/frame:ship` for frontend features
|
|
86
|
+
- When debugging visual regressions
|
|
87
|
+
|
|
88
|
+
## Playwright MCP Setup
|
|
89
|
+
|
|
90
|
+
If `browser_navigate` is not available, Playwright MCP is not configured.
|
|
91
|
+
Add to `.claude/settings.json`:
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"mcpServers": {
|
|
95
|
+
"playwright": {
|
|
96
|
+
"command": "npx",
|
|
97
|
+
"args": ["@playwright/mcp@latest"]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
@@ -141,6 +141,13 @@ Repeat Steps 5-6 for each wave until all waves are complete.
|
|
|
141
141
|
git log --oneline -10
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
+
**UI Verification** (if any wave contained UI tasks — files with `.tsx`, `.vue`, `.css`, `component`, `page`, `layout`):
|
|
145
|
+
|
|
146
|
+
If Playwright MCP is available:
|
|
147
|
+
1. `browser_navigate: {dev server URL}`
|
|
148
|
+
2. `browser_screenshot`
|
|
149
|
+
3. Compare with spec — PASS → continue, FAIL → report to user with exact description, do NOT auto-fix
|
|
150
|
+
|
|
144
151
|
Check completeness:
|
|
145
152
|
```bash
|
|
146
153
|
TOTAL=$(grep -c "^### Task" plan.md)
|