@fermindi/pwn-cli 0.5.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.
@@ -1,19 +1,65 @@
1
- # Codebase Patterns Index
1
+ # Codebase Patterns
2
2
 
3
- This file is an index of patterns documented in `.ai/patterns/`.
4
- Each entry links to the full pattern file.
3
+ This file documents recurring patterns discovered during development.
5
4
 
6
- ## Registered Patterns
5
+ ## Format
7
6
 
8
- <!-- AI: Add entries here as you create pattern files -->
9
- <!-- Format: - [{category}/{name}](.ai/patterns/{category}/{name}.md) - Brief description -->
7
+ Each pattern follows this structure:
10
8
 
11
- _No patterns registered yet._
9
+ ```markdown
10
+ ## Pattern Category
11
+
12
+ ### Pattern Name
13
+ **Context:** When this pattern applies
14
+ **Pattern:** Code example or description
15
+ **Rationale:** Why this is the better approach
16
+ **Example Location:** Where to find real examples
17
+ ```
18
+
19
+ ---
20
+
21
+ ## Add New Patterns
22
+
23
+ When you discover a useful pattern:
24
+
25
+ 1. Identify the category (Frontend, Backend, Testing, etc.)
26
+ 2. Fill in all sections
27
+ 3. Add code example if possible
28
+ 4. Commit with message: `docs: add pattern - [name]`
29
+ 5. Update `patterns/index.md` if this should be auto-applied
30
+
31
+ ---
32
+
33
+ ## Pattern Categories
34
+
35
+ - **Frontend:** React, TypeScript, styling, component patterns
36
+ - **Backend:** API design, database queries, middleware
37
+ - **Testing:** Unit tests, integration tests, fixtures
38
+ - **Performance:** Caching, memoization, optimization
39
+ - **Security:** Input validation, authentication, authorization
40
+ - **Error Handling:** Exception patterns, recovery strategies
41
+ - **Data Structures:** Modeling domain objects effectively
42
+
43
+ ---
44
+
45
+ ## Template for New Pattern
46
+
47
+ ```markdown
48
+ ## [Category]
49
+
50
+ ### [Pattern Name]
51
+ **Context:** (When to use this pattern)
52
+ **Pattern:** (Description or code example)
53
+ **Rationale:** (Why this is better)
54
+ **Example Location:** (Where in codebase)
55
+ ```
12
56
 
13
57
  ---
14
58
 
15
- ## How to Add
59
+ ## Notes
16
60
 
17
- 1. Create pattern file: `.ai/patterns/{category}/{pattern-name}.md`
18
- 2. Add index entry here with link and brief description
19
- 3. Commit both files together
61
+ - Patterns should be language-agnostic when possible
62
+ - Prioritize patterns that prevent common bugs
63
+ - Link related patterns together
64
+ - Update this file as team learns new approaches
65
+ - Archive rarely-used patterns after 3 months of no references
@@ -70,7 +70,7 @@ Highlight 1-3 most important tasks for today.
70
70
 
71
71
  ## Notes
72
72
 
73
- - Check backlog.md for upcoming work
73
+ - Check prd.json for upcoming stories
74
74
  - Move completed tasks to archive or backlog with completion date
75
75
  - When stuck, create SPIKE task to investigate
76
76
  - Reference decisions from `memory/decisions.md`
@@ -4,103 +4,79 @@ This file defines the template for autonomous batch task execution via the `pwn
4
4
 
5
5
  ## Overview
6
6
 
7
- Batch task execution allows AI agents to autonomously:
8
- - Select tasks from backlog
9
- - Execute work in sequence
7
+ Batch task execution uses `.ai/batch/batch_runner.sh` to autonomously:
8
+ - Read stories from `.ai/tasks/prd.json`
9
+ - Spawn fresh Claude sessions per task
10
10
  - Run quality gates (tests, linting, type checking)
11
- - Commit changes following conventions
12
- - Continue until backlog depleted or threshold reached
11
+ - Track progress and learnings between iterations
12
+ - Handle retries, rate limits, and circuit breaking
13
13
 
14
14
  ## Usage
15
15
 
16
16
  ```bash
17
- # Execute next available task
18
- pwn batch
17
+ # Preview next task
18
+ ./.ai/batch/batch_runner.sh --dry-run
19
19
 
20
- # Execute specific number of tasks
21
- pwn batch --count 5
20
+ # Run batch (default 20 iterations)
21
+ ./.ai/batch/batch_runner.sh
22
22
 
23
- # Dry-run: show what would execute
24
- pwn batch --dry-run
23
+ # Custom iteration limit
24
+ ./.ai/batch/batch_runner.sh 50
25
25
 
26
- # Execute with specific priority
27
- pwn batch --priority high
26
+ # Specific phase only
27
+ ./.ai/batch/batch_runner.sh --phase 3
28
28
 
29
- # Resume interrupted batch
30
- pwn batch --resume
29
+ # Via pwn CLI
30
+ pwn batch run
31
+ pwn batch run --dry-run
32
+ pwn batch status
31
33
  ```
32
34
 
33
35
  ## Batch Execution Protocol
34
36
 
35
37
  ### Phase 1: Task Selection
36
38
 
37
- 1. Read `tasks/active.md` for incomplete tasks
38
- 2. If active tasks exist and not blocked, resume
39
- 3. Otherwise, read `tasks/backlog.md`
40
- 4. Select highest priority unblocked task
41
- 5. Verify dependencies are met
42
- 6. Move to `active.md`
39
+ 1. Read `tasks/prd.json` for stories
40
+ 2. Find next story where `passes == false`
41
+ 3. Verify all dependencies have `passes == true`
42
+ 4. Apply phase filter if specified
43
43
 
44
44
  ### Phase 2: Execution
45
45
 
46
- 1. Analyze task requirements
47
- 2. Create feature branch: `feature/[task-id]-[slug]`
48
- 3. Execute work following patterns in `patterns/index.md`
49
- 4. Test incrementally during development
50
- 5. Keep commits atomic and descriptive
46
+ 1. Build prompt from `.ai/batch/prompt.md` template
47
+ 2. Substitute story placeholders ({STORY_ID}, {STORY_TITLE}, etc.)
48
+ 3. Spawn: `claude --print --dangerously-skip-permissions -p "<prompt>"`
49
+ 4. Capture output to `logs/` for debugging
51
50
 
52
51
  ### Phase 3: Quality Gates
53
52
 
54
- Before commit, verify:
53
+ Customize `run_quality_gates()` in `batch_runner.sh` for your stack:
55
54
 
55
+ **Node.js:**
56
56
  ```bash
57
- # Type checking
58
- [ ] npm run typecheck || tsc --noEmit
59
-
60
- # Linting
61
- [ ] npm run lint || eslint .
62
-
63
- # Unit tests
64
- [ ] npm run test || jest
65
-
66
- # Integration tests (if applicable)
67
- [ ] npm run test:integration
68
-
69
- # Build verification
70
- [ ] npm run build
57
+ npm test
58
+ npm run lint
59
+ npm run typecheck
60
+ ```
71
61
 
72
- # Security scan (if configured)
73
- [ ] npm run security
62
+ **Python:**
63
+ ```bash
64
+ pytest --tb=short
65
+ ruff check src/
66
+ mypy src/ --ignore-missing-imports
74
67
  ```
75
68
 
76
69
  **Gate Strategy:**
77
70
  - Fail fast on first error
78
- - Output clear error message
79
- - Suggest fix if known
80
- - Option to skip non-critical gates with `--force`
81
-
82
- ### Phase 4: Commit & Cleanup
83
-
84
- 1. Stage all changes: `git add .`
85
- 2. Commit with message format:
86
- ```
87
- feat: [task-id] - [description]
88
-
89
- - Specific change 1
90
- - Specific change 2
91
-
92
- Fixes: [task-id]
93
- ```
94
- 3. Push to remote: `git push -u origin feature/[task-id]-[slug]`
95
- 4. Create PR if configured
96
- 5. Update `active.md`: mark complete with date
71
+ - Retry up to 2x with error context fed back to Claude
72
+ - Circuit breaker after 3 consecutive failures
97
73
 
98
- ### Phase 5: Completion
74
+ ### Phase 4: Completion
99
75
 
100
- 1. Verify commit is on remote
101
- 2. Delete local feature branch
102
- 3. Report completion to user
103
- 4. Continue to next task or stop
76
+ 1. Mark story as `passes: true` in prd.json
77
+ 2. Append learnings to `progress.txt`
78
+ 3. Commit prd.json + progress.txt update
79
+ 4. Continue to next story
104
80
 
105
81
  ## Batch Configuration
106
82
 
@@ -103,50 +103,10 @@ Create a session summary:
103
103
  ## Dead Ends
104
104
  - DE-XXX: description (if any)
105
105
 
106
- ## Patterns Documented
107
- - [{category}/{name}] - description (if any)
108
-
109
106
  ---
110
107
  *Saved: {ISO timestamp}*
111
108
  ```
112
109
 
113
- ### 6. Pattern Extraction (.ai/patterns/)
114
-
115
- Review the entire session and extract patterns. Check each criterion:
116
-
117
- 1. **User corrected your approach** → Document the correct approach
118
- 2. **You chose between alternatives** → Document what was chosen and why
119
- 3. **Workaround for limitation** → Document the workaround
120
- 4. **Convention established** → Document for consistency
121
- 5. **Repeated approach** → Document to avoid re-deriving
122
- 6. **Non-trivial solution** → Document for future sessions
123
-
124
- **For each pattern found:**
125
-
126
- 1. Create file: `.ai/patterns/{category}/{pattern-name}.md`
127
- ```markdown
128
- # Pattern Name
129
-
130
- ## Context
131
- When to apply this pattern.
132
-
133
- ## Pattern
134
- Description or code example.
135
-
136
- ## Rationale
137
- Why this approach works better.
138
-
139
- ## Examples
140
- - `src/path/file.ts:42`
141
- ```
142
-
143
- 2. Add index entry to `.ai/memory/patterns.md`:
144
- ```markdown
145
- - [{category}/{name}](.ai/patterns/{category}/{name}.md) - Brief description
146
- ```
147
-
148
- **If no patterns found:** Confirm "Patterns: no new patterns this session"
149
-
150
110
  ---
151
111
 
152
112
  ## Output Format
@@ -159,7 +119,6 @@ PWN Save Complete:
159
119
  - Tasks: {X} active, {Y} completed
160
120
  - Dead Ends: {new count or "no new"}
161
121
  - Decisions: {new count or "no new"}
162
- - Patterns: {new count or "no new"}
163
122
  - Archive: .ai/memory/archive/{filename}
164
123
  - Last updated: {timestamp}
165
124
  ```
@@ -173,7 +132,6 @@ PWN Save Complete:
173
132
  3. **decisions.md**: Organized entries with proper DEC-XXX IDs
174
133
  4. **active.md**: Only current sprint tasks
175
134
  5. **archive/**: One file per session with summary
176
- 6. **patterns/**: One file per pattern, indexed in memory/patterns.md
177
135
 
178
136
  ---
179
137
 
package/cli/codespaces.js DELETED
@@ -1,303 +0,0 @@
1
- import { existsSync, mkdirSync, cpSync, readFileSync, writeFileSync, chmodSync } from 'fs';
2
- import { join, dirname } from 'path';
3
- import { fileURLToPath } from 'url';
4
- import { execSync } from 'child_process';
5
-
6
- const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const cwd = process.cwd();
8
-
9
- export default async function codespaces(args) {
10
- const subcommand = args[0];
11
-
12
- switch (subcommand) {
13
- case 'init':
14
- await initCodespaces(args.slice(1));
15
- break;
16
-
17
- case 'status':
18
- await showStatus();
19
- break;
20
-
21
- case 'open':
22
- await openCodespace(args.slice(1));
23
- break;
24
-
25
- case 'list':
26
- await listCodespaces();
27
- break;
28
-
29
- default:
30
- showHelp();
31
- }
32
- }
33
-
34
- function showHelp() {
35
- console.log(`
36
- PWN Codespaces Integration
37
-
38
- Usage: pwn codespaces <command> [options]
39
-
40
- Commands:
41
- init Add devcontainer config to current project
42
- init --minimal Add minimal devcontainer (no extras)
43
- init --docker Use custom Dockerfile
44
- status Check codespaces configuration
45
- open Open project in GitHub Codespace
46
- list List active codespaces (requires gh CLI)
47
-
48
- Examples:
49
- pwn codespaces init
50
- pwn codespaces init --minimal
51
- pwn codespaces open
52
- pwn codespaces status
53
-
54
- Note: Requires GitHub CLI (gh) for some features.
55
- Install: https://cli.github.com/
56
- `);
57
- }
58
-
59
- async function initCodespaces(args) {
60
- const minimal = args.includes('--minimal');
61
- const useDockerfile = args.includes('--docker');
62
-
63
- console.log('Setting up Codespaces configuration...\n');
64
-
65
- // Check if .devcontainer already exists
66
- const devcontainerDir = join(cwd, '.devcontainer');
67
-
68
- if (existsSync(devcontainerDir)) {
69
- if (!args.includes('--force')) {
70
- console.log('.devcontainer/ already exists.');
71
- console.log('Use --force to overwrite.');
72
- return;
73
- }
74
- console.log('Overwriting existing .devcontainer/...');
75
- }
76
-
77
- // Create directory
78
- mkdirSync(devcontainerDir, { recursive: true });
79
-
80
- // Copy template files
81
- const templateDir = join(__dirname, '..', 'templates', 'codespaces');
82
-
83
- if (minimal) {
84
- // Minimal config - just devcontainer.json
85
- const minimalConfig = {
86
- name: getProjectName(),
87
- image: 'mcr.microsoft.com/devcontainers/javascript-node:22',
88
- postCreateCommand: 'npm install',
89
- forwardPorts: [3000, 5173],
90
- remoteUser: 'node'
91
- };
92
-
93
- writeFileSync(
94
- join(devcontainerDir, 'devcontainer.json'),
95
- JSON.stringify(minimalConfig, null, 2)
96
- );
97
- console.log('Created minimal devcontainer.json');
98
-
99
- } else if (useDockerfile) {
100
- // Copy full template with Dockerfile reference
101
- const pwmDevcontainerDir = join(__dirname, '..', '.devcontainer');
102
-
103
- cpSync(join(pwmDevcontainerDir, 'Dockerfile'), join(devcontainerDir, 'Dockerfile'));
104
- cpSync(join(pwmDevcontainerDir, 'devcontainer.dockerfile.json'), join(devcontainerDir, 'devcontainer.json'));
105
- cpSync(join(pwmDevcontainerDir, 'setup-codespace.sh'), join(devcontainerDir, 'setup.sh'));
106
-
107
- console.log('Created devcontainer.json (with Dockerfile)');
108
- console.log('Created Dockerfile');
109
- console.log('Created setup.sh');
110
-
111
- } else {
112
- // Standard setup from templates
113
- if (existsSync(templateDir)) {
114
- cpSync(join(templateDir, 'devcontainer.json'), join(devcontainerDir, 'devcontainer.json'));
115
- cpSync(join(templateDir, 'setup.sh'), join(devcontainerDir, 'setup.sh'));
116
- } else {
117
- // Fallback: copy from PWN's own devcontainer
118
- const pwmDevcontainerDir = join(__dirname, '..', '.devcontainer');
119
- cpSync(join(pwmDevcontainerDir, 'devcontainer.json'), join(devcontainerDir, 'devcontainer.json'));
120
- cpSync(join(pwmDevcontainerDir, 'setup-codespace.sh'), join(devcontainerDir, 'setup.sh'));
121
- }
122
-
123
- // Update project name in devcontainer.json
124
- const configPath = join(devcontainerDir, 'devcontainer.json');
125
- const config = JSON.parse(readFileSync(configPath, 'utf8'));
126
- config.name = getProjectName();
127
- writeFileSync(configPath, JSON.stringify(config, null, 2));
128
-
129
- console.log('Created devcontainer.json');
130
- console.log('Created setup.sh');
131
- }
132
-
133
- // Make setup.sh executable (on Unix systems)
134
- try {
135
- const setupPath = join(devcontainerDir, 'setup.sh');
136
- if (existsSync(setupPath)) {
137
- chmodSync(setupPath, '755');
138
- }
139
- } catch {
140
- // Windows doesn't support chmod
141
- }
142
-
143
- console.log('\nCodespaces configuration ready!');
144
- console.log('\nNext steps:');
145
- console.log(' 1. Commit the .devcontainer/ folder');
146
- console.log(' 2. Push to GitHub');
147
- console.log(' 3. Open in Codespace: gh codespace create');
148
- console.log('');
149
- }
150
-
151
- async function showStatus() {
152
- console.log('Codespaces Configuration Status\n');
153
-
154
- const devcontainerDir = join(cwd, '.devcontainer');
155
- const configPath = join(devcontainerDir, 'devcontainer.json');
156
-
157
- // Check devcontainer
158
- if (!existsSync(devcontainerDir)) {
159
- console.log('Status: Not configured');
160
- console.log('\nRun: pwn codespaces init');
161
- return;
162
- }
163
-
164
- console.log('Status: Configured');
165
- console.log(`Path: ${devcontainerDir}`);
166
-
167
- // Parse config
168
- if (existsSync(configPath)) {
169
- try {
170
- const config = JSON.parse(readFileSync(configPath, 'utf8'));
171
- console.log(`\nContainer: ${config.name || 'Unnamed'}`);
172
- console.log(`Image: ${config.image || 'Custom Dockerfile'}`);
173
-
174
- if (config.forwardPorts?.length) {
175
- console.log(`Ports: ${config.forwardPorts.join(', ')}`);
176
- }
177
-
178
- if (config.customizations?.vscode?.extensions?.length) {
179
- console.log(`Extensions: ${config.customizations.vscode.extensions.length} configured`);
180
- }
181
- } catch {
182
- console.log('Warning: Could not parse devcontainer.json');
183
- }
184
- }
185
-
186
- // Check for Dockerfile
187
- if (existsSync(join(devcontainerDir, 'Dockerfile'))) {
188
- console.log('Dockerfile: Yes (custom build)');
189
- }
190
-
191
- // Check for setup script
192
- if (existsSync(join(devcontainerDir, 'setup.sh'))) {
193
- console.log('Setup script: Yes');
194
- }
195
-
196
- // Check if in a codespace
197
- if (process.env.PWN_CODESPACE === 'true' || process.env.CODESPACES === 'true') {
198
- console.log('\nCurrently running in a Codespace!');
199
- }
200
-
201
- // Check GitHub remote
202
- try {
203
- const remote = execSync('git remote get-url origin', { encoding: 'utf8' }).trim();
204
- if (remote.includes('github.com')) {
205
- const match = remote.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
206
- if (match) {
207
- console.log(`\nGitHub: ${match[1]}/${match[2]}`);
208
- console.log(`Open: https://github.com/codespaces/new?repo=${match[1]}/${match[2]}`);
209
- }
210
- }
211
- } catch {
212
- // Not a git repo or no remote
213
- }
214
-
215
- console.log('');
216
- }
217
-
218
- async function openCodespace(args) {
219
- // Check for gh CLI
220
- try {
221
- execSync('gh --version', { stdio: 'ignore' });
222
- } catch {
223
- console.log('GitHub CLI (gh) not found.');
224
- console.log('Install from: https://cli.github.com/');
225
- return;
226
- }
227
-
228
- // Check auth
229
- try {
230
- execSync('gh auth status', { stdio: 'ignore' });
231
- } catch {
232
- console.log('Not authenticated with GitHub CLI.');
233
- console.log('Run: gh auth login');
234
- return;
235
- }
236
-
237
- console.log('Opening project in Codespace...\n');
238
-
239
- try {
240
- // Check if there's an existing codespace
241
- const existing = execSync('gh codespace list --json name,repository -q ".[] | select(.repository | contains(\\"' + getRepoName() + '\\"))"', {
242
- encoding: 'utf8'
243
- }).trim();
244
-
245
- if (existing) {
246
- console.log('Found existing codespace. Opening...');
247
- execSync('gh codespace code', { stdio: 'inherit' });
248
- } else {
249
- console.log('Creating new codespace...');
250
- execSync('gh codespace create --repo ' + getRepoName(), { stdio: 'inherit' });
251
- }
252
- } catch (error) {
253
- console.log('Error:', error.message);
254
- console.log('\nTry manually: gh codespace create');
255
- }
256
- }
257
-
258
- async function listCodespaces() {
259
- try {
260
- execSync('gh --version', { stdio: 'ignore' });
261
- } catch {
262
- console.log('GitHub CLI (gh) not found.');
263
- console.log('Install from: https://cli.github.com/');
264
- return;
265
- }
266
-
267
- console.log('Active Codespaces:\n');
268
-
269
- try {
270
- execSync('gh codespace list', { stdio: 'inherit' });
271
- } catch {
272
- console.log('Could not list codespaces. Make sure you are authenticated.');
273
- console.log('Run: gh auth login');
274
- }
275
- }
276
-
277
- function getProjectName() {
278
- try {
279
- const pkgPath = join(cwd, 'package.json');
280
- if (existsSync(pkgPath)) {
281
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
282
- return pkg.name || 'Development';
283
- }
284
- } catch {
285
- // ignore
286
- }
287
-
288
- // Use directory name
289
- return cwd.split(/[/\\]/).pop() || 'Development';
290
- }
291
-
292
- function getRepoName() {
293
- try {
294
- const remote = execSync('git remote get-url origin', { encoding: 'utf8' }).trim();
295
- const match = remote.match(/github\.com[:/]([^/]+\/[^/.]+)/);
296
- if (match) {
297
- return match[1].replace('.git', '');
298
- }
299
- } catch {
300
- // ignore
301
- }
302
- return '';
303
- }