@pcoliveira90/pdd 0.2.5 → 0.3.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,249 +1,270 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import readline from 'node:readline/promises';
4
- import { stdin as input, stdout as output } from 'node:process';
5
- import { CORE_TEMPLATES, IDE_ADAPTERS, PDD_TEMPLATE_VERSION } from '../core/template-registry.js';
6
- import { buildTemplateUpgradePlan, applyTemplateUpgradePlan } from '../core/template-upgrade.js';
7
- import {
8
- IDE_ORDER,
9
- IDE_LABELS,
10
- detectIdePresence,
11
- keysWhereDetected
12
- } from '../core/ide-detector.js';
13
-
14
- function ensureDir(filePath) {
15
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
16
- }
17
-
18
- function writeFile(baseDir, relativePath, content) {
19
- const fullPath = path.join(baseDir, relativePath);
20
- ensureDir(fullPath);
21
- fs.writeFileSync(fullPath, content, 'utf-8');
22
- }
23
-
24
- function normalizeIdeList(argv) {
25
- const ideArg = argv.find(arg => arg.startsWith('--ide='));
26
- if (!ideArg) return [];
27
-
28
- return (
29
- ideArg
30
- .split('=')[1]
31
- ?.split(',')
32
- .map(v => v.trim())
33
- .filter(Boolean) || []
34
- );
35
- }
36
-
37
- function readInstalledVersion(baseDir) {
38
- const versionFile = path.join(baseDir, '.pdd/version.json');
39
- if (!fs.existsSync(versionFile)) return null;
40
-
41
- try {
42
- return JSON.parse(fs.readFileSync(versionFile, 'utf-8')).templateVersion;
43
- } catch {
44
- return null;
45
- }
46
- }
47
-
48
- function installIdeAdapters(baseDir, ideList, force) {
49
- const results = [];
50
-
51
- for (const ide of ideList) {
52
- const adapter = IDE_ADAPTERS[ide];
53
- if (!adapter) {
54
- results.push({ path: `adapter:${ide}`, status: 'unknown' });
55
- continue;
56
- }
57
-
58
- for (const [file, content] of Object.entries(adapter)) {
59
- const fullPath = path.join(baseDir, file);
60
- if (!force && fs.existsSync(fullPath)) {
61
- results.push({ path: file, status: 'skipped' });
62
- continue;
63
- }
64
-
65
- writeFile(baseDir, file, content);
66
- results.push({ path: file, status: 'written' });
67
- }
68
- }
69
-
70
- return results;
71
- }
72
-
73
- function printUpgradeSummary(summary) {
74
- console.log('⬆️ PDD upgraded');
75
-
76
- summary.created.forEach(f => console.log(`✔️ created: ${f}`));
77
- summary.updated.forEach(f => console.log(`🔁 updated: ${f}`));
78
- summary.conflicts.forEach(f => console.log(`⚠️ conflict: ${f}`));
79
- summary.skipped.forEach(f => console.log(`⏭️ skipped: ${f}`));
80
-
81
- console.log('');
82
- console.log(`Summary:`);
83
- console.log(`- created: ${summary.created.length}`);
84
- console.log(`- updated: ${summary.updated.length}`);
85
- console.log(`- conflicts: ${summary.conflicts.length}`);
86
- console.log(`- skipped: ${summary.skipped.length}`);
87
-
88
- if (summary.conflicts.length > 0) {
89
- console.log('👉 Run with --force to overwrite conflicts');
90
- }
91
- }
92
-
93
- function resolveIdeSelectionFromInput(raw, presence) {
94
- const t = raw.trim().toLowerCase();
95
- if (t === '' || t === 'y' || t === 'yes' || t === 'sim' || t === 's') {
96
- return keysWhereDetected(presence);
97
- }
98
- if (t === 'n' || t === 'no' || t === 'nao') {
99
- return [];
100
- }
101
- if (t === 'a' || t === 'all' || t === 'todos') {
102
- return [...IDE_ORDER];
103
- }
104
-
105
- const tokens = t.split(/[\s,]+/).map(s => s.trim()).filter(Boolean);
106
- if (tokens.length > 0 && tokens.every(p => /^\d+$/.test(p))) {
107
- const selected = new Set();
108
- for (const p of tokens) {
109
- const n = parseInt(p, 10);
110
- if (n >= 1 && n <= IDE_ORDER.length) {
111
- selected.add(IDE_ORDER[n - 1]);
112
- }
113
- }
114
- return IDE_ORDER.filter(k => selected.has(k));
115
- }
116
-
117
- const names = t.split(',').map(s => s.trim()).filter(Boolean);
118
- if (names.length > 0) {
119
- const unknown = names.filter(id => !IDE_ADAPTERS[id]);
120
- if (unknown.length) {
121
- console.warn(`⚠️ IDE desconhecido(s): ${unknown.join(', ')} ignorado(s)`);
122
- }
123
- const known = names.filter(id => IDE_ADAPTERS[id]);
124
- if (known.length === 0) {
125
- return null;
126
- }
127
- return IDE_ORDER.filter(k => known.includes(k));
128
- }
129
-
130
- return null;
131
- }
132
-
133
- async function promptIdeSelection() {
134
- const presence = detectIdePresence();
135
- const suggested = keysWhereDetected(presence);
136
-
137
- console.log('');
138
- console.log('📦 PDD — adaptadores por IDE (prompts / comandos)');
139
- console.log('');
140
-
141
- IDE_ORDER.forEach((id, i) => {
142
- const label = IDE_LABELS[id];
143
- const ok = presence[id] ? 'sim' : 'não';
144
- console.log(` [${i + 1}] ${id.padEnd(8)} — ${label}`);
145
- console.log(` detetado nesta máquina: ${ok}`);
146
- });
147
-
148
- console.log('');
149
- if (suggested.length > 0) {
150
- console.log(`Sugestão (apenas detetados): ${suggested.join(', ')}`);
151
- } else {
152
- console.log('Nenhum IDE foi detetado automaticamente.');
153
- console.log('Pode ainda instalar adaptadores manualmente (útil para outro ambiente ou CI).');
154
- }
155
-
156
- console.log('');
157
- console.log('Opções:');
158
- console.log(' Enter → aceitar a sugestão');
159
- console.log(' a → instalar todos (cursor, claude, copilot)');
160
- console.log(' n → não instalar adaptadores');
161
- console.log(' 1,3 → números separados por vírgula ou espaço');
162
- console.log(' cursor,copilot nomes separados por vírgula');
163
- console.log('');
164
-
165
- const rl = readline.createInterface({ input, output });
166
- try {
167
- let answer = (await rl.question('> ')).trim();
168
- if (answer === '') {
169
- return suggested;
170
- }
171
-
172
- let resolved = resolveIdeSelectionFromInput(answer, presence);
173
- while (resolved === null) {
174
- console.log('Não percebi. Use Enter, a, n, números (ex.: 1,3) ou nomes (ex.: cursor,claude).');
175
- answer = (await rl.question('> ')).trim();
176
- if (answer === '') {
177
- return suggested;
178
- }
179
- resolved = resolveIdeSelectionFromInput(answer, presence);
180
- }
181
- return resolved;
182
- } finally {
183
- rl.close();
184
- }
185
- }
186
-
187
- async function resolveIdeListInteractive(argv) {
188
- if (argv.includes('--no-ide-prompt') || process.env.CI === 'true') {
189
- return [];
190
- }
191
- if (argv.includes('-y') || argv.includes('--yes')) {
192
- const presence = detectIdePresence();
193
- const detected = keysWhereDetected(presence);
194
- return detected.length > 0 ? detected : [...IDE_ORDER];
195
- }
196
- if (!process.stdin.isTTY) {
197
- return [];
198
- }
199
- return promptIdeSelection();
200
- }
201
-
202
- export async function runInit(argv = process.argv.slice(2)) {
203
- const cwd = process.cwd();
204
- const here = argv.includes('--here');
205
- const force = argv.includes('--force');
206
- const upgrade = argv.includes('--upgrade');
207
-
208
- const projectName = !here && argv[1] && !argv[1].startsWith('--') ? argv[1] : null;
209
- const baseDir = here ? cwd : path.join(cwd, projectName || 'pdd-project');
210
-
211
- if (!here && !fs.existsSync(baseDir)) {
212
- fs.mkdirSync(baseDir, { recursive: true });
213
- }
214
-
215
- const installedVersion = readInstalledVersion(baseDir);
216
-
217
- if (upgrade) {
218
- const plan = buildTemplateUpgradePlan(baseDir, CORE_TEMPLATES);
219
- const summary = applyTemplateUpgradePlan(baseDir, CORE_TEMPLATES, plan, force);
220
-
221
- writeFile(baseDir, '.pdd/version.json', JSON.stringify({ templateVersion: PDD_TEMPLATE_VERSION }, null, 2));
222
-
223
- printUpgradeSummary(summary);
224
-
225
- if (installedVersion === PDD_TEMPLATE_VERSION) {
226
- console.log('ℹ️ Templates were already up to date.');
227
- }
228
- } else {
229
- for (const [file, content] of Object.entries(CORE_TEMPLATES)) {
230
- writeFile(baseDir, file, content);
231
- }
232
-
233
- console.log('🚀 PDD initialized');
234
- }
235
-
236
- let ideList = normalizeIdeList(argv);
237
- if (ideList.length > 0) {
238
- const unknown = ideList.filter(id => !IDE_ADAPTERS[id]);
239
- if (unknown.length) {
240
- console.warn(`⚠️ Adaptador(es) desconhecido(s): ${unknown.join(', ')} — ignorado(s)`);
241
- }
242
- ideList = ideList.filter(id => IDE_ADAPTERS[id]);
243
- } else {
244
- ideList = await resolveIdeListInteractive(argv);
245
- }
246
-
247
- const ideResults = installIdeAdapters(baseDir, ideList, force);
248
- ideResults.forEach(r => console.log(`- ${r.status}: ${r.path}`));
249
- }
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import readline from 'node:readline/promises';
4
+ import { stdin as input, stdout as output } from 'node:process';
5
+ import { CORE_TEMPLATES, IDE_ADAPTERS, PDD_TEMPLATE_VERSION } from '../core/template-registry.js';
6
+ import { buildTemplateUpgradePlan, applyTemplateUpgradePlan } from '../core/template-upgrade.js';
7
+ import { runInitialProjectReviewAgent } from '../core/project-review-agent.js';
8
+ import {
9
+ IDE_ORDER,
10
+ IDE_LABELS,
11
+ detectIdePresence,
12
+ keysWhereDetected
13
+ } from '../core/ide-detector.js';
14
+
15
+ function ensureDir(filePath) {
16
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
17
+ }
18
+
19
+ function writeFile(baseDir, relativePath, content) {
20
+ const fullPath = path.join(baseDir, relativePath);
21
+ ensureDir(fullPath);
22
+ fs.writeFileSync(fullPath, content, 'utf-8');
23
+ }
24
+
25
+ function normalizeIdeList(argv) {
26
+ const ideArg = argv.find(arg => arg.startsWith('--ide='));
27
+ if (!ideArg) return [];
28
+
29
+ return (
30
+ ideArg
31
+ .split('=')[1]
32
+ ?.split(',')
33
+ .map(v => v.trim())
34
+ .filter(Boolean) || []
35
+ );
36
+ }
37
+
38
+ function readInstalledVersion(baseDir) {
39
+ const versionFile = path.join(baseDir, '.pdd/version.json');
40
+ if (!fs.existsSync(versionFile)) return null;
41
+
42
+ try {
43
+ return JSON.parse(fs.readFileSync(versionFile, 'utf-8')).templateVersion;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+
49
+ function installIdeAdapters(baseDir, ideList, force) {
50
+ const results = [];
51
+
52
+ for (const ide of ideList) {
53
+ const adapter = IDE_ADAPTERS[ide];
54
+ if (!adapter) {
55
+ results.push({ path: `adapter:${ide}`, status: 'unknown' });
56
+ continue;
57
+ }
58
+
59
+ for (const [file, content] of Object.entries(adapter)) {
60
+ const fullPath = path.join(baseDir, file);
61
+ if (!force && fs.existsSync(fullPath)) {
62
+ results.push({ path: file, status: 'skipped' });
63
+ continue;
64
+ }
65
+
66
+ writeFile(baseDir, file, content);
67
+ results.push({ path: file, status: 'written' });
68
+ }
69
+ }
70
+
71
+ return results;
72
+ }
73
+
74
+ function printUpgradeSummary(summary) {
75
+ console.log('⬆️ PDD upgraded');
76
+
77
+ summary.created.forEach(f => console.log(`✔️ created: ${f}`));
78
+ summary.updated.forEach(f => console.log(`🔁 updated: ${f}`));
79
+ summary.conflicts.forEach(f => console.log(`⚠️ conflict: ${f}`));
80
+ summary.skipped.forEach(f => console.log(`⏭️ skipped: ${f}`));
81
+
82
+ console.log('');
83
+ console.log(`Summary:`);
84
+ console.log(`- created: ${summary.created.length}`);
85
+ console.log(`- updated: ${summary.updated.length}`);
86
+ console.log(`- conflicts: ${summary.conflicts.length}`);
87
+ console.log(`- skipped: ${summary.skipped.length}`);
88
+
89
+ if (summary.conflicts.length > 0) {
90
+ console.log('👉 Run with --force to overwrite conflicts');
91
+ }
92
+ }
93
+
94
+ function ensureConstitution(baseDir, force = false) {
95
+ const constitutionPath = path.join(baseDir, '.pdd/constitution.md');
96
+ if (!force && fs.existsSync(constitutionPath)) {
97
+ return false;
98
+ }
99
+
100
+ writeFile(baseDir, '.pdd/constitution.md', CORE_TEMPLATES['.pdd/constitution.md']);
101
+ return true;
102
+ }
103
+
104
+ function resolveIdeSelectionFromInput(raw, presence) {
105
+ const t = raw.trim().toLowerCase();
106
+ if (t === '' || t === 'y' || t === 'yes' || t === 'sim' || t === 's') {
107
+ return keysWhereDetected(presence);
108
+ }
109
+ if (t === 'n' || t === 'no' || t === 'nao') {
110
+ return [];
111
+ }
112
+ if (t === 'a' || t === 'all' || t === 'todos') {
113
+ return [...IDE_ORDER];
114
+ }
115
+
116
+ const tokens = t.split(/[\s,]+/).map(s => s.trim()).filter(Boolean);
117
+ if (tokens.length > 0 && tokens.every(p => /^\d+$/.test(p))) {
118
+ const selected = new Set();
119
+ for (const p of tokens) {
120
+ const n = parseInt(p, 10);
121
+ if (n >= 1 && n <= IDE_ORDER.length) {
122
+ selected.add(IDE_ORDER[n - 1]);
123
+ }
124
+ }
125
+ return IDE_ORDER.filter(k => selected.has(k));
126
+ }
127
+
128
+ const names = t.split(',').map(s => s.trim()).filter(Boolean);
129
+ if (names.length > 0) {
130
+ const unknown = names.filter(id => !IDE_ADAPTERS[id]);
131
+ if (unknown.length) {
132
+ console.warn(`⚠️ IDE desconhecido(s): ${unknown.join(', ')} — ignorado(s)`);
133
+ }
134
+ const known = names.filter(id => IDE_ADAPTERS[id]);
135
+ if (known.length === 0) {
136
+ return null;
137
+ }
138
+ return IDE_ORDER.filter(k => known.includes(k));
139
+ }
140
+
141
+ return null;
142
+ }
143
+
144
+ async function promptIdeSelection() {
145
+ const presence = detectIdePresence();
146
+ const suggested = keysWhereDetected(presence);
147
+
148
+ console.log('');
149
+ console.log('📦 PDD — adaptadores por IDE (prompts / comandos)');
150
+ console.log('');
151
+
152
+ IDE_ORDER.forEach((id, i) => {
153
+ const label = IDE_LABELS[id];
154
+ const ok = presence[id] ? 'sim' : 'não';
155
+ console.log(` [${i + 1}] ${id.padEnd(8)} — ${label}`);
156
+ console.log(` detetado nesta máquina: ${ok}`);
157
+ });
158
+
159
+ console.log('');
160
+ if (suggested.length > 0) {
161
+ console.log(`Sugestão (apenas detetados): ${suggested.join(', ')}`);
162
+ } else {
163
+ console.log('Nenhum IDE foi detetado automaticamente.');
164
+ console.log('Pode ainda instalar adaptadores manualmente (útil para outro ambiente ou CI).');
165
+ }
166
+
167
+ console.log('');
168
+ console.log('Opções:');
169
+ console.log(' Enter → aceitar a sugestão');
170
+ console.log(' a → instalar todos (cursor, claude, copilot)');
171
+ console.log(' n → não instalar adaptadores');
172
+ console.log(' 1,3 números separados por vírgula ou espaço');
173
+ console.log(' cursor,copilot nomes separados por vírgula');
174
+ console.log('');
175
+
176
+ const rl = readline.createInterface({ input, output });
177
+ try {
178
+ let answer = (await rl.question('> ')).trim();
179
+ if (answer === '') {
180
+ return suggested;
181
+ }
182
+
183
+ let resolved = resolveIdeSelectionFromInput(answer, presence);
184
+ while (resolved === null) {
185
+ console.log('Não percebi. Use Enter, a, n, números (ex.: 1,3) ou nomes (ex.: cursor,claude).');
186
+ answer = (await rl.question('> ')).trim();
187
+ if (answer === '') {
188
+ return suggested;
189
+ }
190
+ resolved = resolveIdeSelectionFromInput(answer, presence);
191
+ }
192
+ return resolved;
193
+ } finally {
194
+ rl.close();
195
+ }
196
+ }
197
+
198
+ async function resolveIdeListInteractive(argv) {
199
+ if (argv.includes('--no-ide-prompt') || process.env.CI === 'true') {
200
+ return [];
201
+ }
202
+ if (argv.includes('-y') || argv.includes('--yes')) {
203
+ const presence = detectIdePresence();
204
+ const detected = keysWhereDetected(presence);
205
+ return detected.length > 0 ? detected : [...IDE_ORDER];
206
+ }
207
+ if (!process.stdin.isTTY) {
208
+ return [];
209
+ }
210
+ return promptIdeSelection();
211
+ }
212
+
213
+ export async function runInit(argv = process.argv.slice(2)) {
214
+ const cwd = process.cwd();
215
+ const here = argv.includes('--here');
216
+ const force = argv.includes('--force');
217
+ const upgrade = argv.includes('--upgrade');
218
+ const noProjectReview = argv.includes('--no-project-review');
219
+
220
+ const projectName = !here && argv[1] && !argv[1].startsWith('--') ? argv[1] : null;
221
+ const baseDir = here ? cwd : path.join(cwd, projectName || 'pdd-project');
222
+
223
+ if (!here && !fs.existsSync(baseDir)) {
224
+ fs.mkdirSync(baseDir, { recursive: true });
225
+ }
226
+
227
+ const installedVersion = readInstalledVersion(baseDir);
228
+
229
+ if (upgrade) {
230
+ const plan = buildTemplateUpgradePlan(baseDir, CORE_TEMPLATES);
231
+ const summary = applyTemplateUpgradePlan(baseDir, CORE_TEMPLATES, plan, force);
232
+
233
+ writeFile(baseDir, '.pdd/version.json', JSON.stringify({ templateVersion: PDD_TEMPLATE_VERSION }, null, 2));
234
+
235
+ printUpgradeSummary(summary);
236
+
237
+ if (installedVersion === PDD_TEMPLATE_VERSION) {
238
+ console.log('ℹ️ Templates were already up to date.');
239
+ }
240
+ } else {
241
+ for (const [file, content] of Object.entries(CORE_TEMPLATES)) {
242
+ writeFile(baseDir, file, content);
243
+ }
244
+
245
+ console.log('🚀 PDD initialized');
246
+ }
247
+
248
+ const constitutionCreated = ensureConstitution(baseDir, force);
249
+ if (constitutionCreated) {
250
+ console.log('📜 Constitution ensured: .pdd/constitution.md');
251
+ }
252
+
253
+ let ideList = normalizeIdeList(argv);
254
+ if (ideList.length > 0) {
255
+ const unknown = ideList.filter(id => !IDE_ADAPTERS[id]);
256
+ if (unknown.length) {
257
+ console.warn(`⚠️ Adaptador(es) desconhecido(s): ${unknown.join(', ')} — ignorado(s)`);
258
+ }
259
+ ideList = ideList.filter(id => IDE_ADAPTERS[id]);
260
+ } else {
261
+ ideList = await resolveIdeListInteractive(argv);
262
+ }
263
+
264
+ const ideResults = installIdeAdapters(baseDir, ideList, force);
265
+ ideResults.forEach(r => console.log(`- ${r.status}: ${r.path}`));
266
+
267
+ if (!noProjectReview) {
268
+ await runInitialProjectReviewAgent(baseDir, argv);
269
+ }
270
+ }
@@ -1,33 +1,33 @@
1
- import { readProjectState } from '../core/state-manager.js';
2
-
3
- export function runStatus(baseDir = process.cwd()) {
4
- const state = readProjectState(baseDir);
5
-
6
- console.log('📊 PDD Status\n');
7
-
8
- console.log(`Status: ${state.status}`);
9
-
10
- if (state.activeChange) {
11
- console.log(`Active change: ${state.activeChange}`);
12
- } else {
13
- console.log('Active change: none');
14
- }
15
-
16
- if (state.lastChange) {
17
- console.log(`Last change: ${state.lastChange}`);
18
- }
19
-
20
- if (state.updatedAt) {
21
- console.log(`Updated at: ${state.updatedAt}`);
22
- }
23
-
24
- console.log('');
25
-
26
- if (state.status === 'in-progress') {
27
- console.log('⚠️ A change is currently in progress');
28
- } else if (state.status === 'failed') {
29
- console.log('❌ Last operation failed');
30
- } else {
31
- console.log('✅ Project is healthy');
32
- }
33
- }
1
+ import { readProjectState } from '../core/state-manager.js';
2
+
3
+ export function runStatus(baseDir = process.cwd()) {
4
+ const state = readProjectState(baseDir);
5
+
6
+ console.log('📊 PDD Status\n');
7
+
8
+ console.log(`Status: ${state.status}`);
9
+
10
+ if (state.activeChange) {
11
+ console.log(`Active change: ${state.activeChange}`);
12
+ } else {
13
+ console.log('Active change: none');
14
+ }
15
+
16
+ if (state.lastChange) {
17
+ console.log(`Last change: ${state.lastChange}`);
18
+ }
19
+
20
+ if (state.updatedAt) {
21
+ console.log(`Updated at: ${state.updatedAt}`);
22
+ }
23
+
24
+ console.log('');
25
+
26
+ if (state.status === 'in-progress') {
27
+ console.log('⚠️ A change is currently in progress');
28
+ } else if (state.status === 'failed') {
29
+ console.log('❌ Last operation failed');
30
+ } else {
31
+ console.log('✅ Project is healthy');
32
+ }
33
+ }