@pcoliveira90/pdd 0.2.1-beta.0 → 0.2.2
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/bin/pdd-pro.js +5 -2
- package/package.json +1 -1
- package/src/cli/index.js +2 -2
- package/src/cli/init-command.js +136 -10
- package/src/core/ide-detector.js +94 -0
- package/src/core/template-registry.js +29 -2
package/bin/pdd-pro.js
CHANGED
package/package.json
CHANGED
package/src/cli/index.js
CHANGED
|
@@ -25,7 +25,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
25
25
|
const cwd = process.cwd();
|
|
26
26
|
|
|
27
27
|
if (command === 'init') {
|
|
28
|
-
runInit(argv);
|
|
28
|
+
await runInit(argv);
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -94,7 +94,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
94
94
|
console.log('');
|
|
95
95
|
console.log('Commands:');
|
|
96
96
|
console.log(' pdd init <project-name>');
|
|
97
|
-
console.log(' pdd init --here [--force] [--upgrade] [--ide=claude|cursor|copilot
|
|
97
|
+
console.log(' pdd init --here [--force] [--upgrade] [-y] [--no-ide-prompt] [--ide=claude|cursor|copilot|...]');
|
|
98
98
|
console.log(' pdd doctor [--fix]');
|
|
99
99
|
console.log(' pdd status');
|
|
100
100
|
console.log(' pdd fix "description" [--open-pr] [--dry-run] [--no-validate]');
|
package/src/cli/init-command.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import readline from 'node:readline/promises';
|
|
4
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
3
5
|
import { CORE_TEMPLATES, IDE_ADAPTERS, PDD_TEMPLATE_VERSION } from '../core/template-registry.js';
|
|
4
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';
|
|
5
13
|
|
|
6
14
|
function ensureDir(filePath) {
|
|
7
15
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
@@ -17,11 +25,13 @@ function normalizeIdeList(argv) {
|
|
|
17
25
|
const ideArg = argv.find(arg => arg.startsWith('--ide='));
|
|
18
26
|
if (!ideArg) return [];
|
|
19
27
|
|
|
20
|
-
return
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
return (
|
|
29
|
+
ideArg
|
|
30
|
+
.split('=')[1]
|
|
31
|
+
?.split(',')
|
|
32
|
+
.map(v => v.trim())
|
|
33
|
+
.filter(Boolean) || []
|
|
34
|
+
);
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
function readInstalledVersion(baseDir) {
|
|
@@ -80,12 +90,120 @@ function printUpgradeSummary(summary) {
|
|
|
80
90
|
}
|
|
81
91
|
}
|
|
82
92
|
|
|
83
|
-
|
|
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)) {
|
|
84
203
|
const cwd = process.cwd();
|
|
85
204
|
const here = argv.includes('--here');
|
|
86
205
|
const force = argv.includes('--force');
|
|
87
206
|
const upgrade = argv.includes('--upgrade');
|
|
88
|
-
const ideList = normalizeIdeList(argv);
|
|
89
207
|
|
|
90
208
|
const projectName = !here && argv[1] && !argv[1].startsWith('--') ? argv[1] : null;
|
|
91
209
|
const baseDir = here ? cwd : path.join(cwd, projectName || 'pdd-project');
|
|
@@ -100,7 +218,6 @@ export function runInit(argv = process.argv.slice(2)) {
|
|
|
100
218
|
const plan = buildTemplateUpgradePlan(baseDir, CORE_TEMPLATES);
|
|
101
219
|
const summary = applyTemplateUpgradePlan(baseDir, CORE_TEMPLATES, plan, force);
|
|
102
220
|
|
|
103
|
-
// always update version.json
|
|
104
221
|
writeFile(baseDir, '.pdd/version.json', JSON.stringify({ templateVersion: PDD_TEMPLATE_VERSION }, null, 2));
|
|
105
222
|
|
|
106
223
|
printUpgradeSummary(summary);
|
|
@@ -108,9 +225,7 @@ export function runInit(argv = process.argv.slice(2)) {
|
|
|
108
225
|
if (installedVersion === PDD_TEMPLATE_VERSION) {
|
|
109
226
|
console.log('ℹ️ Templates were already up to date.');
|
|
110
227
|
}
|
|
111
|
-
|
|
112
228
|
} else {
|
|
113
|
-
// basic init (no intelligence needed yet)
|
|
114
229
|
for (const [file, content] of Object.entries(CORE_TEMPLATES)) {
|
|
115
230
|
writeFile(baseDir, file, content);
|
|
116
231
|
}
|
|
@@ -118,6 +233,17 @@ export function runInit(argv = process.argv.slice(2)) {
|
|
|
118
233
|
console.log('🚀 PDD initialized');
|
|
119
234
|
}
|
|
120
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
|
+
|
|
121
247
|
const ideResults = installIdeAdapters(baseDir, ideList, force);
|
|
122
248
|
ideResults.forEach(r => console.log(`- ${r.status}: ${r.path}`));
|
|
123
249
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
/** Stable order for prompts and numbered selection */
|
|
6
|
+
export const IDE_ORDER = ['cursor', 'claude', 'copilot'];
|
|
7
|
+
|
|
8
|
+
export const IDE_LABELS = {
|
|
9
|
+
cursor: 'Cursor',
|
|
10
|
+
claude: 'Claude (desktop / Claude Code)',
|
|
11
|
+
copilot: 'GitHub Copilot (VS Code)'
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function exists(p) {
|
|
15
|
+
return Boolean(p && fs.existsSync(p));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function vscodeDetected() {
|
|
19
|
+
const platform = process.platform;
|
|
20
|
+
const home = os.homedir();
|
|
21
|
+
|
|
22
|
+
if (platform === 'win32') {
|
|
23
|
+
const local = process.env.LOCALAPPDATA;
|
|
24
|
+
const pf = process.env.PROGRAMFILES;
|
|
25
|
+
const pf86 = process.env['PROGRAMFILES(X86)'];
|
|
26
|
+
return (
|
|
27
|
+
exists(local && path.join(local, 'Programs', 'Microsoft VS Code', 'Code.exe')) ||
|
|
28
|
+
exists(pf && path.join(pf, 'Microsoft VS Code', 'Code.exe')) ||
|
|
29
|
+
exists(pf86 && path.join(pf86, 'Microsoft VS Code', 'Code.exe')) ||
|
|
30
|
+
exists(path.join(home, '.vscode'))
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (platform === 'darwin') {
|
|
35
|
+
return (
|
|
36
|
+
exists('/Applications/Visual Studio Code.app') ||
|
|
37
|
+
exists(path.join(home, '.vscode'))
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
exists('/usr/share/code/code') ||
|
|
43
|
+
exists('/usr/bin/code') ||
|
|
44
|
+
exists('/snap/bin/code') ||
|
|
45
|
+
exists(path.join(home, '.vscode'))
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Heuristic detection of IDE-related tooling (best-effort, no guarantees).
|
|
51
|
+
* @returns {{ cursor: boolean, claude: boolean, copilot: boolean }}
|
|
52
|
+
*/
|
|
53
|
+
export function detectIdePresence() {
|
|
54
|
+
const platform = process.platform;
|
|
55
|
+
const home = os.homedir();
|
|
56
|
+
const local = process.env.LOCALAPPDATA;
|
|
57
|
+
|
|
58
|
+
const presence = {
|
|
59
|
+
cursor: false,
|
|
60
|
+
claude: false,
|
|
61
|
+
copilot: false
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (platform === 'win32') {
|
|
65
|
+
presence.cursor =
|
|
66
|
+
exists(local && path.join(local, 'Programs', 'cursor', 'Cursor.exe')) ||
|
|
67
|
+
exists(path.join(home, '.cursor'));
|
|
68
|
+
|
|
69
|
+
presence.claude =
|
|
70
|
+
exists(local && path.join(local, 'Programs', 'Claude', 'Claude.exe')) ||
|
|
71
|
+
exists(path.join(home, '.claude'));
|
|
72
|
+
} else if (platform === 'darwin') {
|
|
73
|
+
presence.cursor =
|
|
74
|
+
exists('/Applications/Cursor.app') || exists(path.join(home, '.cursor'));
|
|
75
|
+
|
|
76
|
+
presence.claude =
|
|
77
|
+
exists('/Applications/Claude.app') || exists(path.join(home, '.claude'));
|
|
78
|
+
} else {
|
|
79
|
+
presence.cursor =
|
|
80
|
+
exists(path.join(home, '.cursor')) ||
|
|
81
|
+
exists('/opt/Cursor') ||
|
|
82
|
+
exists('/usr/share/cursor');
|
|
83
|
+
|
|
84
|
+
presence.claude = exists(path.join(home, '.claude'));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
presence.copilot = vscodeDetected();
|
|
88
|
+
|
|
89
|
+
return presence;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function keysWhereDetected(presence) {
|
|
93
|
+
return IDE_ORDER.filter(k => presence[k]);
|
|
94
|
+
}
|
|
@@ -160,9 +160,36 @@ Execute Patch-Driven Development workflow.
|
|
|
160
160
|
`
|
|
161
161
|
},
|
|
162
162
|
cursor: {
|
|
163
|
-
'.cursor/pdd.prompt.md': `# PDD Cursor
|
|
163
|
+
'.cursor/pdd.prompt.md': `# PDD (Cursor)
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
You are helping run **Patch-Driven Development** in this repo. Prefer small, safe changes and evidence before edits.
|
|
166
|
+
|
|
167
|
+
## Context to use
|
|
168
|
+
|
|
169
|
+
- Project rules: \`.pdd/constitution.md\`
|
|
170
|
+
- Command playbooks: \`.pdd/commands/\` (e.g. \`pdd-fix.md\`, \`pdd-verify.md\`)
|
|
171
|
+
- Templates: \`.pdd/templates/\` (delta-spec, patch-plan, verification-report)
|
|
172
|
+
|
|
173
|
+
## Workflow (high level)
|
|
174
|
+
|
|
175
|
+
1. **Recon** — map relevant files and risks; do not edit yet.
|
|
176
|
+
2. **Delta** — describe the minimal change (align with \`delta-spec\` / \`patch-plan\` ideas).
|
|
177
|
+
3. **Implement** — smallest diff that fixes the issue; match existing patterns.
|
|
178
|
+
4. **Verify** — how to confirm behavior; note regressions avoided or checked.
|
|
179
|
+
5. **Artifacts** — if the project uses \`changes/\` or PR notes, keep them consistent.
|
|
180
|
+
|
|
181
|
+
## Issue to address
|
|
182
|
+
|
|
183
|
+
Describe the issue or paste it here:
|
|
184
|
+
|
|
185
|
+
\`\`\`
|
|
186
|
+
{{issue}}
|
|
187
|
+
\`\`\`
|
|
188
|
+
|
|
189
|
+
## Output
|
|
190
|
+
|
|
191
|
+
- Clear list of files touched and why
|
|
192
|
+
- If something is unknown, say what you would verify next (command, test, or manual step)
|
|
166
193
|
`
|
|
167
194
|
},
|
|
168
195
|
copilot: {
|