@agentic15.com/agentic15-claude-zen 4.2.3 → 5.0.4
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/README.md +216 -63
- package/bin/agentic15.js +0 -7
- package/{templates/.claude → framework}/PLAN-SCHEMA.json +240 -240
- package/{templates/.claude → framework}/PROJECT-PLAN-TEMPLATE.json +223 -223
- package/{templates/.claude → framework}/hooks/enforce-plan-template.js +106 -106
- package/{templates/.claude → framework}/settings.json +4 -5
- package/package.json +2 -2
- package/src/cli/AuthCommand.js +201 -195
- package/src/cli/CommitCommand.js +17 -3
- package/src/cli/VisualTestCommand.js +57 -14
- package/src/core/TemplateManager.js +18 -2
- package/src/cli/UpgradeCommand.js +0 -191
- package/templates/.claude/settings.local.json.example +0 -11
- /package/{templates/.claude → framework}/POST-INSTALL.md +0 -0
- /package/{templates/.claude → framework}/hooks/complete-task.js +0 -0
- /package/{templates/.claude → framework}/hooks/require-active-task.js +0 -0
- /package/{templates/.claude → framework}/hooks/session-start-context.js +0 -0
- /package/{templates/.claude → framework}/hooks/start-task.js +0 -0
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"Bash(git log:*)",
|
|
10
10
|
"Bash(git diff:*)",
|
|
11
11
|
"Bash(git branch:*)",
|
|
12
|
+
"Bash(npx agentic15 status:*)",
|
|
12
13
|
"Edit(./Agent/**)",
|
|
13
14
|
"Write(./Agent/**)",
|
|
14
15
|
"Edit(./scripts/**)",
|
|
@@ -31,8 +32,6 @@
|
|
|
31
32
|
"Write(./.env*)",
|
|
32
33
|
"Edit(./.claude/PROJECT-PLAN.json)",
|
|
33
34
|
"Edit(./.claude/settings.json)",
|
|
34
|
-
"Edit(./.claude/hooks/**)",
|
|
35
|
-
"Write(./.claude/hooks/**)",
|
|
36
35
|
"Edit(./.claude/TASK-TRACKER.json)",
|
|
37
36
|
"Bash(agentic15:*)",
|
|
38
37
|
"Bash(npx agentic15:*)",
|
|
@@ -84,7 +83,7 @@
|
|
|
84
83
|
"hooks": [
|
|
85
84
|
{
|
|
86
85
|
"type": "command",
|
|
87
|
-
"command": "node .claude/hooks/session-start-context.js"
|
|
86
|
+
"command": "node node_modules/@agentic15.com/agentic15-claude-zen/framework/hooks/session-start-context.js"
|
|
88
87
|
}
|
|
89
88
|
]
|
|
90
89
|
}
|
|
@@ -95,7 +94,7 @@
|
|
|
95
94
|
"hooks": [
|
|
96
95
|
{
|
|
97
96
|
"type": "command",
|
|
98
|
-
"command": "node .claude/hooks/require-active-task.js"
|
|
97
|
+
"command": "node node_modules/@agentic15.com/agentic15-claude-zen/framework/hooks/require-active-task.js"
|
|
99
98
|
}
|
|
100
99
|
]
|
|
101
100
|
},
|
|
@@ -104,7 +103,7 @@
|
|
|
104
103
|
"hooks": [
|
|
105
104
|
{
|
|
106
105
|
"type": "command",
|
|
107
|
-
"command": "node .claude/hooks/enforce-plan-template.js"
|
|
106
|
+
"command": "node node_modules/@agentic15.com/agentic15-claude-zen/framework/hooks/enforce-plan-template.js"
|
|
108
107
|
}
|
|
109
108
|
]
|
|
110
109
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentic15.com/agentic15-claude-zen",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.4",
|
|
4
4
|
"description": "Structured AI-assisted development framework for Claude Code with enforced quality standards",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"files": [
|
|
45
45
|
"bin/",
|
|
46
46
|
"src/",
|
|
47
|
+
"framework/",
|
|
47
48
|
"templates/",
|
|
48
49
|
"templates/.gitignore",
|
|
49
50
|
"README.md",
|
|
@@ -54,7 +55,6 @@
|
|
|
54
55
|
"node": ">=18.0.0"
|
|
55
56
|
},
|
|
56
57
|
"dependencies": {
|
|
57
|
-
"@octokit/rest": "^20.0.2",
|
|
58
58
|
"commander": "^12.1.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
package/src/cli/AuthCommand.js
CHANGED
|
@@ -1,195 +1,201 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
3
|
-
import { join, dirname } from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
import readline from 'readline';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
console.log('
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (existsSync(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import readline from 'readline';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
export class AuthCommand {
|
|
11
|
+
static async setup() {
|
|
12
|
+
console.log('\n🔐 GitHub Authentication Setup\n');
|
|
13
|
+
|
|
14
|
+
// Step 1: Check if gh CLI is installed
|
|
15
|
+
if (!this.isGhInstalled()) {
|
|
16
|
+
console.log('❌ GitHub CLI (gh) is not installed\n');
|
|
17
|
+
console.log('Please install GitHub CLI first:');
|
|
18
|
+
console.log(' - macOS: brew install gh');
|
|
19
|
+
console.log(' - Windows: winget install --id GitHub.cli');
|
|
20
|
+
console.log(' - Linux: https://github.com/cli/cli/blob/trunk/docs/install_linux.md\n');
|
|
21
|
+
console.log('Or visit: https://cli.github.com/\n');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Step 2: Check if already authenticated with gh
|
|
26
|
+
if (!this.isGhAuthenticated()) {
|
|
27
|
+
console.log('📝 You need to authenticate with GitHub CLI\n');
|
|
28
|
+
console.log('Running: gh auth login\n');
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
execSync('gh auth login', { stdio: 'inherit' });
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.log('\n❌ GitHub authentication failed\n');
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
console.log('✓ Already authenticated with GitHub CLI\n');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Step 3: Display current git configuration
|
|
41
|
+
this.displayGitConfig();
|
|
42
|
+
|
|
43
|
+
// Step 4: Confirm configuration
|
|
44
|
+
const configOk = await this.confirmConfig();
|
|
45
|
+
if (!configOk) {
|
|
46
|
+
this.showConfigCommands();
|
|
47
|
+
console.log('\n❌ Setup cancelled. Please configure git first.\n');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Step 5: Auto-detect owner/repo
|
|
52
|
+
const { owner, repo } = this.detectRepo();
|
|
53
|
+
|
|
54
|
+
// Step 6: Save configuration (no token needed, gh CLI handles auth)
|
|
55
|
+
this.saveConfig(owner, repo);
|
|
56
|
+
|
|
57
|
+
console.log('\n✅ GitHub authentication configured successfully!\n');
|
|
58
|
+
console.log(` Owner: ${owner}`);
|
|
59
|
+
console.log(` Repo: ${repo}`);
|
|
60
|
+
console.log(` Auth: Using gh CLI credentials`);
|
|
61
|
+
console.log(` Config: .claude/settings.local.json\n`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static isGhInstalled() {
|
|
65
|
+
try {
|
|
66
|
+
execSync('gh --version', { stdio: 'pipe' });
|
|
67
|
+
return true;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static isGhAuthenticated() {
|
|
74
|
+
try {
|
|
75
|
+
execSync('gh auth status', { stdio: 'pipe' });
|
|
76
|
+
return true;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static displayGitConfig() {
|
|
83
|
+
console.log('📋 Current Git Configuration:\n');
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
// Global config
|
|
87
|
+
const globalName = execSync('git config --global user.name', { encoding: 'utf-8' }).trim();
|
|
88
|
+
const globalEmail = execSync('git config --global user.email', { encoding: 'utf-8' }).trim();
|
|
89
|
+
|
|
90
|
+
console.log(' Global:');
|
|
91
|
+
console.log(` - user.name: ${globalName || '(not set)'}`);
|
|
92
|
+
console.log(` - user.email: ${globalEmail || '(not set)'}`);
|
|
93
|
+
|
|
94
|
+
// Local config (project-specific)
|
|
95
|
+
try {
|
|
96
|
+
const localName = execSync('git config --local user.name', { encoding: 'utf-8' }).trim();
|
|
97
|
+
const localEmail = execSync('git config --local user.email', { encoding: 'utf-8' }).trim();
|
|
98
|
+
|
|
99
|
+
if (localName || localEmail) {
|
|
100
|
+
console.log('\n Local (this project):');
|
|
101
|
+
console.log(` - user.name: ${localName || '(not set)'}`);
|
|
102
|
+
console.log(` - user.email: ${localEmail || '(not set)'}`);
|
|
103
|
+
}
|
|
104
|
+
} catch (e) {
|
|
105
|
+
// No local config, that's ok
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log('');
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.log(' ⚠️ Unable to read git config. Is git installed?\n');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
static showConfigCommands() {
|
|
115
|
+
console.log('\n💡 To configure git, use these commands:\n');
|
|
116
|
+
console.log(' Global (all projects):');
|
|
117
|
+
console.log(' git config --global user.name "Your Name"');
|
|
118
|
+
console.log(' git config --global user.email "your@email.com"\n');
|
|
119
|
+
console.log(' Local (this project only):');
|
|
120
|
+
console.log(' git config --local user.name "Your Name"');
|
|
121
|
+
console.log(' git config --local user.email "your@email.com"\n');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static async confirmConfig() {
|
|
125
|
+
const rl = readline.createInterface({
|
|
126
|
+
input: process.stdin,
|
|
127
|
+
output: process.stdout
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return new Promise((resolve) => {
|
|
131
|
+
rl.question('Is this configuration correct? (y/n): ', (answer) => {
|
|
132
|
+
rl.close();
|
|
133
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
static detectRepo() {
|
|
140
|
+
try {
|
|
141
|
+
// Get remote URL
|
|
142
|
+
const remoteUrl = execSync('git config --get remote.origin.url', { encoding: 'utf-8' }).trim();
|
|
143
|
+
|
|
144
|
+
// Parse owner/repo from URL
|
|
145
|
+
// Handles: git@github.com:owner/repo.git or https://github.com/owner/repo.git
|
|
146
|
+
const match = remoteUrl.match(/github\.com[:/]([^/]+)\/(.+?)(\.git)?$/);
|
|
147
|
+
|
|
148
|
+
if (match) {
|
|
149
|
+
return {
|
|
150
|
+
owner: match[1],
|
|
151
|
+
repo: match[2]
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
// Fallback if no remote
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Fallback: prompt user
|
|
159
|
+
console.log('\n⚠️ Could not auto-detect repository from git remote.');
|
|
160
|
+
console.log(' Using placeholder values. Update .claude/settings.local.json manually.\n');
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
owner: 'your-github-username',
|
|
164
|
+
repo: 'your-repo-name'
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static saveConfig(owner, repo) {
|
|
169
|
+
const settingsPath = join(process.cwd(), '.claude', 'settings.local.json');
|
|
170
|
+
const settingsDir = dirname(settingsPath);
|
|
171
|
+
|
|
172
|
+
// Ensure directory exists
|
|
173
|
+
if (!existsSync(settingsDir)) {
|
|
174
|
+
mkdirSync(settingsDir, { recursive: true });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Read existing settings or create new
|
|
178
|
+
let settings = {};
|
|
179
|
+
if (existsSync(settingsPath)) {
|
|
180
|
+
try {
|
|
181
|
+
settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
182
|
+
} catch (e) {
|
|
183
|
+
// Invalid JSON, start fresh
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Update GitHub config (no token needed, gh CLI handles auth)
|
|
188
|
+
settings.github = {
|
|
189
|
+
enabled: true,
|
|
190
|
+
autoCreate: false,
|
|
191
|
+
autoUpdate: false,
|
|
192
|
+
autoClose: false,
|
|
193
|
+
owner,
|
|
194
|
+
repo,
|
|
195
|
+
comment: "Authentication handled by gh CLI (no token needed)"
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Write settings
|
|
199
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
200
|
+
}
|
|
201
|
+
}
|
package/src/cli/CommitCommand.js
CHANGED
|
@@ -82,11 +82,25 @@ export class CommitCommand {
|
|
|
82
82
|
// scripts/ might not exist
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// Stage .claude/
|
|
85
|
+
// Stage user-generated .claude/ files (NOT framework files)
|
|
86
|
+
// Stage: ACTIVE-PLAN, plans/, and settings.local.json
|
|
87
|
+
// DO NOT stage: settings.json, POST-INSTALL.md (generated from framework)
|
|
86
88
|
try {
|
|
87
|
-
execSync('git add .claude/', { stdio: 'pipe' });
|
|
89
|
+
execSync('git add .claude/ACTIVE-PLAN', { stdio: 'pipe' });
|
|
88
90
|
} catch (e) {
|
|
89
|
-
//
|
|
91
|
+
// File might not exist
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
execSync('git add .claude/plans/', { stdio: 'pipe' });
|
|
96
|
+
} catch (e) {
|
|
97
|
+
// Directory might not exist
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
execSync('git add .claude/settings.local.json', { stdio: 'pipe' });
|
|
102
|
+
} catch (e) {
|
|
103
|
+
// File might not exist (it's optional)
|
|
90
104
|
}
|
|
91
105
|
|
|
92
106
|
// Stage package.json and package-lock.json if they exist
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
|
-
import { existsSync, writeFileSync, mkdirSync, unlinkSync } from 'fs';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -32,14 +32,23 @@ export class VisualTestCommand {
|
|
|
32
32
|
|
|
33
33
|
try {
|
|
34
34
|
// Run Playwright visual test
|
|
35
|
-
this.runVisualTest(url);
|
|
35
|
+
const generatedFiles = this.runVisualTest(url);
|
|
36
36
|
|
|
37
37
|
console.log('\n✅ Visual test complete');
|
|
38
38
|
console.log(' Screenshots saved to: .claude/visual-test/\n');
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
console.log('
|
|
42
|
-
console.log('
|
|
39
|
+
|
|
40
|
+
// Display all generated files for easy copy-paste
|
|
41
|
+
console.log('📁 Generated Files (copy these paths for Claude):');
|
|
42
|
+
console.log('─'.repeat(60));
|
|
43
|
+
generatedFiles.forEach(file => {
|
|
44
|
+
console.log(file);
|
|
45
|
+
});
|
|
46
|
+
console.log('─'.repeat(60));
|
|
47
|
+
|
|
48
|
+
console.log('\n💡 Next steps:');
|
|
49
|
+
console.log(' 1. Copy the file paths above');
|
|
50
|
+
console.log(' 2. Ask Claude to analyze the visual test results');
|
|
51
|
+
console.log(' 3. Paste the paths when Claude asks for them\n');
|
|
43
52
|
|
|
44
53
|
process.exit(0);
|
|
45
54
|
} catch (error) {
|
|
@@ -68,7 +77,7 @@ export class VisualTestCommand {
|
|
|
68
77
|
static runVisualTest(url) {
|
|
69
78
|
const testScript = `
|
|
70
79
|
const { chromium } = require('playwright');
|
|
71
|
-
const { writeFileSync, mkdirSync } = require('fs');
|
|
80
|
+
const { writeFileSync, mkdirSync, existsSync } = require('fs');
|
|
72
81
|
const { join } = require('path');
|
|
73
82
|
|
|
74
83
|
(async () => {
|
|
@@ -77,6 +86,7 @@ const { join } = require('path');
|
|
|
77
86
|
|
|
78
87
|
const consoleErrors = [];
|
|
79
88
|
const consoleWarnings = [];
|
|
89
|
+
const generatedFiles = [];
|
|
80
90
|
|
|
81
91
|
const browser = await chromium.launch({ headless: true });
|
|
82
92
|
const context = await browser.newContext({
|
|
@@ -116,16 +126,20 @@ const { join } = require('path');
|
|
|
116
126
|
await page.waitForTimeout(2000);
|
|
117
127
|
|
|
118
128
|
// Capture full page screenshot
|
|
129
|
+
const fullpagePath = join(outputDir, 'fullpage.png');
|
|
119
130
|
await page.screenshot({
|
|
120
|
-
path:
|
|
131
|
+
path: fullpagePath,
|
|
121
132
|
fullPage: true
|
|
122
133
|
});
|
|
134
|
+
generatedFiles.push(fullpagePath);
|
|
123
135
|
|
|
124
136
|
// Capture viewport screenshot
|
|
137
|
+
const viewportPath = join(outputDir, 'viewport.png');
|
|
125
138
|
await page.screenshot({
|
|
126
|
-
path:
|
|
139
|
+
path: viewportPath,
|
|
127
140
|
fullPage: false
|
|
128
141
|
});
|
|
142
|
+
generatedFiles.push(viewportPath);
|
|
129
143
|
|
|
130
144
|
// Save console errors
|
|
131
145
|
if (consoleErrors.length > 0) {
|
|
@@ -133,7 +147,9 @@ const { join } = require('path');
|
|
|
133
147
|
\`[\${e.timestamp}] \${e.message}\${e.stack ? '\\n' + e.stack : ''}\`
|
|
134
148
|
).join('\\n\\n');
|
|
135
149
|
|
|
136
|
-
|
|
150
|
+
const errorLogPath = join(outputDir, 'console-errors.log');
|
|
151
|
+
writeFileSync(errorLogPath, errorLog);
|
|
152
|
+
generatedFiles.push(errorLogPath);
|
|
137
153
|
console.log(\`❌ Found \${consoleErrors.length} console errors\`);
|
|
138
154
|
} else {
|
|
139
155
|
console.log('✅ No console errors detected');
|
|
@@ -145,22 +161,36 @@ const { join } = require('path');
|
|
|
145
161
|
\`[\${w.timestamp}] \${w.message}\`
|
|
146
162
|
).join('\\n\\n');
|
|
147
163
|
|
|
148
|
-
|
|
164
|
+
const warningLogPath = join(outputDir, 'console-warnings.log');
|
|
165
|
+
writeFileSync(warningLogPath, warningLog);
|
|
166
|
+
generatedFiles.push(warningLogPath);
|
|
149
167
|
console.log(\`⚠️ Found \${consoleWarnings.length} console warnings\`);
|
|
150
168
|
}
|
|
151
169
|
|
|
152
170
|
// Capture page HTML for debugging
|
|
153
171
|
const html = await page.content();
|
|
154
|
-
|
|
172
|
+
const htmlPath = join(outputDir, 'page.html');
|
|
173
|
+
writeFileSync(htmlPath, html);
|
|
174
|
+
generatedFiles.push(htmlPath);
|
|
175
|
+
|
|
176
|
+
// Write file list for the parent process to read
|
|
177
|
+
const fileListPath = join(outputDir, '.file-list.json');
|
|
178
|
+
writeFileSync(fileListPath, JSON.stringify(generatedFiles, null, 2));
|
|
155
179
|
|
|
156
180
|
} catch (error) {
|
|
157
181
|
console.error('Error during visual test:', error.message);
|
|
158
182
|
|
|
159
183
|
// Try to capture screenshot even on error
|
|
160
184
|
try {
|
|
185
|
+
const errorScreenshotPath = join(outputDir, 'error-screenshot.png');
|
|
161
186
|
await page.screenshot({
|
|
162
|
-
path:
|
|
187
|
+
path: errorScreenshotPath
|
|
163
188
|
});
|
|
189
|
+
generatedFiles.push(errorScreenshotPath);
|
|
190
|
+
|
|
191
|
+
// Write file list even on error
|
|
192
|
+
const fileListPath = join(outputDir, '.file-list.json');
|
|
193
|
+
writeFileSync(fileListPath, JSON.stringify(generatedFiles, null, 2));
|
|
164
194
|
} catch {}
|
|
165
195
|
|
|
166
196
|
throw error;
|
|
@@ -172,6 +202,7 @@ const { join } = require('path');
|
|
|
172
202
|
|
|
173
203
|
// Write test script to temp file and execute
|
|
174
204
|
const tempScriptPath = join(process.cwd(), '.claude', 'temp-visual-test.js');
|
|
205
|
+
const fileListPath = join(process.cwd(), '.claude', 'visual-test', '.file-list.json');
|
|
175
206
|
|
|
176
207
|
mkdirSync(join(process.cwd(), '.claude'), { recursive: true });
|
|
177
208
|
writeFileSync(tempScriptPath, testScript);
|
|
@@ -181,11 +212,23 @@ const { join } = require('path');
|
|
|
181
212
|
stdio: 'inherit',
|
|
182
213
|
cwd: process.cwd()
|
|
183
214
|
});
|
|
215
|
+
|
|
216
|
+
// Read the generated file list
|
|
217
|
+
let generatedFiles = [];
|
|
218
|
+
if (existsSync(fileListPath)) {
|
|
219
|
+
const fileListContent = readFileSync(fileListPath, 'utf-8');
|
|
220
|
+
generatedFiles = JSON.parse(fileListContent);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return generatedFiles;
|
|
184
224
|
} finally {
|
|
185
|
-
// Clean up temp script
|
|
225
|
+
// Clean up temp script and file list
|
|
186
226
|
try {
|
|
187
227
|
unlinkSync(tempScriptPath);
|
|
188
228
|
} catch {}
|
|
229
|
+
try {
|
|
230
|
+
unlinkSync(fileListPath);
|
|
231
|
+
} catch {}
|
|
189
232
|
}
|
|
190
233
|
}
|
|
191
234
|
}
|