@zibby/cli 0.1.5
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/LICENSE +21 -0
- package/README.md +926 -0
- package/bin/zibby.js +266 -0
- package/package.json +65 -0
- package/src/auth/cli-login.js +406 -0
- package/src/commands/analyze-graph.js +334 -0
- package/src/commands/ci-setup.js +65 -0
- package/src/commands/implement.js +664 -0
- package/src/commands/init.js +736 -0
- package/src/commands/list-projects.js +78 -0
- package/src/commands/memory.js +171 -0
- package/src/commands/run.js +926 -0
- package/src/commands/setup-scripts.js +101 -0
- package/src/commands/upload.js +163 -0
- package/src/commands/video.js +30 -0
- package/src/commands/workflow.js +369 -0
- package/src/config/config.js +117 -0
- package/src/config/environments.js +145 -0
- package/src/utils/execution-context.js +25 -0
- package/src/utils/progress-reporter.js +155 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import fetch from 'node-fetch';
|
|
4
|
+
import { getApiUrl } from '../config/environments.js';
|
|
5
|
+
import { getSessionToken, getUserInfo } from '../config/config.js';
|
|
6
|
+
|
|
7
|
+
export async function listProjectsCommand() {
|
|
8
|
+
const spinner = ora('Fetching projects...').start();
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const sessionToken = getSessionToken();
|
|
12
|
+
const userInfo = getUserInfo();
|
|
13
|
+
|
|
14
|
+
if (!sessionToken) {
|
|
15
|
+
spinner.fail('Not logged in');
|
|
16
|
+
console.log(chalk.yellow('\nPlease log in first:'));
|
|
17
|
+
console.log(chalk.gray(' zibby login\n'));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (userInfo) {
|
|
22
|
+
console.log(chalk.gray(`Logged in as: ${userInfo.email}`));
|
|
23
|
+
if (userInfo.account_id) {
|
|
24
|
+
console.log(chalk.gray(`Account ID: ${userInfo.account_id}\n`));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const apiUrl = getApiUrl();
|
|
29
|
+
const response = await fetch(`${apiUrl}/projects`, {
|
|
30
|
+
headers: {
|
|
31
|
+
'Authorization': `Bearer ${sessionToken}`,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
const errorData = await response.json().catch(() => ({}));
|
|
37
|
+
throw new Error(errorData.error || 'Failed to fetch projects');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const data = await response.json();
|
|
41
|
+
const projects = data.projects || [];
|
|
42
|
+
|
|
43
|
+
spinner.stop();
|
|
44
|
+
|
|
45
|
+
if (projects.length === 0) {
|
|
46
|
+
console.log('\nNo projects found');
|
|
47
|
+
console.log('Create a project at: https://zibby.app/projects\n');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const maskToken = (t) => t && t.length > 8 ? `${t.substring(0, 8)}••••` : t || '-';
|
|
52
|
+
|
|
53
|
+
const nameW = Math.max(4, ...projects.map(p => p.name.length));
|
|
54
|
+
const idW = 36;
|
|
55
|
+
const tokenW = 14;
|
|
56
|
+
|
|
57
|
+
const header = ` ${'Name'.padEnd(nameW)} ${'Project ID'.padEnd(idW)} ${'API Token'.padEnd(tokenW)}`;
|
|
58
|
+
const divider = ` ${'─'.repeat(nameW)} ${'─'.repeat(idW)} ${'─'.repeat(tokenW)}`;
|
|
59
|
+
|
|
60
|
+
console.log(`\n Your Projects (${projects.length})\n`);
|
|
61
|
+
console.log(header);
|
|
62
|
+
console.log(divider);
|
|
63
|
+
for (const project of projects) {
|
|
64
|
+
const name = project.name.padEnd(nameW);
|
|
65
|
+
const id = project.projectId.padEnd(idW);
|
|
66
|
+
const token = maskToken(project.apiToken).padEnd(tokenW);
|
|
67
|
+
console.log(` ${name} ${id} ${token}`);
|
|
68
|
+
}
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log('💡 Use the Project ID with --project flag:');
|
|
71
|
+
console.log(' zibby run test-specs/login.txt --project <project-id> --sync\n');
|
|
72
|
+
|
|
73
|
+
} catch (error) {
|
|
74
|
+
spinner.fail('Failed to fetch projects');
|
|
75
|
+
console.error(chalk.red(`\n❌ Error: ${error.message}\n`));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI commands for the test memory database.
|
|
3
|
+
*
|
|
4
|
+
* zibby memory stats — Show database statistics
|
|
5
|
+
* zibby memory reset — Wipe the database
|
|
6
|
+
* zibby memory init — Initialize the database
|
|
7
|
+
*
|
|
8
|
+
* All imports of @zibby/memory are lazy so the CLI doesn't break when the
|
|
9
|
+
* optional package is missing.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
|
|
14
|
+
const INSTALL_HINT = `
|
|
15
|
+
Install @zibby/memory and Dolt to enable test memory:
|
|
16
|
+
|
|
17
|
+
npm install @zibby/memory # add the package
|
|
18
|
+
brew install dolt # macOS
|
|
19
|
+
# or: curl -L https://github.com/dolthub/dolt/releases/latest/download/install.sh | bash
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
async function loadMemory() {
|
|
23
|
+
try {
|
|
24
|
+
return await import('@zibby/memory');
|
|
25
|
+
} catch {
|
|
26
|
+
console.log(chalk.yellow('\n @zibby/memory is not installed.\n'));
|
|
27
|
+
console.log(chalk.white(INSTALL_HINT));
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function memoryStatsCommand() {
|
|
33
|
+
const mem = await loadMemory();
|
|
34
|
+
if (!mem) return;
|
|
35
|
+
|
|
36
|
+
const cwd = process.cwd();
|
|
37
|
+
const stats = mem.getStats(cwd);
|
|
38
|
+
|
|
39
|
+
if (!stats.available) {
|
|
40
|
+
console.log(chalk.red('\n Dolt is not installed.\n'));
|
|
41
|
+
console.log(chalk.white(' Install Dolt to enable test memory:'));
|
|
42
|
+
console.log(chalk.gray(' brew install dolt # macOS'));
|
|
43
|
+
console.log(chalk.gray(' curl -L https://github.com/dolthub/dolt/releases/latest/download/install.sh | bash # Linux\n'));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!stats.initialized) {
|
|
48
|
+
console.log(chalk.yellow('\n Memory database not initialized.\n'));
|
|
49
|
+
console.log(chalk.white(' Run `zibby init` or `zibby memory init` to set it up.\n'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(chalk.bold.cyan('\n Zibby Test Memory\n'));
|
|
54
|
+
console.log(chalk.gray(` Dolt: ${stats.doltVersion}`));
|
|
55
|
+
const sep = chalk.gray(` ${'─'.repeat(40)}`);
|
|
56
|
+
console.log(sep);
|
|
57
|
+
|
|
58
|
+
const c = stats.counts;
|
|
59
|
+
console.log(chalk.white(` Test runs: ${chalk.cyan(c.runs)} (${chalk.green(`${c.passed} passed`)}, ${c.failed > 0 ? chalk.red(`${c.failed} failed`) : chalk.gray(`${c.failed} failed`)})`));
|
|
60
|
+
console.log(chalk.white(` Selectors: ${chalk.cyan(c.selectors)} tracked`));
|
|
61
|
+
console.log(chalk.white(` Pages: ${chalk.cyan(c.pages)} discovered`));
|
|
62
|
+
console.log(chalk.white(` Transitions: ${chalk.cyan(c.transitions)} mapped`));
|
|
63
|
+
console.log(chalk.white(` Insights: ${chalk.cyan(c.insights || 0)} saved`));
|
|
64
|
+
|
|
65
|
+
if (stats.recentRuns.length > 0) {
|
|
66
|
+
console.log(`\n${sep}`);
|
|
67
|
+
console.log(chalk.white(' Recent runs:'));
|
|
68
|
+
for (const r of stats.recentRuns) {
|
|
69
|
+
const icon = r.passed ? chalk.green('✓') : chalk.red('✗');
|
|
70
|
+
const dur = r.duration_ms ? chalk.gray(`${(r.duration_ms / 1000).toFixed(1)}s`) : '';
|
|
71
|
+
console.log(` ${icon} ${chalk.white(r.spec_path)} ${dur}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (stats.topSelectors.length > 0) {
|
|
76
|
+
console.log(`\n${sep}`);
|
|
77
|
+
console.log(chalk.white(' Top selectors:'));
|
|
78
|
+
for (const s of stats.topSelectors) {
|
|
79
|
+
const total = s.success_count + s.failure_count;
|
|
80
|
+
const pct = Math.round((s.success_count / total) * 100);
|
|
81
|
+
console.log(` ${chalk.cyan(s.stable_id || '?')} → ${s.element_desc} (${pct}%, ${total} uses)`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (stats.log) {
|
|
86
|
+
console.log(`\n${sep}`);
|
|
87
|
+
console.log(chalk.white(' Recent commits:'));
|
|
88
|
+
const lines = stats.log.split('\n').filter(l => l.startsWith('commit') || l.trim().startsWith('run ')).slice(0, 10);
|
|
89
|
+
for (const line of lines) {
|
|
90
|
+
console.log(chalk.gray(` ${line.trim()}`));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log('');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function memoryResetCommand(options) {
|
|
98
|
+
const mem = await loadMemory();
|
|
99
|
+
if (!mem) return;
|
|
100
|
+
|
|
101
|
+
const cwd = process.cwd();
|
|
102
|
+
|
|
103
|
+
if (!options.force) {
|
|
104
|
+
console.log(chalk.yellow('\n This will permanently delete the memory database.\n'));
|
|
105
|
+
console.log(chalk.white(' Run with --force to confirm: zibby memory reset --force\n'));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const deleted = mem.resetMemory(cwd);
|
|
110
|
+
if (deleted) {
|
|
111
|
+
console.log(chalk.green('\n Memory database reset.\n'));
|
|
112
|
+
} else {
|
|
113
|
+
console.log(chalk.gray('\n No memory database found.\n'));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function memoryCompactCommand(options) {
|
|
118
|
+
const mem = await loadMemory();
|
|
119
|
+
if (!mem) return;
|
|
120
|
+
|
|
121
|
+
const cwd = process.cwd();
|
|
122
|
+
const stats = mem.getStats(cwd);
|
|
123
|
+
|
|
124
|
+
if (!stats.initialized) {
|
|
125
|
+
console.log(chalk.yellow('\n Memory database not initialized.\n'));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const maxRuns = options.maxRuns || 50;
|
|
130
|
+
const maxAge = options.maxAge || 90;
|
|
131
|
+
|
|
132
|
+
console.log(chalk.white(`\n Compacting memory (keep last ${maxRuns} runs/spec, prune data older than ${maxAge}d)...`));
|
|
133
|
+
|
|
134
|
+
const before = stats.counts;
|
|
135
|
+
const result = mem.compactMemory(cwd, { maxRuns, maxAgeDays: maxAge });
|
|
136
|
+
|
|
137
|
+
if (result.pruned) {
|
|
138
|
+
const after = mem.getStats(cwd).counts;
|
|
139
|
+
const diff = (key) => before[key] - after[key];
|
|
140
|
+
console.log(chalk.green(' Done.'));
|
|
141
|
+
if (diff('runs') > 0) console.log(chalk.gray(` Pruned ${diff('runs')} old runs`));
|
|
142
|
+
if (diff('selectors') > 0) console.log(chalk.gray(` Pruned ${diff('selectors')} stale selectors`));
|
|
143
|
+
if (diff('insights') > 0) console.log(chalk.gray(` Pruned ${diff('insights')} old insights`));
|
|
144
|
+
if (diff('transitions') > 0) console.log(chalk.gray(` Pruned ${diff('transitions')} stale transitions`));
|
|
145
|
+
console.log(chalk.gray(' Dolt GC completed\n'));
|
|
146
|
+
} else {
|
|
147
|
+
console.log(chalk.gray(' Nothing to compact.\n'));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function memoryInitCommand() {
|
|
152
|
+
const mem = await loadMemory();
|
|
153
|
+
if (!mem) return;
|
|
154
|
+
|
|
155
|
+
const cwd = process.cwd();
|
|
156
|
+
|
|
157
|
+
if (!mem.DoltDB.isAvailable()) {
|
|
158
|
+
console.log(chalk.red('\n Dolt is not installed.\n'));
|
|
159
|
+
console.log(chalk.white(' Install Dolt:'));
|
|
160
|
+
console.log(chalk.gray(' brew install dolt # macOS'));
|
|
161
|
+
console.log(chalk.gray(' curl -L https://github.com/dolthub/dolt/releases/latest/download/install.sh | bash # Linux\n'));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const { created, available } = mem.initMemory(cwd);
|
|
166
|
+
if (created) {
|
|
167
|
+
console.log(chalk.green('\n Memory database initialized at .zibby/memory/\n'));
|
|
168
|
+
} else if (available) {
|
|
169
|
+
console.log(chalk.gray('\n Memory database already initialized.\n'));
|
|
170
|
+
}
|
|
171
|
+
}
|