@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 ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ import { hasWorkspace } from '../src/core/state.js';
3
+ import { parsePrdTasks } from '../src/services/batch-service.js';
4
+ import { listTaskFiles } from '../src/services/batch-runner.js';
5
+ import { startViewer, printPlain } from '../src/ui/backlog-viewer.js';
6
+ import { existsSync, readFileSync } from 'fs';
7
+ import { join } from 'path';
8
+
9
+ export default async function backlogCommand(args = []) {
10
+ if (args.includes('--help') || args.includes('-h')) {
11
+ showHelp();
12
+ return;
13
+ }
14
+
15
+ if (!hasWorkspace()) {
16
+ console.log('āŒ No PWN workspace found\n');
17
+ console.log(' Run: pwn inject');
18
+ process.exit(1);
19
+ }
20
+
21
+ const cwd = process.cwd();
22
+ const stories = parsePrdTasks(cwd);
23
+ const taskFiles = listTaskFiles(cwd);
24
+
25
+ // Read project name from prd.json
26
+ let project = 'project';
27
+ const prdPath = join(cwd, '.ai', 'tasks', 'prd.json');
28
+ if (existsSync(prdPath)) {
29
+ try {
30
+ const prd = JSON.parse(readFileSync(prdPath, 'utf8'));
31
+ project = prd.project || project;
32
+ } catch {}
33
+ }
34
+
35
+ const noInteractive = args.includes('--no-interactive') || !process.stdout.isTTY;
36
+
37
+ if (noInteractive) {
38
+ printPlain({ project, stories, taskFiles });
39
+ } else {
40
+ await startViewer({ project, stories, taskFiles });
41
+ }
42
+ }
43
+
44
+ function showHelp() {
45
+ console.log('šŸ“‹ PWN Backlog Viewer\n');
46
+ console.log('Usage: pwn backlog [options]\n');
47
+ console.log('Options:');
48
+ console.log(' --no-interactive Plain text output (for CI/piping)');
49
+ console.log(' --help, -h Show this help\n');
50
+ console.log('Keybindings (list view):');
51
+ console.log(' ↑/k Move up');
52
+ console.log(' ↓/j Move down');
53
+ console.log(' Enter Open full detail view');
54
+ console.log(' Home/End First/last story');
55
+ console.log(' q/Ctrl+C Quit\n');
56
+ console.log('Keybindings (detail view):');
57
+ console.log(' ↑/k ↓/j Scroll content');
58
+ console.log(' ←/→ Previous/next story');
59
+ console.log(' Esc/Bksp Back to list');
60
+ }
package/cli/batch.js CHANGED
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { execSync } from 'child_process';
3
- import { join } from 'path';
4
2
  import * as batch from '../src/services/batch-service.js';
5
3
  import { hasWorkspace } from '../src/core/state.js';
6
4
 
@@ -29,16 +27,14 @@ export default async function batchCommand(args = []) {
29
27
  return showConfig();
30
28
  }
31
29
 
30
+ if (subcommand === 'tasks') {
31
+ return showTasks(args.slice(1));
32
+ }
33
+
32
34
  if (subcommand === 'run') {
33
- const runnerPath = join(process.cwd(), '.ai', 'batch', 'batch_runner.sh');
34
- try {
35
- execSync(`bash ${runnerPath} ${args.slice(1).join(' ')}`, {
36
- cwd: process.cwd(),
37
- stdio: 'inherit'
38
- });
39
- } catch (error) {
40
- process.exit(error.status || 1);
41
- }
35
+ const runArgs = parseRunOptions(args.slice(1));
36
+ const { runBatch } = await import('../src/services/batch-runner.js');
37
+ await runBatch(runArgs, process.cwd());
42
38
  return;
43
39
  }
44
40
 
@@ -94,6 +90,31 @@ function parseOptions(args) {
94
90
  return options;
95
91
  }
96
92
 
93
+ /**
94
+ * Parse options for `pwn batch run`
95
+ */
96
+ function parseRunOptions(args) {
97
+ const options = {};
98
+
99
+ for (let i = 0; i < args.length; i++) {
100
+ const arg = args[i];
101
+
102
+ if (arg === '--dry-run') {
103
+ options.dryRun = true;
104
+ } else if (arg === '--phase') {
105
+ options.phase = args[++i];
106
+ } else if (arg === '--no-plan') {
107
+ options.noPlan = true;
108
+ } else if (arg === '--rate-limit-wait') {
109
+ options.rateLimitWait = parseInt(args[++i], 10);
110
+ } else if (/^\d+$/.test(arg)) {
111
+ options.maxIterations = parseInt(arg, 10);
112
+ }
113
+ }
114
+
115
+ return options;
116
+ }
117
+
97
118
  /**
98
119
  * Show batch status
99
120
  */
@@ -167,8 +188,6 @@ function showConfig() {
167
188
  console.log(` Create PR: ${config.create_pr ? 'Yes' : 'No'}`);
168
189
  console.log(` Branch Format: ${config.branch_format}`);
169
190
  console.log(` Commit Format: ${config.commit_format}`);
170
- console.log(`\n Notify Complete: ${config.notify_on_complete ? 'Yes' : 'No'}`);
171
- console.log(` Notify Error: ${config.notify_on_error ? 'Yes' : 'No'}`);
172
191
  console.log('\nšŸ“ Config location: .ai/state.json (batch_config)');
173
192
  }
174
193
 
@@ -308,6 +327,67 @@ async function resumeBatch(options) {
308
327
  console.log(` Completed: ${result.completed?.length || 0} tasks`);
309
328
  }
310
329
 
330
+ /**
331
+ * Show task files from .ai/batch/tasks/
332
+ */
333
+ async function showTasks(args) {
334
+ const { listTaskFiles, deleteTaskFile } = await import('../src/services/batch-runner.js');
335
+ const cwd = process.cwd();
336
+ const failedOnly = args.includes('--failed');
337
+ const cleanMode = args.includes('--clean');
338
+
339
+ if (cleanMode) {
340
+ const completed = listTaskFiles(cwd, { statusFilter: 'completed' });
341
+ if (completed.length === 0) {
342
+ console.log('No completed task files to clean.');
343
+ return;
344
+ }
345
+ let cleaned = 0;
346
+ for (const task of completed) {
347
+ if (deleteTaskFile(task.id, cwd)) cleaned++;
348
+ }
349
+ console.log(`Cleaned ${cleaned} completed task file(s).`);
350
+ return;
351
+ }
352
+
353
+ const filter = failedOnly ? { statusFilter: 'failed' } : {};
354
+ const tasks = listTaskFiles(cwd, filter);
355
+
356
+ if (tasks.length === 0) {
357
+ console.log(failedOnly ? 'No failed tasks.' : 'No task files found.');
358
+ return;
359
+ }
360
+
361
+ const title = failedOnly ? 'Failed Tasks' : 'Batch Task Files';
362
+ console.log(`\n${title}\n`);
363
+
364
+ for (const task of tasks) {
365
+ const statusColor = task.status === 'completed' ? 'green'
366
+ : task.status === 'failed' ? 'red'
367
+ : 'yellow';
368
+ const statusLabel = chalk[statusColor](task.status.toUpperCase());
369
+ const complexity = task.complexity ? ` [${task.complexity}]` : '';
370
+ const estimate = task.estimated_time_seconds
371
+ ? ` ~${Math.round(task.estimated_time_seconds)}s`
372
+ : '';
373
+
374
+ console.log(` ${task.id}: ${task.title}`);
375
+ console.log(` Status: ${statusLabel}${complexity}${estimate}`);
376
+
377
+ if (task.status === 'failed' && task.failure_reason) {
378
+ const reason = task.failure_reason.length > 120
379
+ ? task.failure_reason.slice(0, 120) + '...'
380
+ : task.failure_reason;
381
+ console.log(` Reason: ${chalk.dim(reason)}`);
382
+ }
383
+
384
+ if (task.plan && task.plan.length > 0 && task.plan[0] !== 'fallback - no plan available') {
385
+ console.log(` Plan: ${chalk.dim(task.plan.join(' → '))}`);
386
+ }
387
+ console.log('');
388
+ }
389
+ }
390
+
311
391
  /**
312
392
  * Show help
313
393
  */
@@ -316,7 +396,8 @@ function showHelp() {
316
396
  console.log('Usage: pwn batch [command] [options]\n');
317
397
  console.log('Commands:');
318
398
  console.log(' (default) Execute next available task(s)');
319
- console.log(' run Run batch_runner.sh (autonomous loop)');
399
+ console.log(' run Run autonomous batch loop (Node.js TUI)');
400
+ console.log(' tasks List batch task files');
320
401
  console.log(' status Show batch status');
321
402
  console.log(' config Show batch configuration\n');
322
403
  console.log('Options:');
@@ -329,12 +410,18 @@ function showHelp() {
329
410
  console.log(' --continue Continue on errors');
330
411
  console.log(' --no-commit Skip auto-commit');
331
412
  console.log(' --no-branch Skip branch creation');
413
+ console.log(' --no-plan Skip planning phase (use fixed 10min timeout)');
414
+ console.log(' --rate-limit-wait <s> Seconds to wait on rate limit (default: 1800)');
332
415
  console.log(' --help, -h Show this help\n');
333
416
  console.log('Examples:');
334
417
  console.log(' pwn batch # Execute next task');
335
418
  console.log(' pwn batch run # Run autonomous batch loop');
336
419
  console.log(' pwn batch run --dry-run # Preview next story');
337
420
  console.log(' pwn batch run --phase 3 # Run specific phase');
421
+ console.log(' pwn batch run --no-plan # Skip planning, fixed timeout');
422
+ console.log(' pwn batch tasks # List all task files');
423
+ console.log(' pwn batch tasks --failed # Show only failed tasks');
424
+ console.log(' pwn batch tasks --clean # Delete completed task files');
338
425
  console.log(' pwn batch --count 5 # Execute 5 tasks');
339
426
  console.log(' pwn batch --dry-run # Preview execution');
340
427
  console.log(' pwn batch --resume # Resume paused batch');
package/cli/index.js CHANGED
@@ -22,31 +22,24 @@ if (!command || command === '--help' || command === '-h') {
22
22
  console.log('Commands:');
23
23
  console.log(' inject Inject .ai/ workspace into current project');
24
24
  console.log(' update Update PWN framework files to latest version');
25
- console.log(' migrate Migrate existing AI files to PWN structure');
26
25
  console.log(' save Save session context to memory');
27
26
  console.log(' status Show workspace status');
28
27
  console.log(' validate Validate workspace structure');
29
- console.log(' notify Send notifications (test, send, config)');
28
+ console.log(' backlog Interactive backlog viewer (prd.json)');
30
29
  console.log(' batch Execute tasks (batch run = autonomous loop)');
31
- console.log(' mode Manage session mode (interactive/batch)');
32
30
  console.log(' patterns Manage patterns and triggers');
33
31
  console.log(' knowledge Knowledge lifecycle management');
34
- console.log(' codespaces GitHub Codespaces integration');
35
32
  console.log(' --version, -v Show version');
36
33
  console.log(' --help, -h Show help\n');
37
34
  console.log('Options:');
38
35
  console.log(' inject --force Overwrite existing .ai/ directory');
39
36
  console.log(' update --dry-run Preview update without changes');
40
- console.log(' migrate --dry-run Preview migration without changes');
41
37
  console.log(' save --message=X Save with custom summary');
42
38
  console.log(' validate --verbose Show detailed structure report');
43
- console.log(' notify test [ch] Test notification channel');
44
39
  console.log(' batch run Run autonomous batch loop');
45
- console.log(' mode batch --max-tasks=3 Configure batch mode');
46
40
  console.log(' patterns eval <f> Evaluate triggers for file');
47
- console.log(' knowledge status Show knowledge system status');
48
- console.log(' codespaces init Add devcontainer config\n');
49
- console.log('Documentation: https://github.com/anthropics/pwn');
41
+ console.log(' knowledge status Show knowledge system status\n');
42
+ console.log('Documentation: https://github.com/fermindi/pwn');
50
43
  process.exit(0);
51
44
  }
52
45
 
@@ -62,11 +55,6 @@ switch (command) {
62
55
  await update(args);
63
56
  break;
64
57
 
65
- case 'migrate':
66
- const { default: migrate } = await import('./migrate.js');
67
- await migrate(args);
68
- break;
69
-
70
58
  case 'save':
71
59
  const { default: save } = await import('./save.js');
72
60
  await save(args);
@@ -82,9 +70,9 @@ switch (command) {
82
70
  await validate(args);
83
71
  break;
84
72
 
85
- case 'notify':
86
- const { default: notify } = await import('./notify.js');
87
- await notify(args);
73
+ case 'backlog':
74
+ const { default: backlogCmd } = await import('./backlog.js');
75
+ await backlogCmd(args);
88
76
  break;
89
77
 
90
78
  case 'batch':
@@ -92,11 +80,6 @@ switch (command) {
92
80
  await batchCmd(args);
93
81
  break;
94
82
 
95
- case 'mode':
96
- const { default: modeCmd } = await import('./mode.js');
97
- await modeCmd(args);
98
- break;
99
-
100
83
  case 'patterns':
101
84
  const { default: patternsCmd } = await import('./patterns.js');
102
85
  await patternsCmd(args);
@@ -107,13 +90,8 @@ switch (command) {
107
90
  await knowledgeCmd(args);
108
91
  break;
109
92
 
110
- case 'codespaces':
111
- const { default: codespacesCmd } = await import('./codespaces.js');
112
- await codespacesCmd(args);
113
- break;
114
-
115
93
  default:
116
- console.log(`āŒ Unknown command: ${command}`);
94
+ console.log(`Unknown command: ${command}`);
117
95
  console.log(' Run: pwn --help');
118
96
  process.exit(1);
119
97
  }
package/cli/inject.js CHANGED
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, readFileSync } from 'fs';
3
- import { join } from 'path';
4
2
  import { inject, detectKnownAIFiles } from '../src/core/inject.js';
5
3
 
6
4
  export default async function injectCommand(args = []) {
@@ -22,17 +20,11 @@ export default async function injectCommand(args = []) {
22
20
  console.log(` Type: ${file.type} - ${file.description}`);
23
21
  }
24
22
 
25
- if (migratable.length > 0) {
26
- console.log('\n šŸ”„ Run "pwn migrate" to automatically migrate content:');
27
- console.log(' pwn migrate --dry-run # Preview changes');
28
- console.log(' pwn migrate # Execute migration\n');
29
- } else {
30
- console.log('\n šŸ’” Consider migrating content manually to .ai/ structure:');
31
- console.log(' - Instructions → .ai/agents/claude.md');
32
- console.log(' - Decisions → .ai/memory/decisions.md');
33
- console.log(' - Patterns → .ai/memory/patterns.md');
34
- console.log(' - Tasks → .ai/tasks/active.md\n');
35
- }
23
+ console.log('\n šŸ’” Consider migrating content manually to .ai/ structure:');
24
+ console.log(' - Instructions → .ai/agents/claude.md');
25
+ console.log(' - Decisions → .ai/memory/decisions.md');
26
+ console.log(' - Patterns → .ai/memory/patterns.md');
27
+ console.log(' - Tasks → .ai/tasks/active.md\n');
36
28
  }
37
29
 
38
30
  if (!result.success) {
@@ -63,30 +55,13 @@ export default async function injectCommand(args = []) {
63
55
  console.log(' ā”œā”€ā”€ memory/ (decisions, patterns, dead-ends)');
64
56
  console.log(' ā”œā”€ā”€ tasks/ (active work, prd.json)');
65
57
  console.log(' ā”œā”€ā”€ patterns/ (auto-applied patterns)');
66
- console.log(' ā”œā”€ā”€ batch/ (batch runner, prompts)
67
- ā”œā”€ā”€ workflows/ (batch execution)');
58
+ console.log(' ā”œā”€ā”€ batch/ (batch runner, prompts)');
59
+ console.log(' ā”œā”€ā”€ workflows/ (batch execution)');
68
60
  console.log(' ā”œā”€ā”€ agents/ (AI agent configs)');
69
- console.log(' └── config/ (notifications, etc)');
61
+ console.log(' └── config/ (project config)');
70
62
  console.log(' .claude/');
71
63
  console.log(' └── commands/ (slash commands: /save)\n');
72
64
 
73
- // Show ntfy topic if generated
74
- const notifyPath = join(process.cwd(), '.ai', 'config', 'notifications.json');
75
- if (existsSync(notifyPath)) {
76
- try {
77
- const config = JSON.parse(readFileSync(notifyPath, 'utf8'));
78
- const topic = config.channels?.ntfy?.topic;
79
- if (topic && !topic.includes('your-unique')) {
80
- console.log('šŸ”” Notifications:');
81
- console.log(` ntfy topic: ${topic}`);
82
- console.log(` Subscribe: https://ntfy.sh/${topic}`);
83
- console.log(' Enable: Edit .ai/config/notifications.json\n');
84
- }
85
- } catch {
86
- // Ignore
87
- }
88
- }
89
-
90
65
  console.log('šŸ“– Next steps:');
91
66
  console.log(' 1. Read: .ai/README.md');
92
67
  console.log(' 2. Start working with AI assistance\n');
package/cli/update.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { existsSync, readFileSync, writeFileSync, cpSync, renameSync, mkdirSync, readdirSync } from 'fs';
3
3
  import { join, dirname } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import { convertBacklogToPrd } from '../src/services/batch-service.js';
5
+ import { convertBacklogToPrd, detectAvailableGates } from '../src/services/batch-service.js';
6
6
 
7
7
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
8
 
@@ -18,10 +18,8 @@ const FRAMEWORK_FILES = [
18
18
  'patterns/backend/backend.template.md',
19
19
  'patterns/universal/universal.template.md',
20
20
  'workflows/batch-task.md',
21
- 'batch/batch_runner.sh',
22
21
  'batch/prompt.md',
23
22
  'batch/progress.txt',
24
- 'batch/prd_status.sh',
25
23
  'config/README.md',
26
24
  'README.md',
27
25
  ];
@@ -31,7 +29,6 @@ const FRAMEWORK_FILES = [
31
29
  */
32
30
  const CLAUDE_COMMANDS = [
33
31
  'save.md',
34
- 'mode.md',
35
32
  ];
36
33
 
37
34
  /**
@@ -46,7 +43,6 @@ const USER_FILES = [
46
43
  'tasks/active.md',
47
44
  'tasks/prd.json',
48
45
  'state.json',
49
- 'config/notifications.json',
50
46
  ];
51
47
 
52
48
  export default async function updateCommand(args = []) {
@@ -262,25 +258,6 @@ export default async function updateCommand(args = []) {
262
258
  }
263
259
  }
264
260
 
265
- // Update .claude/settings.json (hooks) - only if doesn't exist (user config)
266
- const claudeSettingsTemplate = join(__dirname, '../templates/workspace/.claude/settings.json');
267
- const claudeSettingsTarget = join(cwd, '.claude', 'settings.json');
268
-
269
- if (existsSync(claudeSettingsTemplate) && !existsSync(claudeSettingsTarget)) {
270
- if (dryRun) {
271
- console.log(` šŸ“ Would create: .claude/settings.json (notification hooks)`);
272
- } else {
273
- const claudeDir = join(cwd, '.claude');
274
- if (!existsSync(claudeDir)) {
275
- mkdirSync(claudeDir, { recursive: true });
276
- }
277
- const templateContent = readFileSync(claudeSettingsTemplate, 'utf8');
278
- writeFileSync(claudeSettingsTarget, templateContent);
279
- console.log(` šŸ“ Created: .claude/settings.json (notification hooks)`);
280
- }
281
- updated.push('.claude/settings.json');
282
- }
283
-
284
261
  // Update state.json with new version
285
262
  if (!dryRun && existsSync(statePath)) {
286
263
  try {
@@ -293,6 +270,36 @@ export default async function updateCommand(args = []) {
293
270
  }
294
271
  }
295
272
 
273
+ // Re-detect quality gates and update skip_gates
274
+ if (!dryRun) {
275
+ const gates = detectAvailableGates(cwd);
276
+ console.log('\nāš™ļø Quality Gates Detection');
277
+ for (const gate of gates.available) {
278
+ console.log(` āœ… ${gate.padEnd(12)} ${gates.details[gate]}`);
279
+ }
280
+ for (const gate of gates.missing) {
281
+ console.log(` āš ļø ${gate.padEnd(12)} not detected`);
282
+ }
283
+ if (gates.missing.length > 0) {
284
+ console.log(` → Auto-configured skip_gates: ${JSON.stringify(gates.missing)}`);
285
+ } else {
286
+ console.log(` → All gates available`);
287
+ }
288
+
289
+ // Update skip_gates in state.json
290
+ if (existsSync(statePath)) {
291
+ try {
292
+ const state = JSON.parse(readFileSync(statePath, 'utf8'));
293
+ if (!state.batch_config) state.batch_config = {};
294
+ state.batch_config.skip_gates = gates.missing;
295
+ state.last_updated = new Date().toISOString();
296
+ writeFileSync(statePath, JSON.stringify(state, null, 2));
297
+ } catch {
298
+ // Ignore
299
+ }
300
+ }
301
+ }
302
+
296
303
  // Summary
297
304
  console.log('');
298
305
  if (updated.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fermindi/pwn-cli",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Professional AI Workspace - Inject structured memory and automation into any project for AI-powered development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,7 +14,6 @@
14
14
  "./core/validate": "./src/core/validate.js",
15
15
  "./core/workspace": "./src/core/workspace.js",
16
16
  "./services/batch": "./src/services/batch-service.js",
17
- "./services/notifications": "./src/services/notification-service.js",
18
17
  "./patterns/registry": "./src/patterns/registry.js",
19
18
  "./patterns/triggers": "./src/patterns/triggers.js",
20
19
  "./knowledge/lifecycle": "./src/knowledge/lifecycle.js",
@@ -47,7 +46,7 @@
47
46
  "memory",
48
47
  "decisions",
49
48
  "batch",
50
- "codespaces",
49
+ "claude-code",
51
50
  "developer-tools",
52
51
  "productivity"
53
52
  ],
@@ -66,5 +65,9 @@
66
65
  },
67
66
  "devDependencies": {
68
67
  "vitest": "^2.0.0"
68
+ },
69
+ "dependencies": {
70
+ "chalk": "^5.6.2",
71
+ "ora": "^9.3.0"
69
72
  }
70
73
  }
@@ -1,9 +1,9 @@
1
1
  import { existsSync, cpSync, renameSync, readFileSync, appendFileSync, writeFileSync, readdirSync, mkdirSync } from 'fs';
2
2
  import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
- import { randomUUID } from 'crypto';
5
4
  import { initState } from './state.js';
6
- import { convertBacklogToPrd } from '../services/batch-service.js';
5
+ import { convertBacklogToPrd, detectAvailableGates } from '../services/batch-service.js';
6
+ import { updateState } from './state.js';
7
7
 
8
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
9
 
@@ -175,18 +175,24 @@ export async function inject(options = {}) {
175
175
  renameSync(templateState, stateFile);
176
176
  }
177
177
 
178
- // Rename notifications.template.json → notifications.json and generate unique topic
179
- const templateNotify = join(targetDir, 'config', 'notifications.template.json');
180
- const notifyFile = join(targetDir, 'config', 'notifications.json');
181
-
182
- if (existsSync(templateNotify)) {
183
- renameSync(templateNotify, notifyFile);
184
- initNotifications(notifyFile);
185
- }
186
-
187
178
  // Initialize state.json with current user
188
179
  initState(cwd);
189
180
 
181
+ // Detect available quality gates and auto-configure skip_gates
182
+ const gates = detectAvailableGates(cwd);
183
+ log('\nāš™ļø Quality Gates Detection');
184
+ for (const gate of gates.available) {
185
+ log(` āœ… ${gate.padEnd(12)} ${gates.details[gate]}`);
186
+ }
187
+ for (const gate of gates.missing) {
188
+ log(` āš ļø ${gate.padEnd(12)} not detected`);
189
+ }
190
+ if (gates.missing.length > 0) {
191
+ log(` → Auto-configured skip_gates: ${JSON.stringify(gates.missing)}`);
192
+ updateState({ batch_config: { skip_gates: gates.missing } }, cwd);
193
+ }
194
+ log('');
195
+
190
196
  // Write migrated prd.json (from backlog.md) if available
191
197
  if (migratedPrd) {
192
198
  const prdPath = join(targetDir, 'tasks', 'prd.json');
@@ -242,15 +248,6 @@ export async function inject(options = {}) {
242
248
  }
243
249
  }
244
250
 
245
- // Copy settings.json with hooks (if doesn't exist)
246
- const settingsSource = join(claudeTemplateDir, 'settings.json');
247
- const settingsTarget = join(claudeDir, 'settings.json');
248
- if (existsSync(settingsSource) && !existsSync(settingsTarget)) {
249
- cpSync(settingsSource, settingsTarget);
250
- if (!silent) {
251
- console.log('šŸ“ Created .claude/settings.json with notification hooks');
252
- }
253
- }
254
251
  }
255
252
 
256
253
  // Backup other AI files (not CLAUDE.md) to .ai/
@@ -280,24 +277,6 @@ export async function inject(options = {}) {
280
277
  }
281
278
  }
282
279
 
283
- /**
284
- * Initialize notifications.json with unique topic
285
- * @param {string} notifyFile - Path to notifications.json
286
- */
287
- function initNotifications(notifyFile) {
288
- try {
289
- const content = readFileSync(notifyFile, 'utf8');
290
- const config = JSON.parse(content);
291
-
292
- // Generate unique topic ID
293
- const uniqueId = randomUUID().split('-')[0]; // First segment: 8 chars
294
- config.channels.ntfy.topic = `pwn-${uniqueId}`;
295
-
296
- writeFileSync(notifyFile, JSON.stringify(config, null, 2));
297
- } catch {
298
- // Ignore errors - notifications will use defaults
299
- }
300
- }
301
280
 
302
281
  /**
303
282
  * Backup existing AI files to .ai/ root with ~ prefix
@@ -351,7 +330,7 @@ function updateGitignore(cwd, silent = false) {
351
330
  }
352
331
 
353
332
  if (!gitignoreContent.includes('.ai/state.json')) {
354
- const pwnSection = '\n# PWN\n.ai/state.json\n.ai/config/notifications.json\n';
333
+ const pwnSection = '\n# PWN\n.ai/state.json\n';
355
334
  appendFileSync(gitignorePath, pwnSection);
356
335
  if (!silent) {
357
336
  console.log('šŸ“ Updated .gitignore');
package/src/core/state.js CHANGED
@@ -95,7 +95,6 @@ export function initState(cwd = process.cwd()) {
95
95
  const state = {
96
96
  developer: getGitUser(),
97
97
  session_started: new Date().toISOString(),
98
- session_mode: 'interactive',
99
98
  current_task: null,
100
99
  context_loaded: [],
101
100
  batch_config: { ...DEFAULT_BATCH_CONFIG }
@@ -27,10 +27,8 @@ const REQUIRED_STRUCTURE = {
27
27
  'tasks/prd.json'
28
28
  ],
29
29
  batchFiles: [
30
- 'batch/batch_runner.sh',
31
30
  'batch/prompt.md',
32
- 'batch/progress.txt',
33
- 'batch/prd_status.sh'
31
+ 'batch/progress.txt'
34
32
  ],
35
33
  agentFiles: [
36
34
  'agents/README.md',
package/src/index.js CHANGED
@@ -5,7 +5,6 @@ export { validate } from './core/validate.js';
5
5
  export { getWorkspaceInfo } from './core/workspace.js';
6
6
 
7
7
  // Services
8
- export * as notifications from './services/notification-service.js';
9
8
  export * as batch from './services/batch-service.js';
10
9
 
11
10
  // Patterns