@fermindi/pwn-cli 0.1.0 → 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.
Files changed (46) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +265 -251
  3. package/cli/batch.js +333 -333
  4. package/cli/codespaces.js +303 -303
  5. package/cli/index.js +98 -91
  6. package/cli/inject.js +78 -53
  7. package/cli/knowledge.js +531 -531
  8. package/cli/migrate.js +466 -0
  9. package/cli/notify.js +135 -135
  10. package/cli/patterns.js +665 -665
  11. package/cli/status.js +91 -91
  12. package/cli/validate.js +61 -61
  13. package/package.json +70 -70
  14. package/src/core/inject.js +208 -128
  15. package/src/core/state.js +91 -91
  16. package/src/core/validate.js +202 -202
  17. package/src/core/workspace.js +176 -176
  18. package/src/index.js +20 -20
  19. package/src/knowledge/gc.js +308 -308
  20. package/src/knowledge/lifecycle.js +401 -401
  21. package/src/knowledge/promote.js +364 -364
  22. package/src/knowledge/references.js +342 -342
  23. package/src/patterns/matcher.js +218 -218
  24. package/src/patterns/registry.js +375 -375
  25. package/src/patterns/triggers.js +423 -423
  26. package/src/services/batch-service.js +849 -849
  27. package/src/services/notification-service.js +342 -342
  28. package/templates/codespaces/devcontainer.json +52 -52
  29. package/templates/codespaces/setup.sh +70 -70
  30. package/templates/workspace/.ai/README.md +164 -164
  31. package/templates/workspace/.ai/agents/README.md +204 -204
  32. package/templates/workspace/.ai/agents/claude.md +625 -625
  33. package/templates/workspace/.ai/config/README.md +79 -79
  34. package/templates/workspace/.ai/config/notifications.template.json +20 -20
  35. package/templates/workspace/.ai/memory/deadends.md +79 -79
  36. package/templates/workspace/.ai/memory/decisions.md +58 -58
  37. package/templates/workspace/.ai/memory/patterns.md +65 -65
  38. package/templates/workspace/.ai/patterns/backend/README.md +126 -126
  39. package/templates/workspace/.ai/patterns/frontend/README.md +103 -103
  40. package/templates/workspace/.ai/patterns/index.md +256 -256
  41. package/templates/workspace/.ai/patterns/triggers.json +1087 -1087
  42. package/templates/workspace/.ai/patterns/universal/README.md +141 -141
  43. package/templates/workspace/.ai/state.template.json +8 -8
  44. package/templates/workspace/.ai/tasks/active.md +77 -77
  45. package/templates/workspace/.ai/tasks/backlog.md +95 -95
  46. package/templates/workspace/.ai/workflows/batch-task.md +356 -356
@@ -1,202 +1,202 @@
1
- import { existsSync, statSync, readFileSync } from 'fs';
2
- import { join } from 'path';
3
-
4
- /**
5
- * Expected workspace structure
6
- */
7
- const REQUIRED_STRUCTURE = {
8
- files: [
9
- 'README.md',
10
- 'state.json'
11
- ],
12
- directories: [
13
- 'memory',
14
- 'tasks',
15
- 'patterns',
16
- 'workflows',
17
- 'agents',
18
- 'config'
19
- ],
20
- memoryFiles: [
21
- 'memory/decisions.md',
22
- 'memory/patterns.md',
23
- 'memory/deadends.md'
24
- ],
25
- taskFiles: [
26
- 'tasks/active.md',
27
- 'tasks/backlog.md'
28
- ],
29
- agentFiles: [
30
- 'agents/README.md',
31
- 'agents/claude.md'
32
- ],
33
- patternFiles: [
34
- 'patterns/index.md'
35
- ]
36
- };
37
-
38
- /**
39
- * Validate a PWN workspace structure
40
- * @param {string} cwd - Working directory
41
- * @returns {object} Validation result with issues array
42
- */
43
- export function validate(cwd = process.cwd()) {
44
- const aiDir = join(cwd, '.ai');
45
- const issues = [];
46
- const warnings = [];
47
-
48
- // Check if .ai/ exists
49
- if (!existsSync(aiDir)) {
50
- return {
51
- valid: false,
52
- issues: ['No .ai/ directory found. Run: pwn inject'],
53
- warnings: []
54
- };
55
- }
56
-
57
- // Check required files
58
- for (const file of REQUIRED_STRUCTURE.files) {
59
- const filePath = join(aiDir, file);
60
- if (!existsSync(filePath)) {
61
- issues.push(`Missing file: .ai/${file}`);
62
- }
63
- }
64
-
65
- // Check required directories
66
- for (const dir of REQUIRED_STRUCTURE.directories) {
67
- const dirPath = join(aiDir, dir);
68
- if (!existsSync(dirPath)) {
69
- issues.push(`Missing directory: .ai/${dir}/`);
70
- } else if (!statSync(dirPath).isDirectory()) {
71
- issues.push(`.ai/${dir} exists but is not a directory`);
72
- }
73
- }
74
-
75
- // Check memory files
76
- for (const file of REQUIRED_STRUCTURE.memoryFiles) {
77
- const filePath = join(aiDir, file);
78
- if (!existsSync(filePath)) {
79
- warnings.push(`Missing memory file: .ai/${file}`);
80
- }
81
- }
82
-
83
- // Check task files
84
- for (const file of REQUIRED_STRUCTURE.taskFiles) {
85
- const filePath = join(aiDir, file);
86
- if (!existsSync(filePath)) {
87
- warnings.push(`Missing task file: .ai/${file}`);
88
- }
89
- }
90
-
91
- // Check agent files
92
- for (const file of REQUIRED_STRUCTURE.agentFiles) {
93
- const filePath = join(aiDir, file);
94
- if (!existsSync(filePath)) {
95
- warnings.push(`Missing agent file: .ai/${file}`);
96
- }
97
- }
98
-
99
- // Check pattern files
100
- for (const file of REQUIRED_STRUCTURE.patternFiles) {
101
- const filePath = join(aiDir, file);
102
- if (!existsSync(filePath)) {
103
- warnings.push(`Missing pattern file: .ai/${file}`);
104
- }
105
- }
106
-
107
- // Validate state.json format
108
- const stateValidation = validateStateJson(aiDir);
109
- if (!stateValidation.valid) {
110
- issues.push(...stateValidation.issues);
111
- }
112
-
113
- return {
114
- valid: issues.length === 0,
115
- issues,
116
- warnings
117
- };
118
- }
119
-
120
- /**
121
- * Validate state.json structure and content
122
- * @param {string} aiDir - Path to .ai directory
123
- * @returns {object} Validation result
124
- */
125
- function validateStateJson(aiDir) {
126
- const statePath = join(aiDir, 'state.json');
127
- const issues = [];
128
-
129
- if (!existsSync(statePath)) {
130
- return { valid: false, issues: ['state.json not found'] };
131
- }
132
-
133
- try {
134
- const content = readFileSync(statePath, 'utf8');
135
- const state = JSON.parse(content);
136
-
137
- // Check required fields
138
- const requiredFields = ['developer', 'session_started'];
139
- for (const field of requiredFields) {
140
- if (!(field in state)) {
141
- issues.push(`state.json missing required field: ${field}`);
142
- }
143
- }
144
-
145
- // Validate date format
146
- if (state.session_started && isNaN(Date.parse(state.session_started))) {
147
- issues.push('state.json: session_started is not a valid date');
148
- }
149
-
150
- } catch (error) {
151
- if (error instanceof SyntaxError) {
152
- issues.push('state.json contains invalid JSON');
153
- } else {
154
- issues.push(`state.json read error: ${error.message}`);
155
- }
156
- }
157
-
158
- return {
159
- valid: issues.length === 0,
160
- issues
161
- };
162
- }
163
-
164
- /**
165
- * Get a detailed report of workspace structure
166
- * @param {string} cwd - Working directory
167
- * @returns {object} Structure report
168
- */
169
- export function getStructureReport(cwd = process.cwd()) {
170
- const aiDir = join(cwd, '.ai');
171
- const report = {
172
- exists: existsSync(aiDir),
173
- directories: {},
174
- files: {}
175
- };
176
-
177
- if (!report.exists) {
178
- return report;
179
- }
180
-
181
- // Check directories
182
- for (const dir of REQUIRED_STRUCTURE.directories) {
183
- const dirPath = join(aiDir, dir);
184
- report.directories[dir] = existsSync(dirPath) && statSync(dirPath).isDirectory();
185
- }
186
-
187
- // Check all expected files
188
- const allFiles = [
189
- ...REQUIRED_STRUCTURE.files,
190
- ...REQUIRED_STRUCTURE.memoryFiles,
191
- ...REQUIRED_STRUCTURE.taskFiles,
192
- ...REQUIRED_STRUCTURE.agentFiles,
193
- ...REQUIRED_STRUCTURE.patternFiles
194
- ];
195
-
196
- for (const file of allFiles) {
197
- const filePath = join(aiDir, file);
198
- report.files[file] = existsSync(filePath);
199
- }
200
-
201
- return report;
202
- }
1
+ import { existsSync, statSync, readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ /**
5
+ * Expected workspace structure
6
+ */
7
+ const REQUIRED_STRUCTURE = {
8
+ files: [
9
+ 'README.md',
10
+ 'state.json'
11
+ ],
12
+ directories: [
13
+ 'memory',
14
+ 'tasks',
15
+ 'patterns',
16
+ 'workflows',
17
+ 'agents',
18
+ 'config'
19
+ ],
20
+ memoryFiles: [
21
+ 'memory/decisions.md',
22
+ 'memory/patterns.md',
23
+ 'memory/deadends.md'
24
+ ],
25
+ taskFiles: [
26
+ 'tasks/active.md',
27
+ 'tasks/backlog.md'
28
+ ],
29
+ agentFiles: [
30
+ 'agents/README.md',
31
+ 'agents/claude.md'
32
+ ],
33
+ patternFiles: [
34
+ 'patterns/index.md'
35
+ ]
36
+ };
37
+
38
+ /**
39
+ * Validate a PWN workspace structure
40
+ * @param {string} cwd - Working directory
41
+ * @returns {object} Validation result with issues array
42
+ */
43
+ export function validate(cwd = process.cwd()) {
44
+ const aiDir = join(cwd, '.ai');
45
+ const issues = [];
46
+ const warnings = [];
47
+
48
+ // Check if .ai/ exists
49
+ if (!existsSync(aiDir)) {
50
+ return {
51
+ valid: false,
52
+ issues: ['No .ai/ directory found. Run: pwn inject'],
53
+ warnings: []
54
+ };
55
+ }
56
+
57
+ // Check required files
58
+ for (const file of REQUIRED_STRUCTURE.files) {
59
+ const filePath = join(aiDir, file);
60
+ if (!existsSync(filePath)) {
61
+ issues.push(`Missing file: .ai/${file}`);
62
+ }
63
+ }
64
+
65
+ // Check required directories
66
+ for (const dir of REQUIRED_STRUCTURE.directories) {
67
+ const dirPath = join(aiDir, dir);
68
+ if (!existsSync(dirPath)) {
69
+ issues.push(`Missing directory: .ai/${dir}/`);
70
+ } else if (!statSync(dirPath).isDirectory()) {
71
+ issues.push(`.ai/${dir} exists but is not a directory`);
72
+ }
73
+ }
74
+
75
+ // Check memory files
76
+ for (const file of REQUIRED_STRUCTURE.memoryFiles) {
77
+ const filePath = join(aiDir, file);
78
+ if (!existsSync(filePath)) {
79
+ warnings.push(`Missing memory file: .ai/${file}`);
80
+ }
81
+ }
82
+
83
+ // Check task files
84
+ for (const file of REQUIRED_STRUCTURE.taskFiles) {
85
+ const filePath = join(aiDir, file);
86
+ if (!existsSync(filePath)) {
87
+ warnings.push(`Missing task file: .ai/${file}`);
88
+ }
89
+ }
90
+
91
+ // Check agent files
92
+ for (const file of REQUIRED_STRUCTURE.agentFiles) {
93
+ const filePath = join(aiDir, file);
94
+ if (!existsSync(filePath)) {
95
+ warnings.push(`Missing agent file: .ai/${file}`);
96
+ }
97
+ }
98
+
99
+ // Check pattern files
100
+ for (const file of REQUIRED_STRUCTURE.patternFiles) {
101
+ const filePath = join(aiDir, file);
102
+ if (!existsSync(filePath)) {
103
+ warnings.push(`Missing pattern file: .ai/${file}`);
104
+ }
105
+ }
106
+
107
+ // Validate state.json format
108
+ const stateValidation = validateStateJson(aiDir);
109
+ if (!stateValidation.valid) {
110
+ issues.push(...stateValidation.issues);
111
+ }
112
+
113
+ return {
114
+ valid: issues.length === 0,
115
+ issues,
116
+ warnings
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Validate state.json structure and content
122
+ * @param {string} aiDir - Path to .ai directory
123
+ * @returns {object} Validation result
124
+ */
125
+ function validateStateJson(aiDir) {
126
+ const statePath = join(aiDir, 'state.json');
127
+ const issues = [];
128
+
129
+ if (!existsSync(statePath)) {
130
+ return { valid: false, issues: ['state.json not found'] };
131
+ }
132
+
133
+ try {
134
+ const content = readFileSync(statePath, 'utf8');
135
+ const state = JSON.parse(content);
136
+
137
+ // Check required fields
138
+ const requiredFields = ['developer', 'session_started'];
139
+ for (const field of requiredFields) {
140
+ if (!(field in state)) {
141
+ issues.push(`state.json missing required field: ${field}`);
142
+ }
143
+ }
144
+
145
+ // Validate date format
146
+ if (state.session_started && isNaN(Date.parse(state.session_started))) {
147
+ issues.push('state.json: session_started is not a valid date');
148
+ }
149
+
150
+ } catch (error) {
151
+ if (error instanceof SyntaxError) {
152
+ issues.push('state.json contains invalid JSON');
153
+ } else {
154
+ issues.push(`state.json read error: ${error.message}`);
155
+ }
156
+ }
157
+
158
+ return {
159
+ valid: issues.length === 0,
160
+ issues
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Get a detailed report of workspace structure
166
+ * @param {string} cwd - Working directory
167
+ * @returns {object} Structure report
168
+ */
169
+ export function getStructureReport(cwd = process.cwd()) {
170
+ const aiDir = join(cwd, '.ai');
171
+ const report = {
172
+ exists: existsSync(aiDir),
173
+ directories: {},
174
+ files: {}
175
+ };
176
+
177
+ if (!report.exists) {
178
+ return report;
179
+ }
180
+
181
+ // Check directories
182
+ for (const dir of REQUIRED_STRUCTURE.directories) {
183
+ const dirPath = join(aiDir, dir);
184
+ report.directories[dir] = existsSync(dirPath) && statSync(dirPath).isDirectory();
185
+ }
186
+
187
+ // Check all expected files
188
+ const allFiles = [
189
+ ...REQUIRED_STRUCTURE.files,
190
+ ...REQUIRED_STRUCTURE.memoryFiles,
191
+ ...REQUIRED_STRUCTURE.taskFiles,
192
+ ...REQUIRED_STRUCTURE.agentFiles,
193
+ ...REQUIRED_STRUCTURE.patternFiles
194
+ ];
195
+
196
+ for (const file of allFiles) {
197
+ const filePath = join(aiDir, file);
198
+ report.files[file] = existsSync(filePath);
199
+ }
200
+
201
+ return report;
202
+ }