@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.
- 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 -53
- 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 -128
- 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/src/core/workspace.js
CHANGED
|
@@ -1,176 +1,176 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { getState } from './state.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Get comprehensive workspace information
|
|
7
|
-
* @param {string} cwd - Working directory
|
|
8
|
-
* @returns {object} Workspace info
|
|
9
|
-
*/
|
|
10
|
-
export function getWorkspaceInfo(cwd = process.cwd()) {
|
|
11
|
-
const aiDir = join(cwd, '.ai');
|
|
12
|
-
|
|
13
|
-
if (!existsSync(aiDir)) {
|
|
14
|
-
return {
|
|
15
|
-
exists: false,
|
|
16
|
-
path: aiDir
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const state = getState(cwd);
|
|
21
|
-
const tasks = getTasksSummary(cwd);
|
|
22
|
-
const memory = getMemorySummary(cwd);
|
|
23
|
-
const patterns = getPatternsSummary(cwd);
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
exists: true,
|
|
27
|
-
path: aiDir,
|
|
28
|
-
state,
|
|
29
|
-
tasks,
|
|
30
|
-
memory,
|
|
31
|
-
patterns
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Get summary of active tasks
|
|
37
|
-
* @param {string} cwd - Working directory
|
|
38
|
-
* @returns {object} Tasks summary
|
|
39
|
-
*/
|
|
40
|
-
function getTasksSummary(cwd) {
|
|
41
|
-
const activePath = join(cwd, '.ai', 'tasks', 'active.md');
|
|
42
|
-
const backlogPath = join(cwd, '.ai', 'tasks', 'backlog.md');
|
|
43
|
-
|
|
44
|
-
const summary = {
|
|
45
|
-
active: { total: 0, completed: 0, pending: 0 },
|
|
46
|
-
backlog: { total: 0 }
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Parse active.md
|
|
50
|
-
if (existsSync(activePath)) {
|
|
51
|
-
const content = readFileSync(activePath, 'utf8');
|
|
52
|
-
const lines = content.split('\n');
|
|
53
|
-
|
|
54
|
-
for (const line of lines) {
|
|
55
|
-
if (line.match(/^- \[[ x]\]/)) {
|
|
56
|
-
summary.active.total++;
|
|
57
|
-
if (line.match(/^- \[x\]/i)) {
|
|
58
|
-
summary.active.completed++;
|
|
59
|
-
} else {
|
|
60
|
-
summary.active.pending++;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Parse backlog.md
|
|
67
|
-
if (existsSync(backlogPath)) {
|
|
68
|
-
const content = readFileSync(backlogPath, 'utf8');
|
|
69
|
-
const lines = content.split('\n');
|
|
70
|
-
|
|
71
|
-
for (const line of lines) {
|
|
72
|
-
if (line.match(/^- \[[ ]\]/) || line.match(/^\d+\./)) {
|
|
73
|
-
summary.backlog.total++;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return summary;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Get summary of memory files
|
|
83
|
-
* @param {string} cwd - Working directory
|
|
84
|
-
* @returns {object} Memory summary
|
|
85
|
-
*/
|
|
86
|
-
function getMemorySummary(cwd) {
|
|
87
|
-
const memoryDir = join(cwd, '.ai', 'memory');
|
|
88
|
-
|
|
89
|
-
const summary = {
|
|
90
|
-
decisions: 0,
|
|
91
|
-
patterns: 0,
|
|
92
|
-
deadends: 0
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
// Count decisions (DEC-XXX format)
|
|
96
|
-
const decisionsPath = join(memoryDir, 'decisions.md');
|
|
97
|
-
if (existsSync(decisionsPath)) {
|
|
98
|
-
const content = readFileSync(decisionsPath, 'utf8');
|
|
99
|
-
const matches = content.match(/DEC-\d+/g);
|
|
100
|
-
summary.decisions = matches ? matches.length : 0;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Count patterns (### headers in patterns.md)
|
|
104
|
-
const patternsPath = join(memoryDir, 'patterns.md');
|
|
105
|
-
if (existsSync(patternsPath)) {
|
|
106
|
-
const content = readFileSync(patternsPath, 'utf8');
|
|
107
|
-
const matches = content.match(/^### /gm);
|
|
108
|
-
summary.patterns = matches ? matches.length : 0;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Count dead-ends (DE-XXX format)
|
|
112
|
-
const deadendsPath = join(memoryDir, 'deadends.md');
|
|
113
|
-
if (existsSync(deadendsPath)) {
|
|
114
|
-
const content = readFileSync(deadendsPath, 'utf8');
|
|
115
|
-
const matches = content.match(/DE-\d+/g);
|
|
116
|
-
summary.deadends = matches ? matches.length : 0;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return summary;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Get summary of pattern triggers
|
|
124
|
-
* @param {string} cwd - Working directory
|
|
125
|
-
* @returns {object} Patterns summary
|
|
126
|
-
*/
|
|
127
|
-
function getPatternsSummary(cwd) {
|
|
128
|
-
const patternsDir = join(cwd, '.ai', 'patterns');
|
|
129
|
-
|
|
130
|
-
const summary = {
|
|
131
|
-
triggers: 0,
|
|
132
|
-
categories: []
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
// Count triggers in index.md
|
|
136
|
-
const indexPath = join(patternsDir, 'index.md');
|
|
137
|
-
if (existsSync(indexPath)) {
|
|
138
|
-
const content = readFileSync(indexPath, 'utf8');
|
|
139
|
-
// Count lines that look like trigger mappings (| pattern | directory |)
|
|
140
|
-
const matches = content.match(/^\|[^|]+\|[^|]+\|$/gm);
|
|
141
|
-
// Subtract header rows (usually 2)
|
|
142
|
-
summary.triggers = matches ? Math.max(0, matches.length - 2) : 0;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// List pattern categories (subdirectories)
|
|
146
|
-
if (existsSync(patternsDir)) {
|
|
147
|
-
const entries = readdirSync(patternsDir);
|
|
148
|
-
for (const entry of entries) {
|
|
149
|
-
const entryPath = join(patternsDir, entry);
|
|
150
|
-
if (statSync(entryPath).isDirectory()) {
|
|
151
|
-
summary.categories.push(entry);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return summary;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Check if current directory is inside a PWN workspace
|
|
161
|
-
* @param {string} cwd - Starting directory
|
|
162
|
-
* @returns {string|null} Path to .ai directory or null
|
|
163
|
-
*/
|
|
164
|
-
export function findWorkspaceRoot(cwd = process.cwd()) {
|
|
165
|
-
let current = cwd;
|
|
166
|
-
|
|
167
|
-
while (current !== join(current, '..')) {
|
|
168
|
-
const aiDir = join(current, '.ai');
|
|
169
|
-
if (existsSync(aiDir)) {
|
|
170
|
-
return current;
|
|
171
|
-
}
|
|
172
|
-
current = join(current, '..');
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { getState } from './state.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Get comprehensive workspace information
|
|
7
|
+
* @param {string} cwd - Working directory
|
|
8
|
+
* @returns {object} Workspace info
|
|
9
|
+
*/
|
|
10
|
+
export function getWorkspaceInfo(cwd = process.cwd()) {
|
|
11
|
+
const aiDir = join(cwd, '.ai');
|
|
12
|
+
|
|
13
|
+
if (!existsSync(aiDir)) {
|
|
14
|
+
return {
|
|
15
|
+
exists: false,
|
|
16
|
+
path: aiDir
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const state = getState(cwd);
|
|
21
|
+
const tasks = getTasksSummary(cwd);
|
|
22
|
+
const memory = getMemorySummary(cwd);
|
|
23
|
+
const patterns = getPatternsSummary(cwd);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
exists: true,
|
|
27
|
+
path: aiDir,
|
|
28
|
+
state,
|
|
29
|
+
tasks,
|
|
30
|
+
memory,
|
|
31
|
+
patterns
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get summary of active tasks
|
|
37
|
+
* @param {string} cwd - Working directory
|
|
38
|
+
* @returns {object} Tasks summary
|
|
39
|
+
*/
|
|
40
|
+
function getTasksSummary(cwd) {
|
|
41
|
+
const activePath = join(cwd, '.ai', 'tasks', 'active.md');
|
|
42
|
+
const backlogPath = join(cwd, '.ai', 'tasks', 'backlog.md');
|
|
43
|
+
|
|
44
|
+
const summary = {
|
|
45
|
+
active: { total: 0, completed: 0, pending: 0 },
|
|
46
|
+
backlog: { total: 0 }
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Parse active.md
|
|
50
|
+
if (existsSync(activePath)) {
|
|
51
|
+
const content = readFileSync(activePath, 'utf8');
|
|
52
|
+
const lines = content.split('\n');
|
|
53
|
+
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
if (line.match(/^- \[[ x]\]/)) {
|
|
56
|
+
summary.active.total++;
|
|
57
|
+
if (line.match(/^- \[x\]/i)) {
|
|
58
|
+
summary.active.completed++;
|
|
59
|
+
} else {
|
|
60
|
+
summary.active.pending++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Parse backlog.md
|
|
67
|
+
if (existsSync(backlogPath)) {
|
|
68
|
+
const content = readFileSync(backlogPath, 'utf8');
|
|
69
|
+
const lines = content.split('\n');
|
|
70
|
+
|
|
71
|
+
for (const line of lines) {
|
|
72
|
+
if (line.match(/^- \[[ ]\]/) || line.match(/^\d+\./)) {
|
|
73
|
+
summary.backlog.total++;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return summary;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get summary of memory files
|
|
83
|
+
* @param {string} cwd - Working directory
|
|
84
|
+
* @returns {object} Memory summary
|
|
85
|
+
*/
|
|
86
|
+
function getMemorySummary(cwd) {
|
|
87
|
+
const memoryDir = join(cwd, '.ai', 'memory');
|
|
88
|
+
|
|
89
|
+
const summary = {
|
|
90
|
+
decisions: 0,
|
|
91
|
+
patterns: 0,
|
|
92
|
+
deadends: 0
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Count decisions (DEC-XXX format)
|
|
96
|
+
const decisionsPath = join(memoryDir, 'decisions.md');
|
|
97
|
+
if (existsSync(decisionsPath)) {
|
|
98
|
+
const content = readFileSync(decisionsPath, 'utf8');
|
|
99
|
+
const matches = content.match(/DEC-\d+/g);
|
|
100
|
+
summary.decisions = matches ? matches.length : 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Count patterns (### headers in patterns.md)
|
|
104
|
+
const patternsPath = join(memoryDir, 'patterns.md');
|
|
105
|
+
if (existsSync(patternsPath)) {
|
|
106
|
+
const content = readFileSync(patternsPath, 'utf8');
|
|
107
|
+
const matches = content.match(/^### /gm);
|
|
108
|
+
summary.patterns = matches ? matches.length : 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Count dead-ends (DE-XXX format)
|
|
112
|
+
const deadendsPath = join(memoryDir, 'deadends.md');
|
|
113
|
+
if (existsSync(deadendsPath)) {
|
|
114
|
+
const content = readFileSync(deadendsPath, 'utf8');
|
|
115
|
+
const matches = content.match(/DE-\d+/g);
|
|
116
|
+
summary.deadends = matches ? matches.length : 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return summary;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get summary of pattern triggers
|
|
124
|
+
* @param {string} cwd - Working directory
|
|
125
|
+
* @returns {object} Patterns summary
|
|
126
|
+
*/
|
|
127
|
+
function getPatternsSummary(cwd) {
|
|
128
|
+
const patternsDir = join(cwd, '.ai', 'patterns');
|
|
129
|
+
|
|
130
|
+
const summary = {
|
|
131
|
+
triggers: 0,
|
|
132
|
+
categories: []
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Count triggers in index.md
|
|
136
|
+
const indexPath = join(patternsDir, 'index.md');
|
|
137
|
+
if (existsSync(indexPath)) {
|
|
138
|
+
const content = readFileSync(indexPath, 'utf8');
|
|
139
|
+
// Count lines that look like trigger mappings (| pattern | directory |)
|
|
140
|
+
const matches = content.match(/^\|[^|]+\|[^|]+\|$/gm);
|
|
141
|
+
// Subtract header rows (usually 2)
|
|
142
|
+
summary.triggers = matches ? Math.max(0, matches.length - 2) : 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// List pattern categories (subdirectories)
|
|
146
|
+
if (existsSync(patternsDir)) {
|
|
147
|
+
const entries = readdirSync(patternsDir);
|
|
148
|
+
for (const entry of entries) {
|
|
149
|
+
const entryPath = join(patternsDir, entry);
|
|
150
|
+
if (statSync(entryPath).isDirectory()) {
|
|
151
|
+
summary.categories.push(entry);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return summary;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Check if current directory is inside a PWN workspace
|
|
161
|
+
* @param {string} cwd - Starting directory
|
|
162
|
+
* @returns {string|null} Path to .ai directory or null
|
|
163
|
+
*/
|
|
164
|
+
export function findWorkspaceRoot(cwd = process.cwd()) {
|
|
165
|
+
let current = cwd;
|
|
166
|
+
|
|
167
|
+
while (current !== join(current, '..')) {
|
|
168
|
+
const aiDir = join(current, '.ai');
|
|
169
|
+
if (existsSync(aiDir)) {
|
|
170
|
+
return current;
|
|
171
|
+
}
|
|
172
|
+
current = join(current, '..');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return null;
|
|
176
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
// PWN Core - Main exports
|
|
2
|
-
export { inject } from './core/inject.js';
|
|
3
|
-
export { getState, updateState, initState } from './core/state.js';
|
|
4
|
-
export { validate } from './core/validate.js';
|
|
5
|
-
export { getWorkspaceInfo } from './core/workspace.js';
|
|
6
|
-
|
|
7
|
-
// Services
|
|
8
|
-
export * as notifications from './services/notification-service.js';
|
|
9
|
-
export * as batch from './services/batch-service.js';
|
|
10
|
-
|
|
11
|
-
// Patterns
|
|
12
|
-
export * as patterns from './patterns/registry.js';
|
|
13
|
-
export * as triggers from './patterns/triggers.js';
|
|
14
|
-
export * as matcher from './patterns/matcher.js';
|
|
15
|
-
|
|
16
|
-
// Knowledge Lifecycle
|
|
17
|
-
export * as lifecycle from './knowledge/lifecycle.js';
|
|
18
|
-
export * as references from './knowledge/references.js';
|
|
19
|
-
export * as gc from './knowledge/gc.js';
|
|
20
|
-
export * as promote from './knowledge/promote.js';
|
|
1
|
+
// PWN Core - Main exports
|
|
2
|
+
export { inject, detectKnownAIFiles, KNOWN_AI_FILES } from './core/inject.js';
|
|
3
|
+
export { getState, updateState, initState } from './core/state.js';
|
|
4
|
+
export { validate } from './core/validate.js';
|
|
5
|
+
export { getWorkspaceInfo } from './core/workspace.js';
|
|
6
|
+
|
|
7
|
+
// Services
|
|
8
|
+
export * as notifications from './services/notification-service.js';
|
|
9
|
+
export * as batch from './services/batch-service.js';
|
|
10
|
+
|
|
11
|
+
// Patterns
|
|
12
|
+
export * as patterns from './patterns/registry.js';
|
|
13
|
+
export * as triggers from './patterns/triggers.js';
|
|
14
|
+
export * as matcher from './patterns/matcher.js';
|
|
15
|
+
|
|
16
|
+
// Knowledge Lifecycle
|
|
17
|
+
export * as lifecycle from './knowledge/lifecycle.js';
|
|
18
|
+
export * as references from './knowledge/references.js';
|
|
19
|
+
export * as gc from './knowledge/gc.js';
|
|
20
|
+
export * as promote from './knowledge/promote.js';
|