@fermindi/pwn-cli 0.1.1 → 0.2.0
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 -21
- package/README.md +265 -251
- package/cli/batch.js +333 -333
- package/cli/codespaces.js +303 -303
- package/cli/index.js +98 -91
- package/cli/inject.js +78 -67
- package/cli/knowledge.js +531 -531
- package/cli/migrate.js +466 -0
- package/cli/notify.js +135 -135
- package/cli/patterns.js +665 -665
- package/cli/status.js +91 -91
- package/cli/validate.js +61 -61
- package/package.json +70 -70
- package/src/core/inject.js +208 -204
- package/src/core/state.js +91 -91
- package/src/core/validate.js +202 -202
- package/src/core/workspace.js +176 -176
- package/src/index.js +20 -20
- package/src/knowledge/gc.js +308 -308
- package/src/knowledge/lifecycle.js +401 -401
- package/src/knowledge/promote.js +364 -364
- package/src/knowledge/references.js +342 -342
- package/src/patterns/matcher.js +218 -218
- package/src/patterns/registry.js +375 -375
- package/src/patterns/triggers.js +423 -423
- package/src/services/batch-service.js +849 -849
- package/src/services/notification-service.js +342 -342
- package/templates/codespaces/devcontainer.json +52 -52
- package/templates/codespaces/setup.sh +70 -70
- package/templates/workspace/.ai/README.md +164 -164
- package/templates/workspace/.ai/agents/README.md +204 -204
- package/templates/workspace/.ai/agents/claude.md +625 -625
- package/templates/workspace/.ai/config/README.md +79 -79
- package/templates/workspace/.ai/config/notifications.template.json +20 -20
- package/templates/workspace/.ai/memory/deadends.md +79 -79
- package/templates/workspace/.ai/memory/decisions.md +58 -58
- package/templates/workspace/.ai/memory/patterns.md +65 -65
- package/templates/workspace/.ai/patterns/backend/README.md +126 -126
- package/templates/workspace/.ai/patterns/frontend/README.md +103 -103
- package/templates/workspace/.ai/patterns/index.md +256 -256
- package/templates/workspace/.ai/patterns/triggers.json +1087 -1087
- package/templates/workspace/.ai/patterns/universal/README.md +141 -141
- package/templates/workspace/.ai/state.template.json +8 -8
- package/templates/workspace/.ai/tasks/active.md +77 -77
- package/templates/workspace/.ai/tasks/backlog.md +95 -95
- package/templates/workspace/.ai/workflows/batch-task.md +356 -356
package/cli/batch.js
CHANGED
|
@@ -1,333 +1,333 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import * as batch from '../src/services/batch-service.js';
|
|
3
|
-
import { hasWorkspace } from '../src/core/state.js';
|
|
4
|
-
|
|
5
|
-
export default async function batchCommand(args = []) {
|
|
6
|
-
// Check for help first (doesn't require workspace)
|
|
7
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
8
|
-
showHelp();
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Check for workspace
|
|
13
|
-
if (!hasWorkspace()) {
|
|
14
|
-
console.log('❌ No PWN workspace found\n');
|
|
15
|
-
console.log(' Run: pwn inject');
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const subcommand = args[0];
|
|
20
|
-
|
|
21
|
-
// Handle subcommands
|
|
22
|
-
if (subcommand === 'status' || args.includes('--status')) {
|
|
23
|
-
return showStatus();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (subcommand === 'config' || args.includes('--config')) {
|
|
27
|
-
return showConfig();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Parse options
|
|
31
|
-
const options = parseOptions(args);
|
|
32
|
-
|
|
33
|
-
// Handle actions
|
|
34
|
-
if (options.resume) {
|
|
35
|
-
return resumeBatch(options);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (options.dryRun) {
|
|
39
|
-
return dryRun(options);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Default: start batch
|
|
43
|
-
return startBatch(options);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Parse command line options
|
|
48
|
-
*/
|
|
49
|
-
function parseOptions(args) {
|
|
50
|
-
const options = {};
|
|
51
|
-
|
|
52
|
-
for (let i = 0; i < args.length; i++) {
|
|
53
|
-
const arg = args[i];
|
|
54
|
-
|
|
55
|
-
if (arg === '--count' || arg === '-n') {
|
|
56
|
-
options.count = parseInt(args[++i], 10);
|
|
57
|
-
} else if (arg === '--dry-run' || arg === '-d') {
|
|
58
|
-
options.dryRun = true;
|
|
59
|
-
} else if (arg === '--priority' || arg === '-p') {
|
|
60
|
-
options.priority = args[++i]?.toLowerCase();
|
|
61
|
-
} else if (arg === '--resume' || arg === '-r') {
|
|
62
|
-
options.resume = true;
|
|
63
|
-
} else if (arg === '--skip') {
|
|
64
|
-
options.skip = true;
|
|
65
|
-
} else if (arg === '--force' || arg === '-f') {
|
|
66
|
-
options.skipGates = true;
|
|
67
|
-
} else if (arg === '--continue') {
|
|
68
|
-
options.continueOnError = true;
|
|
69
|
-
} else if (arg === '--no-commit') {
|
|
70
|
-
options.skipCommit = true;
|
|
71
|
-
} else if (arg === '--no-branch') {
|
|
72
|
-
options.skipBranch = true;
|
|
73
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
74
|
-
showHelp();
|
|
75
|
-
process.exit(0);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return options;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Show batch status
|
|
84
|
-
*/
|
|
85
|
-
function showStatus() {
|
|
86
|
-
const status = batch.getStatus();
|
|
87
|
-
|
|
88
|
-
if (!status.hasWorkspace) {
|
|
89
|
-
console.log('❌ No PWN workspace found');
|
|
90
|
-
process.exit(1);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
console.log('📋 Batch Status\n');
|
|
94
|
-
|
|
95
|
-
// Running state
|
|
96
|
-
if (status.isRunning) {
|
|
97
|
-
console.log(` Status: 🔄 Running`);
|
|
98
|
-
console.log(` Current Task: ${status.currentTask || '(selecting)'}`);
|
|
99
|
-
} else if (status.isPaused) {
|
|
100
|
-
console.log(` Status: ⏸️ Paused`);
|
|
101
|
-
console.log(` Current Task: ${status.currentTask}`);
|
|
102
|
-
console.log(` Reason: ${status.pauseReason}`);
|
|
103
|
-
console.log('\n Resume with: pwn batch --resume');
|
|
104
|
-
console.log(' Skip task: pwn batch --resume --skip');
|
|
105
|
-
} else {
|
|
106
|
-
console.log(' Status: ⏹️ Idle');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Task counts
|
|
110
|
-
console.log('\n📝 Tasks\n');
|
|
111
|
-
console.log(` Active: ${status.tasks.activePending} pending, ${status.tasks.activeCompleted} completed`);
|
|
112
|
-
console.log(` Backlog: ${status.tasks.backlogTotal} total`);
|
|
113
|
-
if (status.tasks.backlogTotal > 0) {
|
|
114
|
-
console.log(` - High: ${status.tasks.backlogHigh}`);
|
|
115
|
-
console.log(` - Medium: ${status.tasks.backlogMedium}`);
|
|
116
|
-
console.log(` - Low: ${status.tasks.backlogLow}`);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Batch history
|
|
120
|
-
if (status.batchState?.completed?.length > 0) {
|
|
121
|
-
console.log('\n✅ Completed this batch\n');
|
|
122
|
-
for (const taskId of status.batchState.completed) {
|
|
123
|
-
console.log(` - ${taskId}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Next task preview
|
|
128
|
-
const nextTask = batch.selectNextTask();
|
|
129
|
-
if (nextTask) {
|
|
130
|
-
console.log('\n🎯 Next Task\n');
|
|
131
|
-
console.log(` ${nextTask.id}: ${nextTask.title}`);
|
|
132
|
-
if (nextTask.priority) {
|
|
133
|
-
console.log(` Priority: ${nextTask.priority}`);
|
|
134
|
-
}
|
|
135
|
-
} else {
|
|
136
|
-
console.log('\n No tasks available in backlog');
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Show batch configuration
|
|
142
|
-
*/
|
|
143
|
-
function showConfig() {
|
|
144
|
-
const config = batch.loadConfig();
|
|
145
|
-
|
|
146
|
-
console.log('⚙️ Batch Configuration\n');
|
|
147
|
-
console.log(` Max Tasks: ${config.max_tasks}`);
|
|
148
|
-
console.log(` Max Duration: ${config.max_duration_hours} hours`);
|
|
149
|
-
console.log(` Selection: ${config.selection_strategy}`);
|
|
150
|
-
console.log(`\n Quality Gates: ${config.quality_gates.join(', ') || '(none)'}`);
|
|
151
|
-
if (config.skip_gates.length > 0) {
|
|
152
|
-
console.log(` Skip Gates: ${config.skip_gates.join(', ')}`);
|
|
153
|
-
}
|
|
154
|
-
console.log(`\n Auto Commit: ${config.auto_commit ? 'Yes' : 'No'}`);
|
|
155
|
-
console.log(` Auto Push: ${config.auto_push ? 'Yes' : 'No'}`);
|
|
156
|
-
console.log(` Create PR: ${config.create_pr ? 'Yes' : 'No'}`);
|
|
157
|
-
console.log(` Branch Format: ${config.branch_format}`);
|
|
158
|
-
console.log(` Commit Format: ${config.commit_format}`);
|
|
159
|
-
console.log(`\n Notify Complete: ${config.notify_on_complete ? 'Yes' : 'No'}`);
|
|
160
|
-
console.log(` Notify Error: ${config.notify_on_error ? 'Yes' : 'No'}`);
|
|
161
|
-
console.log('\n📁 Config location: .ai/state.json (batch_config)');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Dry run - show what would execute
|
|
166
|
-
*/
|
|
167
|
-
async function dryRun(options) {
|
|
168
|
-
console.log('🔍 Dry Run - Preview batch execution\n');
|
|
169
|
-
|
|
170
|
-
const config = batch.loadConfig();
|
|
171
|
-
const count = options.count || config.max_tasks;
|
|
172
|
-
|
|
173
|
-
console.log(` Would execute up to ${count} tasks\n`);
|
|
174
|
-
|
|
175
|
-
// Show tasks that would be selected
|
|
176
|
-
const tasks = [];
|
|
177
|
-
for (let i = 0; i < count; i++) {
|
|
178
|
-
// Note: This is a simplified preview - actual selection happens one at a time
|
|
179
|
-
const task = batch.selectNextTask(process.cwd(), { priority: options.priority });
|
|
180
|
-
if (!task) break;
|
|
181
|
-
|
|
182
|
-
// Skip already seen tasks for preview
|
|
183
|
-
if (tasks.find(t => t.id === task.id)) break;
|
|
184
|
-
tasks.push(task);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (tasks.length === 0) {
|
|
188
|
-
console.log(' No tasks available in backlog');
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
console.log(' Tasks to execute:\n');
|
|
193
|
-
for (let i = 0; i < tasks.length; i++) {
|
|
194
|
-
const task = tasks[i];
|
|
195
|
-
const priority = task.priority ? ` (${task.priority})` : '';
|
|
196
|
-
console.log(` ${i + 1}. ${task.id}: ${task.title}${priority}`);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
console.log('\n Quality gates that would run:');
|
|
200
|
-
for (const gate of config.quality_gates) {
|
|
201
|
-
if (config.skip_gates.includes(gate)) {
|
|
202
|
-
console.log(` ⏭️ ${gate} (skipped)`);
|
|
203
|
-
} else {
|
|
204
|
-
console.log(` ✓ ${gate}`);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (options.skipGates) {
|
|
209
|
-
console.log('\n ⚠️ Gates would be skipped due to --force flag');
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
console.log('\n Run without --dry-run to execute');
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Start batch execution
|
|
217
|
-
*/
|
|
218
|
-
async function startBatch(options) {
|
|
219
|
-
const config = batch.loadConfig();
|
|
220
|
-
const count = options.count || config.max_tasks;
|
|
221
|
-
|
|
222
|
-
console.log('🚀 Starting batch execution\n');
|
|
223
|
-
console.log(` Max tasks: ${count}`);
|
|
224
|
-
console.log(` Quality gates: ${options.skipGates ? 'Skipped' : config.quality_gates.join(', ')}`);
|
|
225
|
-
console.log('');
|
|
226
|
-
|
|
227
|
-
const startTime = Date.now();
|
|
228
|
-
|
|
229
|
-
const result = await batch.startBatch({
|
|
230
|
-
count,
|
|
231
|
-
priority: options.priority,
|
|
232
|
-
skipGates: options.skipGates,
|
|
233
|
-
skipCommit: options.skipCommit,
|
|
234
|
-
skipBranch: options.skipBranch,
|
|
235
|
-
continueOnError: options.continueOnError
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
239
|
-
|
|
240
|
-
console.log('');
|
|
241
|
-
|
|
242
|
-
if (result.completed.length > 0) {
|
|
243
|
-
console.log('✅ Completed tasks:\n');
|
|
244
|
-
for (const task of result.completed) {
|
|
245
|
-
console.log(` - ${task.id}: ${task.title}`);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (result.errors.length > 0) {
|
|
250
|
-
console.log('\n❌ Failed tasks:\n');
|
|
251
|
-
for (const error of result.errors) {
|
|
252
|
-
console.log(` - ${error.id}: ${error.error}`);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
console.log(`\n⏱️ Duration: ${duration}s`);
|
|
257
|
-
console.log(`📊 Summary: ${result.completed.length} completed, ${result.errors.length} failed`);
|
|
258
|
-
|
|
259
|
-
if (!result.success) {
|
|
260
|
-
console.log('\n💡 To resume: pwn batch --resume');
|
|
261
|
-
console.log(' To skip: pwn batch --resume --skip');
|
|
262
|
-
process.exit(1);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Resume paused batch
|
|
268
|
-
*/
|
|
269
|
-
async function resumeBatch(options) {
|
|
270
|
-
const status = batch.getStatus();
|
|
271
|
-
|
|
272
|
-
if (!status.isPaused) {
|
|
273
|
-
console.log('❌ No paused batch to resume\n');
|
|
274
|
-
console.log(' Current status:', status.isRunning ? 'Running' : 'Idle');
|
|
275
|
-
process.exit(1);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
console.log('▶️ Resuming batch execution\n');
|
|
279
|
-
|
|
280
|
-
if (options.skip) {
|
|
281
|
-
console.log(` Skipping task: ${status.currentTask}`);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
console.log('');
|
|
285
|
-
|
|
286
|
-
const result = await batch.resumeBatch({
|
|
287
|
-
skip: options.skip,
|
|
288
|
-
skipGates: options.skipGates
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
if (!result.success) {
|
|
292
|
-
console.log(`❌ ${result.message}`);
|
|
293
|
-
process.exit(1);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
console.log(`\n✅ Batch resumed`);
|
|
297
|
-
console.log(` Completed: ${result.completed?.length || 0} tasks`);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Show help
|
|
302
|
-
*/
|
|
303
|
-
function showHelp() {
|
|
304
|
-
console.log('🚀 PWN Batch Execution\n');
|
|
305
|
-
console.log('Usage: pwn batch [command] [options]\n');
|
|
306
|
-
console.log('Commands:');
|
|
307
|
-
console.log(' (default) Execute next available task(s)');
|
|
308
|
-
console.log(' status Show batch status');
|
|
309
|
-
console.log(' config Show batch configuration\n');
|
|
310
|
-
console.log('Options:');
|
|
311
|
-
console.log(' --count, -n <num> Execute specific number of tasks');
|
|
312
|
-
console.log(' --dry-run, -d Preview what would execute');
|
|
313
|
-
console.log(' --priority, -p <level> Filter by priority (high, medium, low)');
|
|
314
|
-
console.log(' --resume, -r Resume interrupted batch');
|
|
315
|
-
console.log(' --skip Skip current task when resuming');
|
|
316
|
-
console.log(' --force, -f Skip quality gates');
|
|
317
|
-
console.log(' --continue Continue on errors');
|
|
318
|
-
console.log(' --no-commit Skip auto-commit');
|
|
319
|
-
console.log(' --no-branch Skip branch creation');
|
|
320
|
-
console.log(' --help, -h Show this help\n');
|
|
321
|
-
console.log('Examples:');
|
|
322
|
-
console.log(' pwn batch # Execute next task');
|
|
323
|
-
console.log(' pwn batch --count 5 # Execute 5 tasks');
|
|
324
|
-
console.log(' pwn batch --dry-run # Preview execution');
|
|
325
|
-
console.log(' pwn batch --priority high # Only high priority');
|
|
326
|
-
console.log(' pwn batch --resume # Resume paused batch');
|
|
327
|
-
console.log(' pwn batch --resume --skip # Resume, skip current');
|
|
328
|
-
console.log(' pwn batch status # Show status');
|
|
329
|
-
console.log(' pwn batch config # Show configuration\n');
|
|
330
|
-
console.log('Configuration:');
|
|
331
|
-
console.log(' Edit .ai/state.json and add batch_config object.');
|
|
332
|
-
console.log(' See .ai/workflows/batch-task.md for full documentation.');
|
|
333
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as batch from '../src/services/batch-service.js';
|
|
3
|
+
import { hasWorkspace } from '../src/core/state.js';
|
|
4
|
+
|
|
5
|
+
export default async function batchCommand(args = []) {
|
|
6
|
+
// Check for help first (doesn't require workspace)
|
|
7
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
8
|
+
showHelp();
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Check for workspace
|
|
13
|
+
if (!hasWorkspace()) {
|
|
14
|
+
console.log('❌ No PWN workspace found\n');
|
|
15
|
+
console.log(' Run: pwn inject');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const subcommand = args[0];
|
|
20
|
+
|
|
21
|
+
// Handle subcommands
|
|
22
|
+
if (subcommand === 'status' || args.includes('--status')) {
|
|
23
|
+
return showStatus();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (subcommand === 'config' || args.includes('--config')) {
|
|
27
|
+
return showConfig();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Parse options
|
|
31
|
+
const options = parseOptions(args);
|
|
32
|
+
|
|
33
|
+
// Handle actions
|
|
34
|
+
if (options.resume) {
|
|
35
|
+
return resumeBatch(options);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (options.dryRun) {
|
|
39
|
+
return dryRun(options);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Default: start batch
|
|
43
|
+
return startBatch(options);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Parse command line options
|
|
48
|
+
*/
|
|
49
|
+
function parseOptions(args) {
|
|
50
|
+
const options = {};
|
|
51
|
+
|
|
52
|
+
for (let i = 0; i < args.length; i++) {
|
|
53
|
+
const arg = args[i];
|
|
54
|
+
|
|
55
|
+
if (arg === '--count' || arg === '-n') {
|
|
56
|
+
options.count = parseInt(args[++i], 10);
|
|
57
|
+
} else if (arg === '--dry-run' || arg === '-d') {
|
|
58
|
+
options.dryRun = true;
|
|
59
|
+
} else if (arg === '--priority' || arg === '-p') {
|
|
60
|
+
options.priority = args[++i]?.toLowerCase();
|
|
61
|
+
} else if (arg === '--resume' || arg === '-r') {
|
|
62
|
+
options.resume = true;
|
|
63
|
+
} else if (arg === '--skip') {
|
|
64
|
+
options.skip = true;
|
|
65
|
+
} else if (arg === '--force' || arg === '-f') {
|
|
66
|
+
options.skipGates = true;
|
|
67
|
+
} else if (arg === '--continue') {
|
|
68
|
+
options.continueOnError = true;
|
|
69
|
+
} else if (arg === '--no-commit') {
|
|
70
|
+
options.skipCommit = true;
|
|
71
|
+
} else if (arg === '--no-branch') {
|
|
72
|
+
options.skipBranch = true;
|
|
73
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
74
|
+
showHelp();
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return options;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Show batch status
|
|
84
|
+
*/
|
|
85
|
+
function showStatus() {
|
|
86
|
+
const status = batch.getStatus();
|
|
87
|
+
|
|
88
|
+
if (!status.hasWorkspace) {
|
|
89
|
+
console.log('❌ No PWN workspace found');
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log('📋 Batch Status\n');
|
|
94
|
+
|
|
95
|
+
// Running state
|
|
96
|
+
if (status.isRunning) {
|
|
97
|
+
console.log(` Status: 🔄 Running`);
|
|
98
|
+
console.log(` Current Task: ${status.currentTask || '(selecting)'}`);
|
|
99
|
+
} else if (status.isPaused) {
|
|
100
|
+
console.log(` Status: ⏸️ Paused`);
|
|
101
|
+
console.log(` Current Task: ${status.currentTask}`);
|
|
102
|
+
console.log(` Reason: ${status.pauseReason}`);
|
|
103
|
+
console.log('\n Resume with: pwn batch --resume');
|
|
104
|
+
console.log(' Skip task: pwn batch --resume --skip');
|
|
105
|
+
} else {
|
|
106
|
+
console.log(' Status: ⏹️ Idle');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Task counts
|
|
110
|
+
console.log('\n📝 Tasks\n');
|
|
111
|
+
console.log(` Active: ${status.tasks.activePending} pending, ${status.tasks.activeCompleted} completed`);
|
|
112
|
+
console.log(` Backlog: ${status.tasks.backlogTotal} total`);
|
|
113
|
+
if (status.tasks.backlogTotal > 0) {
|
|
114
|
+
console.log(` - High: ${status.tasks.backlogHigh}`);
|
|
115
|
+
console.log(` - Medium: ${status.tasks.backlogMedium}`);
|
|
116
|
+
console.log(` - Low: ${status.tasks.backlogLow}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Batch history
|
|
120
|
+
if (status.batchState?.completed?.length > 0) {
|
|
121
|
+
console.log('\n✅ Completed this batch\n');
|
|
122
|
+
for (const taskId of status.batchState.completed) {
|
|
123
|
+
console.log(` - ${taskId}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Next task preview
|
|
128
|
+
const nextTask = batch.selectNextTask();
|
|
129
|
+
if (nextTask) {
|
|
130
|
+
console.log('\n🎯 Next Task\n');
|
|
131
|
+
console.log(` ${nextTask.id}: ${nextTask.title}`);
|
|
132
|
+
if (nextTask.priority) {
|
|
133
|
+
console.log(` Priority: ${nextTask.priority}`);
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
console.log('\n No tasks available in backlog');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Show batch configuration
|
|
142
|
+
*/
|
|
143
|
+
function showConfig() {
|
|
144
|
+
const config = batch.loadConfig();
|
|
145
|
+
|
|
146
|
+
console.log('⚙️ Batch Configuration\n');
|
|
147
|
+
console.log(` Max Tasks: ${config.max_tasks}`);
|
|
148
|
+
console.log(` Max Duration: ${config.max_duration_hours} hours`);
|
|
149
|
+
console.log(` Selection: ${config.selection_strategy}`);
|
|
150
|
+
console.log(`\n Quality Gates: ${config.quality_gates.join(', ') || '(none)'}`);
|
|
151
|
+
if (config.skip_gates.length > 0) {
|
|
152
|
+
console.log(` Skip Gates: ${config.skip_gates.join(', ')}`);
|
|
153
|
+
}
|
|
154
|
+
console.log(`\n Auto Commit: ${config.auto_commit ? 'Yes' : 'No'}`);
|
|
155
|
+
console.log(` Auto Push: ${config.auto_push ? 'Yes' : 'No'}`);
|
|
156
|
+
console.log(` Create PR: ${config.create_pr ? 'Yes' : 'No'}`);
|
|
157
|
+
console.log(` Branch Format: ${config.branch_format}`);
|
|
158
|
+
console.log(` Commit Format: ${config.commit_format}`);
|
|
159
|
+
console.log(`\n Notify Complete: ${config.notify_on_complete ? 'Yes' : 'No'}`);
|
|
160
|
+
console.log(` Notify Error: ${config.notify_on_error ? 'Yes' : 'No'}`);
|
|
161
|
+
console.log('\n📁 Config location: .ai/state.json (batch_config)');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Dry run - show what would execute
|
|
166
|
+
*/
|
|
167
|
+
async function dryRun(options) {
|
|
168
|
+
console.log('🔍 Dry Run - Preview batch execution\n');
|
|
169
|
+
|
|
170
|
+
const config = batch.loadConfig();
|
|
171
|
+
const count = options.count || config.max_tasks;
|
|
172
|
+
|
|
173
|
+
console.log(` Would execute up to ${count} tasks\n`);
|
|
174
|
+
|
|
175
|
+
// Show tasks that would be selected
|
|
176
|
+
const tasks = [];
|
|
177
|
+
for (let i = 0; i < count; i++) {
|
|
178
|
+
// Note: This is a simplified preview - actual selection happens one at a time
|
|
179
|
+
const task = batch.selectNextTask(process.cwd(), { priority: options.priority });
|
|
180
|
+
if (!task) break;
|
|
181
|
+
|
|
182
|
+
// Skip already seen tasks for preview
|
|
183
|
+
if (tasks.find(t => t.id === task.id)) break;
|
|
184
|
+
tasks.push(task);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (tasks.length === 0) {
|
|
188
|
+
console.log(' No tasks available in backlog');
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log(' Tasks to execute:\n');
|
|
193
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
194
|
+
const task = tasks[i];
|
|
195
|
+
const priority = task.priority ? ` (${task.priority})` : '';
|
|
196
|
+
console.log(` ${i + 1}. ${task.id}: ${task.title}${priority}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log('\n Quality gates that would run:');
|
|
200
|
+
for (const gate of config.quality_gates) {
|
|
201
|
+
if (config.skip_gates.includes(gate)) {
|
|
202
|
+
console.log(` ⏭️ ${gate} (skipped)`);
|
|
203
|
+
} else {
|
|
204
|
+
console.log(` ✓ ${gate}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (options.skipGates) {
|
|
209
|
+
console.log('\n ⚠️ Gates would be skipped due to --force flag');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
console.log('\n Run without --dry-run to execute');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Start batch execution
|
|
217
|
+
*/
|
|
218
|
+
async function startBatch(options) {
|
|
219
|
+
const config = batch.loadConfig();
|
|
220
|
+
const count = options.count || config.max_tasks;
|
|
221
|
+
|
|
222
|
+
console.log('🚀 Starting batch execution\n');
|
|
223
|
+
console.log(` Max tasks: ${count}`);
|
|
224
|
+
console.log(` Quality gates: ${options.skipGates ? 'Skipped' : config.quality_gates.join(', ')}`);
|
|
225
|
+
console.log('');
|
|
226
|
+
|
|
227
|
+
const startTime = Date.now();
|
|
228
|
+
|
|
229
|
+
const result = await batch.startBatch({
|
|
230
|
+
count,
|
|
231
|
+
priority: options.priority,
|
|
232
|
+
skipGates: options.skipGates,
|
|
233
|
+
skipCommit: options.skipCommit,
|
|
234
|
+
skipBranch: options.skipBranch,
|
|
235
|
+
continueOnError: options.continueOnError
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
239
|
+
|
|
240
|
+
console.log('');
|
|
241
|
+
|
|
242
|
+
if (result.completed.length > 0) {
|
|
243
|
+
console.log('✅ Completed tasks:\n');
|
|
244
|
+
for (const task of result.completed) {
|
|
245
|
+
console.log(` - ${task.id}: ${task.title}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (result.errors.length > 0) {
|
|
250
|
+
console.log('\n❌ Failed tasks:\n');
|
|
251
|
+
for (const error of result.errors) {
|
|
252
|
+
console.log(` - ${error.id}: ${error.error}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
console.log(`\n⏱️ Duration: ${duration}s`);
|
|
257
|
+
console.log(`📊 Summary: ${result.completed.length} completed, ${result.errors.length} failed`);
|
|
258
|
+
|
|
259
|
+
if (!result.success) {
|
|
260
|
+
console.log('\n💡 To resume: pwn batch --resume');
|
|
261
|
+
console.log(' To skip: pwn batch --resume --skip');
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Resume paused batch
|
|
268
|
+
*/
|
|
269
|
+
async function resumeBatch(options) {
|
|
270
|
+
const status = batch.getStatus();
|
|
271
|
+
|
|
272
|
+
if (!status.isPaused) {
|
|
273
|
+
console.log('❌ No paused batch to resume\n');
|
|
274
|
+
console.log(' Current status:', status.isRunning ? 'Running' : 'Idle');
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
console.log('▶️ Resuming batch execution\n');
|
|
279
|
+
|
|
280
|
+
if (options.skip) {
|
|
281
|
+
console.log(` Skipping task: ${status.currentTask}`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
console.log('');
|
|
285
|
+
|
|
286
|
+
const result = await batch.resumeBatch({
|
|
287
|
+
skip: options.skip,
|
|
288
|
+
skipGates: options.skipGates
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (!result.success) {
|
|
292
|
+
console.log(`❌ ${result.message}`);
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
console.log(`\n✅ Batch resumed`);
|
|
297
|
+
console.log(` Completed: ${result.completed?.length || 0} tasks`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Show help
|
|
302
|
+
*/
|
|
303
|
+
function showHelp() {
|
|
304
|
+
console.log('🚀 PWN Batch Execution\n');
|
|
305
|
+
console.log('Usage: pwn batch [command] [options]\n');
|
|
306
|
+
console.log('Commands:');
|
|
307
|
+
console.log(' (default) Execute next available task(s)');
|
|
308
|
+
console.log(' status Show batch status');
|
|
309
|
+
console.log(' config Show batch configuration\n');
|
|
310
|
+
console.log('Options:');
|
|
311
|
+
console.log(' --count, -n <num> Execute specific number of tasks');
|
|
312
|
+
console.log(' --dry-run, -d Preview what would execute');
|
|
313
|
+
console.log(' --priority, -p <level> Filter by priority (high, medium, low)');
|
|
314
|
+
console.log(' --resume, -r Resume interrupted batch');
|
|
315
|
+
console.log(' --skip Skip current task when resuming');
|
|
316
|
+
console.log(' --force, -f Skip quality gates');
|
|
317
|
+
console.log(' --continue Continue on errors');
|
|
318
|
+
console.log(' --no-commit Skip auto-commit');
|
|
319
|
+
console.log(' --no-branch Skip branch creation');
|
|
320
|
+
console.log(' --help, -h Show this help\n');
|
|
321
|
+
console.log('Examples:');
|
|
322
|
+
console.log(' pwn batch # Execute next task');
|
|
323
|
+
console.log(' pwn batch --count 5 # Execute 5 tasks');
|
|
324
|
+
console.log(' pwn batch --dry-run # Preview execution');
|
|
325
|
+
console.log(' pwn batch --priority high # Only high priority');
|
|
326
|
+
console.log(' pwn batch --resume # Resume paused batch');
|
|
327
|
+
console.log(' pwn batch --resume --skip # Resume, skip current');
|
|
328
|
+
console.log(' pwn batch status # Show status');
|
|
329
|
+
console.log(' pwn batch config # Show configuration\n');
|
|
330
|
+
console.log('Configuration:');
|
|
331
|
+
console.log(' Edit .ai/state.json and add batch_config object.');
|
|
332
|
+
console.log(' See .ai/workflows/batch-task.md for full documentation.');
|
|
333
|
+
}
|