@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 CHANGED
@@ -1,5 +1,8 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import { runCli } from '../src/cli/index.js';
4
4
 
5
- runCli();
5
+ runCli().catch(err => {
6
+ console.error(err);
7
+ process.exitCode = 1;
8
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pcoliveira90/pdd",
3
- "version": "0.2.1-beta.0",
3
+ "version": "0.2.2",
4
4
  "description": "Patch-Driven Development CLI — safe, resilient and guided code changes",
5
5
  "type": "module",
6
6
  "bin": {
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|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]');
@@ -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 ideArg
21
- .split('=')[1]
22
- ?.split(',')
23
- .map(v => v.trim())
24
- .filter(Boolean) || [];
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
- export function runInit(argv = process.argv.slice(2)) {
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 Prompt
163
+ '.cursor/pdd.prompt.md': `# PDD (Cursor)
164
164
 
165
- Goal: execute PDD workflow for a given issue.
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: {