@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,135 +1,135 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import {
4
- readProjectState,
5
- setActiveChange,
6
- clearActiveChange,
7
- writeProjectState
8
- } from './state-manager.js';
9
-
10
- function ensureDir(filePath) {
11
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
12
- }
13
-
14
- function appendText(filePath, content) {
15
- ensureDir(filePath);
16
- fs.appendFileSync(filePath, content, 'utf-8');
17
- }
18
-
19
- function writeJson(filePath, value) {
20
- ensureDir(filePath);
21
- fs.writeFileSync(filePath, JSON.stringify(value, null, 2) + '\n', 'utf-8');
22
- }
23
-
24
- function nowIso() {
25
- return new Date().toISOString();
26
- }
27
-
28
- function buildFailurePayload({ issue, changeId, phase, error }) {
29
- return {
30
- issue,
31
- changeId,
32
- phase,
33
- error: error?.message || String(error),
34
- failedAt: nowIso()
35
- };
36
- }
37
-
38
- function persistFailureArtifacts(baseDir, payload) {
39
- const changeDir = path.join(baseDir, 'changes', payload.changeId);
40
- const failureReportPath = path.join(changeDir, 'failure-report.json');
41
- const verificationReportPath = path.join(changeDir, 'verification-report.md');
42
-
43
- writeJson(failureReportPath, payload);
44
- appendText(
45
- verificationReportPath,
46
- `\n## Failure\n- phase: ${payload.phase}\n- error: ${payload.error}\n- failedAt: ${payload.failedAt}\n`
47
- );
48
-
49
- return {
50
- failureReportPath,
51
- verificationReportPath
52
- };
53
- }
54
-
55
- export async function runResilientFixWorkflow({
56
- baseDir = process.cwd(),
57
- issue,
58
- dryRun = false,
59
- noValidate = false,
60
- openPr = false,
61
- generatePatchArtifacts,
62
- runValidation,
63
- openPullRequest
64
- }) {
65
- const current = readProjectState(baseDir);
66
- if (current.status === 'in-progress' && current.activeChange) {
67
- throw new Error(`Another change is already in progress: ${current.activeChange}`);
68
- }
69
-
70
- if (dryRun) {
71
- return {
72
- status: 'dry-run',
73
- issue
74
- };
75
- }
76
-
77
- const changeId = `change-${Date.now()}`;
78
- setActiveChange(baseDir, changeId, 'in-progress');
79
-
80
- try {
81
- let phase = 'patch-generation';
82
- const patch = generatePatchArtifacts({ issue, baseDir });
83
-
84
- if (!noValidate) {
85
- phase = 'validation';
86
- runValidation(baseDir);
87
- }
88
-
89
- if (openPr) {
90
- phase = 'pr-preparation';
91
- await openPullRequest({
92
- issue,
93
- changeId: patch.changeId,
94
- changeDir: patch.changeDir,
95
- baseDir
96
- });
97
- }
98
-
99
- clearActiveChange(baseDir, 'completed');
100
- writeProjectState(baseDir, {
101
- activeChange: null,
102
- lastChange: patch.changeId,
103
- status: 'completed',
104
- lastResult: 'success',
105
- lastIssue: issue,
106
- lastPhase: 'completed',
107
- lastError: null
108
- });
109
-
110
- return {
111
- status: 'completed',
112
- changeId: patch.changeId,
113
- files: patch.files
114
- };
115
- } catch (error) {
116
- const phase = current.status === 'in-progress' ? current.lastPhase || 'unknown' : 'unknown';
117
- const payload = buildFailurePayload({ issue, changeId, phase, error });
118
- const artifacts = persistFailureArtifacts(baseDir, payload);
119
-
120
- writeProjectState(baseDir, {
121
- activeChange: changeId,
122
- lastChange: changeId,
123
- status: 'failed',
124
- lastResult: 'failed',
125
- lastIssue: issue,
126
- lastPhase: phase,
127
- lastError: payload.error,
128
- lastFailureReport: artifacts.failureReportPath
129
- });
130
-
131
- const wrapped = new Error(`Fix failed during ${phase}: ${payload.error}`);
132
- wrapped.cause = error;
133
- throw wrapped;
134
- }
135
- }
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import {
4
+ readProjectState,
5
+ setActiveChange,
6
+ clearActiveChange,
7
+ writeProjectState
8
+ } from './state-manager.js';
9
+
10
+ function ensureDir(filePath) {
11
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
12
+ }
13
+
14
+ function appendText(filePath, content) {
15
+ ensureDir(filePath);
16
+ fs.appendFileSync(filePath, content, 'utf-8');
17
+ }
18
+
19
+ function writeJson(filePath, value) {
20
+ ensureDir(filePath);
21
+ fs.writeFileSync(filePath, JSON.stringify(value, null, 2) + '\n', 'utf-8');
22
+ }
23
+
24
+ function nowIso() {
25
+ return new Date().toISOString();
26
+ }
27
+
28
+ function buildFailurePayload({ issue, changeId, phase, error }) {
29
+ return {
30
+ issue,
31
+ changeId,
32
+ phase,
33
+ error: error?.message || String(error),
34
+ failedAt: nowIso()
35
+ };
36
+ }
37
+
38
+ function persistFailureArtifacts(baseDir, payload) {
39
+ const changeDir = path.join(baseDir, 'changes', payload.changeId);
40
+ const failureReportPath = path.join(changeDir, 'failure-report.json');
41
+ const verificationReportPath = path.join(changeDir, 'verification-report.md');
42
+
43
+ writeJson(failureReportPath, payload);
44
+ appendText(
45
+ verificationReportPath,
46
+ `\n## Failure\n- phase: ${payload.phase}\n- error: ${payload.error}\n- failedAt: ${payload.failedAt}\n`
47
+ );
48
+
49
+ return {
50
+ failureReportPath,
51
+ verificationReportPath
52
+ };
53
+ }
54
+
55
+ export async function runResilientFixWorkflow({
56
+ baseDir = process.cwd(),
57
+ issue,
58
+ dryRun = false,
59
+ noValidate = false,
60
+ openPr = false,
61
+ generatePatchArtifacts,
62
+ runValidation,
63
+ openPullRequest
64
+ }) {
65
+ const current = readProjectState(baseDir);
66
+ if (current.status === 'in-progress' && current.activeChange) {
67
+ throw new Error(`Another change is already in progress: ${current.activeChange}`);
68
+ }
69
+
70
+ if (dryRun) {
71
+ return {
72
+ status: 'dry-run',
73
+ issue
74
+ };
75
+ }
76
+
77
+ const changeId = `change-${Date.now()}`;
78
+ setActiveChange(baseDir, changeId, 'in-progress');
79
+
80
+ try {
81
+ let phase = 'patch-generation';
82
+ const patch = generatePatchArtifacts({ issue, baseDir });
83
+
84
+ if (!noValidate) {
85
+ phase = 'validation';
86
+ runValidation(baseDir);
87
+ }
88
+
89
+ if (openPr) {
90
+ phase = 'pr-preparation';
91
+ await openPullRequest({
92
+ issue,
93
+ changeId: patch.changeId,
94
+ changeDir: patch.changeDir,
95
+ baseDir
96
+ });
97
+ }
98
+
99
+ clearActiveChange(baseDir, 'completed');
100
+ writeProjectState(baseDir, {
101
+ activeChange: null,
102
+ lastChange: patch.changeId,
103
+ status: 'completed',
104
+ lastResult: 'success',
105
+ lastIssue: issue,
106
+ lastPhase: 'completed',
107
+ lastError: null
108
+ });
109
+
110
+ return {
111
+ status: 'completed',
112
+ changeId: patch.changeId,
113
+ files: patch.files
114
+ };
115
+ } catch (error) {
116
+ const phase = current.status === 'in-progress' ? current.lastPhase || 'unknown' : 'unknown';
117
+ const payload = buildFailurePayload({ issue, changeId, phase, error });
118
+ const artifacts = persistFailureArtifacts(baseDir, payload);
119
+
120
+ writeProjectState(baseDir, {
121
+ activeChange: changeId,
122
+ lastChange: changeId,
123
+ status: 'failed',
124
+ lastResult: 'failed',
125
+ lastIssue: issue,
126
+ lastPhase: phase,
127
+ lastError: payload.error,
128
+ lastFailureReport: artifacts.failureReportPath
129
+ });
130
+
131
+ const wrapped = new Error(`Fix failed during ${phase}: ${payload.error}`);
132
+ wrapped.cause = error;
133
+ throw wrapped;
134
+ }
135
+ }
@@ -1,94 +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
- }
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
+ }