@fermindi/pwn-cli 0.6.0 → 0.7.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/cli/backlog.js +60 -0
- package/cli/batch.js +101 -14
- package/cli/index.js +7 -29
- package/cli/inject.js +8 -33
- package/cli/update.js +31 -24
- package/package.json +6 -3
- package/src/core/inject.js +18 -39
- package/src/core/state.js +0 -1
- package/src/core/validate.js +1 -3
- package/src/index.js +0 -1
- package/src/services/batch-runner.js +769 -0
- package/src/services/batch-service.js +107 -19
- package/src/ui/backlog-viewer.js +394 -0
- package/templates/workspace/.ai/batch/tasks/.gitkeep +0 -0
- package/cli/codespaces.js +0 -303
- package/cli/migrate.js +0 -466
- package/cli/mode.js +0 -206
- package/cli/notify.js +0 -135
- package/src/services/notification-service.js +0 -342
- package/templates/codespaces/devcontainer.json +0 -52
- package/templates/codespaces/setup.sh +0 -70
- package/templates/workspace/.ai/config/notifications.template.json +0 -20
- package/templates/workspace/.claude/commands/mode.md +0 -104
- package/templates/workspace/.claude/settings.json +0 -24
package/cli/migrate.js
DELETED
|
@@ -1,466 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
import { detectKnownAIFiles } from '../src/core/inject.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Migration strategies for different AI file types
|
|
8
|
-
*/
|
|
9
|
-
const MIGRATION_STRATEGIES = {
|
|
10
|
-
'claude': migrateClaude,
|
|
11
|
-
'cursor': migrateCursor,
|
|
12
|
-
'memory-bank': migrateMemoryBank,
|
|
13
|
-
'copilot': migrateCopilot,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Parse markdown content to extract decisions, patterns, and tasks
|
|
18
|
-
*/
|
|
19
|
-
function parseMarkdownContent(content) {
|
|
20
|
-
const result = {
|
|
21
|
-
decisions: [],
|
|
22
|
-
patterns: [],
|
|
23
|
-
deadends: [],
|
|
24
|
-
tasks: [],
|
|
25
|
-
instructions: [],
|
|
26
|
-
raw: content
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const lines = content.split('\n');
|
|
30
|
-
let currentSection = null;
|
|
31
|
-
let currentItem = [];
|
|
32
|
-
|
|
33
|
-
for (const line of lines) {
|
|
34
|
-
// Detect section headers
|
|
35
|
-
const h2Match = line.match(/^##\s+(.+)/);
|
|
36
|
-
if (h2Match) {
|
|
37
|
-
// Save previous item
|
|
38
|
-
if (currentItem.length > 0) {
|
|
39
|
-
categorizeItem(currentItem.join('\n'), currentSection, result);
|
|
40
|
-
}
|
|
41
|
-
currentSection = h2Match[1].toLowerCase();
|
|
42
|
-
currentItem = [line];
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (currentSection) {
|
|
47
|
-
currentItem.push(line);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Save last item
|
|
52
|
-
if (currentItem.length > 0) {
|
|
53
|
-
categorizeItem(currentItem.join('\n'), currentSection, result);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return result;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Categorize a parsed item into the appropriate bucket
|
|
61
|
-
*/
|
|
62
|
-
function categorizeItem(content, sectionName, result) {
|
|
63
|
-
const lowerContent = content.toLowerCase();
|
|
64
|
-
const lowerSection = sectionName || '';
|
|
65
|
-
|
|
66
|
-
// Decision indicators
|
|
67
|
-
if (lowerSection.includes('decision') ||
|
|
68
|
-
lowerSection.includes('architecture') ||
|
|
69
|
-
lowerSection.includes('tech stack') ||
|
|
70
|
-
lowerContent.includes('we decided') ||
|
|
71
|
-
lowerContent.includes('decision:')) {
|
|
72
|
-
result.decisions.push(content);
|
|
73
|
-
}
|
|
74
|
-
// Dead-end indicators
|
|
75
|
-
else if (lowerSection.includes('avoid') ||
|
|
76
|
-
lowerSection.includes('don\'t') ||
|
|
77
|
-
lowerSection.includes('failed') ||
|
|
78
|
-
lowerSection.includes('dead') ||
|
|
79
|
-
lowerContent.includes('didn\'t work') ||
|
|
80
|
-
lowerContent.includes('don\'t use')) {
|
|
81
|
-
result.deadends.push(content);
|
|
82
|
-
}
|
|
83
|
-
// Task indicators
|
|
84
|
-
else if (lowerSection.includes('task') ||
|
|
85
|
-
lowerSection.includes('todo') ||
|
|
86
|
-
lowerSection.includes('backlog') ||
|
|
87
|
-
lowerSection.includes('current work') ||
|
|
88
|
-
content.includes('- [ ]') ||
|
|
89
|
-
content.includes('- [x]')) {
|
|
90
|
-
result.tasks.push(content);
|
|
91
|
-
}
|
|
92
|
-
// Pattern indicators
|
|
93
|
-
else if (lowerSection.includes('pattern') ||
|
|
94
|
-
lowerSection.includes('convention') ||
|
|
95
|
-
lowerSection.includes('style') ||
|
|
96
|
-
lowerSection.includes('rule')) {
|
|
97
|
-
result.patterns.push(content);
|
|
98
|
-
}
|
|
99
|
-
// Everything else goes to instructions
|
|
100
|
-
else {
|
|
101
|
-
result.instructions.push(content);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Generate DEC-XXX ID based on existing count
|
|
107
|
-
*/
|
|
108
|
-
function generateDecisionId(existingContent) {
|
|
109
|
-
const matches = existingContent.match(/DEC-(\d+)/g) || [];
|
|
110
|
-
const maxId = matches.reduce((max, match) => {
|
|
111
|
-
const num = parseInt(match.replace('DEC-', ''));
|
|
112
|
-
return num > max ? num : max;
|
|
113
|
-
}, 0);
|
|
114
|
-
return `DEC-${String(maxId + 1).padStart(3, '0')}`;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Generate DE-XXX ID based on existing count
|
|
119
|
-
*/
|
|
120
|
-
function generateDeadendId(existingContent) {
|
|
121
|
-
const matches = existingContent.match(/DE-(\d+)/g) || [];
|
|
122
|
-
const maxId = matches.reduce((max, match) => {
|
|
123
|
-
const num = parseInt(match.replace('DE-', ''));
|
|
124
|
-
return num > max ? num : max;
|
|
125
|
-
}, 0);
|
|
126
|
-
return `DE-${String(maxId + 1).padStart(3, '0')}`;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Migrate CLAUDE.md or claude.md
|
|
131
|
-
*/
|
|
132
|
-
function migrateClaude(filePath, cwd) {
|
|
133
|
-
const content = readFileSync(filePath, 'utf8');
|
|
134
|
-
const parsed = parseMarkdownContent(content);
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
source: filePath,
|
|
138
|
-
type: 'claude',
|
|
139
|
-
parsed,
|
|
140
|
-
actions: generateMigrationActions(parsed, cwd)
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Migrate .cursorrules or .cursor/rules
|
|
146
|
-
*/
|
|
147
|
-
function migrateCursor(filePath, cwd) {
|
|
148
|
-
const content = readFileSync(filePath, 'utf8');
|
|
149
|
-
const parsed = parseMarkdownContent(content);
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
source: filePath,
|
|
153
|
-
type: 'cursor',
|
|
154
|
-
parsed,
|
|
155
|
-
actions: generateMigrationActions(parsed, cwd)
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Migrate memory-bank/ directory
|
|
161
|
-
*/
|
|
162
|
-
function migrateMemoryBank(dirPath, cwd) {
|
|
163
|
-
const parsed = {
|
|
164
|
-
decisions: [],
|
|
165
|
-
patterns: [],
|
|
166
|
-
deadends: [],
|
|
167
|
-
tasks: [],
|
|
168
|
-
instructions: [],
|
|
169
|
-
raw: ''
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const fileMapping = {
|
|
173
|
-
'projectbrief.md': 'instructions',
|
|
174
|
-
'productContext.md': 'decisions',
|
|
175
|
-
'systemPatterns.md': 'patterns',
|
|
176
|
-
'techContext.md': 'decisions',
|
|
177
|
-
'activeContext.md': 'tasks',
|
|
178
|
-
'progress.md': 'tasks',
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
try {
|
|
182
|
-
const files = readdirSync(dirPath);
|
|
183
|
-
for (const file of files) {
|
|
184
|
-
const filePath = join(dirPath, file);
|
|
185
|
-
if (!statSync(filePath).isFile()) continue;
|
|
186
|
-
|
|
187
|
-
const content = readFileSync(filePath, 'utf8');
|
|
188
|
-
const category = fileMapping[file] || 'instructions';
|
|
189
|
-
|
|
190
|
-
if (category === 'decisions') {
|
|
191
|
-
parsed.decisions.push(`<!-- From ${file} -->\n${content}`);
|
|
192
|
-
} else if (category === 'patterns') {
|
|
193
|
-
parsed.patterns.push(`<!-- From ${file} -->\n${content}`);
|
|
194
|
-
} else if (category === 'tasks') {
|
|
195
|
-
parsed.tasks.push(`<!-- From ${file} -->\n${content}`);
|
|
196
|
-
} else {
|
|
197
|
-
parsed.instructions.push(`<!-- From ${file} -->\n${content}`);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
} catch (err) {
|
|
201
|
-
console.error(`Error reading memory-bank: ${err.message}`);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return {
|
|
205
|
-
source: dirPath,
|
|
206
|
-
type: 'memory-bank',
|
|
207
|
-
parsed,
|
|
208
|
-
actions: generateMigrationActions(parsed, cwd)
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Migrate GitHub Copilot instructions
|
|
214
|
-
*/
|
|
215
|
-
function migrateCopilot(filePath, cwd) {
|
|
216
|
-
const content = readFileSync(filePath, 'utf8');
|
|
217
|
-
const parsed = parseMarkdownContent(content);
|
|
218
|
-
|
|
219
|
-
return {
|
|
220
|
-
source: filePath,
|
|
221
|
-
type: 'copilot',
|
|
222
|
-
parsed,
|
|
223
|
-
actions: generateMigrationActions(parsed, cwd)
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Generate migration actions based on parsed content
|
|
229
|
-
*/
|
|
230
|
-
function generateMigrationActions(parsed, cwd) {
|
|
231
|
-
const actions = [];
|
|
232
|
-
const today = new Date().toISOString().split('T')[0];
|
|
233
|
-
|
|
234
|
-
// Decisions
|
|
235
|
-
if (parsed.decisions.length > 0) {
|
|
236
|
-
const targetPath = join(cwd, '.ai', 'memory', 'decisions.md');
|
|
237
|
-
let existingContent = '';
|
|
238
|
-
if (existsSync(targetPath)) {
|
|
239
|
-
existingContent = readFileSync(targetPath, 'utf8');
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
let newContent = '';
|
|
243
|
-
for (const decision of parsed.decisions) {
|
|
244
|
-
const id = generateDecisionId(existingContent + newContent);
|
|
245
|
-
newContent += `\n## ${id}: Migrated Decision\n`;
|
|
246
|
-
newContent += `**Date:** ${today}\n`;
|
|
247
|
-
newContent += `**Status:** Active\n`;
|
|
248
|
-
newContent += `**Source:** Migration\n\n`;
|
|
249
|
-
newContent += decision.replace(/^##\s+.+\n/, '') + '\n';
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
actions.push({
|
|
253
|
-
type: 'append',
|
|
254
|
-
target: targetPath,
|
|
255
|
-
content: newContent,
|
|
256
|
-
description: `Add ${parsed.decisions.length} decision(s) to decisions.md`
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Patterns
|
|
261
|
-
if (parsed.patterns.length > 0) {
|
|
262
|
-
const targetPath = join(cwd, '.ai', 'memory', 'patterns.md');
|
|
263
|
-
let newContent = '\n## Migrated Patterns\n\n';
|
|
264
|
-
for (const pattern of parsed.patterns) {
|
|
265
|
-
newContent += pattern.replace(/^##\s+.+\n/, '') + '\n\n';
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
actions.push({
|
|
269
|
-
type: 'append',
|
|
270
|
-
target: targetPath,
|
|
271
|
-
content: newContent,
|
|
272
|
-
description: `Add ${parsed.patterns.length} pattern(s) to patterns.md`
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Dead-ends
|
|
277
|
-
if (parsed.deadends.length > 0) {
|
|
278
|
-
const targetPath = join(cwd, '.ai', 'memory', 'deadends.md');
|
|
279
|
-
let existingContent = '';
|
|
280
|
-
if (existsSync(targetPath)) {
|
|
281
|
-
existingContent = readFileSync(targetPath, 'utf8');
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
let newContent = '';
|
|
285
|
-
for (const deadend of parsed.deadends) {
|
|
286
|
-
const id = generateDeadendId(existingContent + newContent);
|
|
287
|
-
newContent += `\n## ${id}: Migrated Dead-end\n`;
|
|
288
|
-
newContent += `**Date:** ${today}\n`;
|
|
289
|
-
newContent += `**Source:** Migration\n\n`;
|
|
290
|
-
newContent += deadend.replace(/^##\s+.+\n/, '') + '\n';
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
actions.push({
|
|
294
|
-
type: 'append',
|
|
295
|
-
target: targetPath,
|
|
296
|
-
content: newContent,
|
|
297
|
-
description: `Add ${parsed.deadends.length} dead-end(s) to deadends.md`
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// Tasks
|
|
302
|
-
if (parsed.tasks.length > 0) {
|
|
303
|
-
const targetPath = join(cwd, '.ai', 'tasks', 'active.md');
|
|
304
|
-
let newContent = '\n## Migrated Tasks\n\n';
|
|
305
|
-
for (const task of parsed.tasks) {
|
|
306
|
-
newContent += task.replace(/^##\s+.+\n/, '') + '\n';
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
actions.push({
|
|
310
|
-
type: 'append',
|
|
311
|
-
target: targetPath,
|
|
312
|
-
content: newContent,
|
|
313
|
-
description: `Add ${parsed.tasks.length} task section(s) to active.md`
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Instructions (append to claude.md)
|
|
318
|
-
if (parsed.instructions.length > 0) {
|
|
319
|
-
const targetPath = join(cwd, '.ai', 'agents', 'claude.md');
|
|
320
|
-
let newContent = '\n\n---\n\n## Migrated Instructions\n\n';
|
|
321
|
-
for (const instruction of parsed.instructions) {
|
|
322
|
-
newContent += instruction + '\n\n';
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
actions.push({
|
|
326
|
-
type: 'append',
|
|
327
|
-
target: targetPath,
|
|
328
|
-
content: newContent,
|
|
329
|
-
description: `Add ${parsed.instructions.length} instruction section(s) to claude.md`
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
return actions;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Execute migration actions
|
|
338
|
-
*/
|
|
339
|
-
function executeMigrationActions(actions, dryRun = false) {
|
|
340
|
-
const results = [];
|
|
341
|
-
|
|
342
|
-
for (const action of actions) {
|
|
343
|
-
if (dryRun) {
|
|
344
|
-
results.push({ ...action, status: 'would-execute' });
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
try {
|
|
349
|
-
if (action.type === 'append') {
|
|
350
|
-
let existing = '';
|
|
351
|
-
if (existsSync(action.target)) {
|
|
352
|
-
existing = readFileSync(action.target, 'utf8');
|
|
353
|
-
}
|
|
354
|
-
writeFileSync(action.target, existing + action.content);
|
|
355
|
-
results.push({ ...action, status: 'success' });
|
|
356
|
-
}
|
|
357
|
-
} catch (err) {
|
|
358
|
-
results.push({ ...action, status: 'error', error: err.message });
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
return results;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Main migrate command
|
|
367
|
-
*/
|
|
368
|
-
export default async function migrateCommand(args = []) {
|
|
369
|
-
const dryRun = args.includes('--dry-run');
|
|
370
|
-
const cwd = process.cwd();
|
|
371
|
-
|
|
372
|
-
console.log('🔄 PWN Migration\n');
|
|
373
|
-
|
|
374
|
-
// Check if .ai/ exists
|
|
375
|
-
if (!existsSync(join(cwd, '.ai'))) {
|
|
376
|
-
console.log('❌ No .ai/ directory found.');
|
|
377
|
-
console.log(' Run "pwn inject" first to create the PWN workspace.\n');
|
|
378
|
-
process.exit(1);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Detect migratable files
|
|
382
|
-
const detected = detectKnownAIFiles(cwd);
|
|
383
|
-
const migratable = detected.filter(f => f.migratable);
|
|
384
|
-
|
|
385
|
-
if (migratable.length === 0) {
|
|
386
|
-
console.log('✅ No migratable AI files detected.\n');
|
|
387
|
-
console.log('Supported sources:');
|
|
388
|
-
console.log(' - CLAUDE.md / claude.md');
|
|
389
|
-
console.log(' - .cursorrules / .cursor/rules');
|
|
390
|
-
console.log(' - memory-bank/');
|
|
391
|
-
console.log(' - .github/copilot-instructions.md\n');
|
|
392
|
-
process.exit(0);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
console.log(`📄 Found ${migratable.length} migratable source(s):\n`);
|
|
396
|
-
for (const file of migratable) {
|
|
397
|
-
console.log(` ${file.file} (${file.type})`);
|
|
398
|
-
}
|
|
399
|
-
console.log('');
|
|
400
|
-
|
|
401
|
-
// Process each file
|
|
402
|
-
const allActions = [];
|
|
403
|
-
|
|
404
|
-
for (const file of migratable) {
|
|
405
|
-
const strategy = MIGRATION_STRATEGIES[file.type];
|
|
406
|
-
if (!strategy) {
|
|
407
|
-
console.log(` ⚠️ No migration strategy for ${file.type}`);
|
|
408
|
-
continue;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
console.log(`📦 Analyzing ${file.file}...`);
|
|
412
|
-
const result = strategy(file.path, cwd);
|
|
413
|
-
|
|
414
|
-
console.log(` Found: ${result.parsed.decisions.length} decisions, ${result.parsed.patterns.length} patterns, ${result.parsed.deadends.length} dead-ends, ${result.parsed.tasks.length} tasks, ${result.parsed.instructions.length} instructions`);
|
|
415
|
-
|
|
416
|
-
allActions.push(...result.actions);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
if (allActions.length === 0) {
|
|
420
|
-
console.log('\n✅ Nothing to migrate (content may already be structured).\n');
|
|
421
|
-
process.exit(0);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
console.log(`\n📋 Migration plan (${allActions.length} actions):\n`);
|
|
425
|
-
for (const action of allActions) {
|
|
426
|
-
const targetShort = action.target.replace(cwd, '.');
|
|
427
|
-
console.log(` ${action.type}: ${targetShort}`);
|
|
428
|
-
console.log(` → ${action.description}`);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (dryRun) {
|
|
432
|
-
console.log('\n🔍 Dry run - no changes made.');
|
|
433
|
-
console.log(' Remove --dry-run to execute migration.\n');
|
|
434
|
-
process.exit(0);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
console.log('\n🚀 Executing migration...\n');
|
|
438
|
-
const results = executeMigrationActions(allActions);
|
|
439
|
-
|
|
440
|
-
let successCount = 0;
|
|
441
|
-
let errorCount = 0;
|
|
442
|
-
|
|
443
|
-
for (const result of results) {
|
|
444
|
-
const targetShort = result.target.replace(cwd, '.');
|
|
445
|
-
if (result.status === 'success') {
|
|
446
|
-
console.log(` ✅ ${targetShort}`);
|
|
447
|
-
successCount++;
|
|
448
|
-
} else {
|
|
449
|
-
console.log(` ❌ ${targetShort}: ${result.error}`);
|
|
450
|
-
errorCount++;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
console.log(`\n📊 Migration complete: ${successCount} succeeded, ${errorCount} failed\n`);
|
|
455
|
-
|
|
456
|
-
if (successCount > 0) {
|
|
457
|
-
console.log('💡 Next steps:');
|
|
458
|
-
console.log(' 1. Review migrated content in .ai/memory/');
|
|
459
|
-
console.log(' 2. Adjust DEC-XXX and DE-XXX entries as needed');
|
|
460
|
-
console.log(' 3. Backup and remove original files:\n');
|
|
461
|
-
for (const file of migratable) {
|
|
462
|
-
console.log(` mv ${file.file} .ai-migration-backup/`);
|
|
463
|
-
}
|
|
464
|
-
console.log('');
|
|
465
|
-
}
|
|
466
|
-
}
|
package/cli/mode.js
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
hasWorkspace,
|
|
4
|
-
getState,
|
|
5
|
-
updateState,
|
|
6
|
-
getBatchConfig,
|
|
7
|
-
updateBatchConfig,
|
|
8
|
-
DEFAULT_BATCH_CONFIG
|
|
9
|
-
} from '../src/core/state.js';
|
|
10
|
-
|
|
11
|
-
export default async function modeCommand(args = []) {
|
|
12
|
-
// Check workspace
|
|
13
|
-
if (!hasWorkspace()) {
|
|
14
|
-
console.log('❌ No PWN workspace found');
|
|
15
|
-
console.log(' Run: pwn inject');
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const subcommand = args[0];
|
|
20
|
-
const subArgs = args.slice(1);
|
|
21
|
-
|
|
22
|
-
// No arguments: show current mode
|
|
23
|
-
if (!subcommand) {
|
|
24
|
-
return showCurrentMode();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Subcommands
|
|
28
|
-
switch (subcommand) {
|
|
29
|
-
case 'interactive':
|
|
30
|
-
return setInteractiveMode();
|
|
31
|
-
case 'batch':
|
|
32
|
-
return setBatchMode(subArgs);
|
|
33
|
-
default:
|
|
34
|
-
console.log(`❌ Unknown mode: ${subcommand}`);
|
|
35
|
-
console.log(' Valid modes: interactive, batch');
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function showCurrentMode() {
|
|
41
|
-
const state = getState();
|
|
42
|
-
const mode = state?.session_mode || 'interactive';
|
|
43
|
-
const config = getBatchConfig();
|
|
44
|
-
|
|
45
|
-
console.log(`📋 Session Mode: ${mode}\n`);
|
|
46
|
-
console.log('Batch config:');
|
|
47
|
-
printConfig(config);
|
|
48
|
-
console.log('\nUsage:');
|
|
49
|
-
console.log(' pwn mode interactive Switch to interactive mode');
|
|
50
|
-
console.log(' pwn mode batch Switch to batch mode');
|
|
51
|
-
console.log(' pwn mode batch --help Show batch options');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function setInteractiveMode() {
|
|
55
|
-
updateState({ session_mode: 'interactive' });
|
|
56
|
-
console.log('✅ Switched to interactive mode\n');
|
|
57
|
-
console.log('Batch config preserved for next batch session.');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function setBatchMode(args) {
|
|
61
|
-
// Check for help
|
|
62
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
63
|
-
showBatchHelp();
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Check for reset
|
|
68
|
-
if (args.includes('--reset')) {
|
|
69
|
-
updateBatchConfig(DEFAULT_BATCH_CONFIG);
|
|
70
|
-
updateState({ session_mode: 'batch' });
|
|
71
|
-
console.log('✅ Switched to batch mode (config reset to defaults)\n');
|
|
72
|
-
console.log('Config:');
|
|
73
|
-
printConfig(DEFAULT_BATCH_CONFIG);
|
|
74
|
-
console.log('\nRun \'pwn batch run\' to start executing tasks from prd.json.');
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Parse config updates
|
|
79
|
-
const currentConfig = getBatchConfig();
|
|
80
|
-
const updates = parseConfigArgs(args);
|
|
81
|
-
const hasUpdates = Object.keys(updates).length > 0;
|
|
82
|
-
|
|
83
|
-
// Apply updates if any
|
|
84
|
-
if (hasUpdates) {
|
|
85
|
-
updateBatchConfig(updates);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Switch to batch mode
|
|
89
|
-
updateState({ session_mode: 'batch' });
|
|
90
|
-
|
|
91
|
-
// Show result
|
|
92
|
-
if (hasUpdates) {
|
|
93
|
-
console.log('✅ Switched to batch mode\n');
|
|
94
|
-
console.log('Config updated:');
|
|
95
|
-
printConfigChanges(currentConfig, updates);
|
|
96
|
-
} else {
|
|
97
|
-
console.log('✅ Switched to batch mode\n');
|
|
98
|
-
console.log('Config:');
|
|
99
|
-
printConfig(getBatchConfig());
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.log('\nRun \'pwn batch run\' to start executing tasks from prd.json.');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function parseConfigArgs(args) {
|
|
106
|
-
const updates = {};
|
|
107
|
-
|
|
108
|
-
for (const arg of args) {
|
|
109
|
-
// --max-tasks=N
|
|
110
|
-
if (arg.startsWith('--max-tasks=')) {
|
|
111
|
-
const value = parseInt(arg.split('=')[1], 10);
|
|
112
|
-
if (!isNaN(value) && value > 0) {
|
|
113
|
-
updates.max_tasks = value;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
// --max-hours=N
|
|
117
|
-
else if (arg.startsWith('--max-hours=')) {
|
|
118
|
-
const value = parseInt(arg.split('=')[1], 10);
|
|
119
|
-
if (!isNaN(value) && value > 0) {
|
|
120
|
-
updates.max_duration_hours = value;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
// --quality-gates=X,Y,Z
|
|
124
|
-
else if (arg.startsWith('--quality-gates=')) {
|
|
125
|
-
const value = arg.split('=')[1];
|
|
126
|
-
if (value) {
|
|
127
|
-
updates.quality_gates = value.split(',').map(g => g.trim()).filter(Boolean);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
// Boolean flags
|
|
131
|
-
else if (arg === '--auto-commit') {
|
|
132
|
-
updates.auto_commit = true;
|
|
133
|
-
}
|
|
134
|
-
else if (arg === '--no-auto-commit') {
|
|
135
|
-
updates.auto_commit = false;
|
|
136
|
-
}
|
|
137
|
-
else if (arg === '--auto-push') {
|
|
138
|
-
updates.auto_push = true;
|
|
139
|
-
}
|
|
140
|
-
else if (arg === '--no-auto-push') {
|
|
141
|
-
updates.auto_push = false;
|
|
142
|
-
}
|
|
143
|
-
else if (arg === '--create-pr') {
|
|
144
|
-
updates.create_pr = true;
|
|
145
|
-
}
|
|
146
|
-
else if (arg === '--no-create-pr') {
|
|
147
|
-
updates.create_pr = false;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return updates;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function printConfig(config) {
|
|
155
|
-
console.log(` max_tasks: ${config.max_tasks}`);
|
|
156
|
-
console.log(` max_hours: ${config.max_duration_hours}`);
|
|
157
|
-
console.log(` quality_gates: ${config.quality_gates.join(', ')}`);
|
|
158
|
-
console.log(` auto_commit: ${config.auto_commit}`);
|
|
159
|
-
console.log(` auto_push: ${config.auto_push}`);
|
|
160
|
-
console.log(` create_pr: ${config.create_pr}`);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function printConfigChanges(oldConfig, updates) {
|
|
164
|
-
const newConfig = { ...oldConfig, ...updates };
|
|
165
|
-
|
|
166
|
-
if ('max_tasks' in updates) {
|
|
167
|
-
console.log(` max_tasks: ${oldConfig.max_tasks} → ${newConfig.max_tasks}`);
|
|
168
|
-
}
|
|
169
|
-
if ('max_duration_hours' in updates) {
|
|
170
|
-
console.log(` max_hours: ${oldConfig.max_duration_hours} → ${newConfig.max_duration_hours}`);
|
|
171
|
-
}
|
|
172
|
-
if ('quality_gates' in updates) {
|
|
173
|
-
console.log(` quality_gates: ${oldConfig.quality_gates.join(', ')} → ${newConfig.quality_gates.join(', ')}`);
|
|
174
|
-
}
|
|
175
|
-
if ('auto_commit' in updates) {
|
|
176
|
-
console.log(` auto_commit: ${oldConfig.auto_commit} → ${newConfig.auto_commit}`);
|
|
177
|
-
}
|
|
178
|
-
if ('auto_push' in updates) {
|
|
179
|
-
console.log(` auto_push: ${oldConfig.auto_push} → ${newConfig.auto_push}`);
|
|
180
|
-
}
|
|
181
|
-
if ('create_pr' in updates) {
|
|
182
|
-
console.log(` create_pr: ${oldConfig.create_pr} → ${newConfig.create_pr}`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function showBatchHelp() {
|
|
187
|
-
console.log('PWN Mode - Batch Configuration\n');
|
|
188
|
-
console.log('Usage: pwn mode batch [options]\n');
|
|
189
|
-
console.log('Options:');
|
|
190
|
-
console.log(' --max-tasks=N Maximum tasks per batch (default: 5)');
|
|
191
|
-
console.log(' --max-hours=N Maximum duration in hours (default: 4)');
|
|
192
|
-
console.log(' --quality-gates=X,Y,Z Quality gates to run (default: typecheck,lint,test)');
|
|
193
|
-
console.log(' --auto-commit Enable auto commit (default)');
|
|
194
|
-
console.log(' --no-auto-commit Disable auto commit');
|
|
195
|
-
console.log(' --auto-push Enable auto push');
|
|
196
|
-
console.log(' --no-auto-push Disable auto push (default)');
|
|
197
|
-
console.log(' --create-pr Enable PR creation');
|
|
198
|
-
console.log(' --no-create-pr Disable PR creation (default)');
|
|
199
|
-
console.log(' --reset Reset config to defaults');
|
|
200
|
-
console.log(' --help, -h Show this help\n');
|
|
201
|
-
console.log('Examples:');
|
|
202
|
-
console.log(' pwn mode batch Switch to batch with current config');
|
|
203
|
-
console.log(' pwn mode batch --max-tasks=3 Set max tasks to 3');
|
|
204
|
-
console.log(' pwn mode batch --auto-push --create-pr Enable push and PR creation');
|
|
205
|
-
console.log(' pwn mode batch --reset Reset to defaults');
|
|
206
|
-
}
|