@pcoliveira90/pdd 0.2.6 → 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,68 +1,68 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
-
4
- function ensureDir(filePath) {
5
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
6
- }
7
-
8
- function readFileOrNull(filePath) {
9
- if (!fs.existsSync(filePath)) return null;
10
- return fs.readFileSync(filePath, 'utf-8');
11
- }
12
-
13
- function writeFile(baseDir, relativePath, content) {
14
- const fullPath = path.join(baseDir, relativePath);
15
- ensureDir(fullPath);
16
- fs.writeFileSync(fullPath, content, 'utf-8');
17
- }
18
-
19
- export function buildTemplateUpgradePlan(baseDir, templates) {
20
- const plan = {
21
- created: [],
22
- conflicts: [],
23
- skipped: []
24
- };
25
-
26
- for (const [relativePath, templateContent] of Object.entries(templates)) {
27
- const fullPath = path.join(baseDir, relativePath);
28
- const currentContent = readFileOrNull(fullPath);
29
-
30
- if (currentContent === null) {
31
- plan.created.push(relativePath);
32
- continue;
33
- }
34
-
35
- if (currentContent === templateContent) {
36
- plan.skipped.push(relativePath);
37
- continue;
38
- }
39
-
40
- plan.conflicts.push(relativePath);
41
- }
42
-
43
- return plan;
44
- }
45
-
46
- export function applyTemplateUpgradePlan(baseDir, templates, plan, force = false) {
47
- const summary = {
48
- created: [],
49
- updated: [],
50
- conflicts: [...plan.conflicts],
51
- skipped: [...plan.skipped]
52
- };
53
-
54
- for (const relativePath of plan.created) {
55
- writeFile(baseDir, relativePath, templates[relativePath]);
56
- summary.created.push(relativePath);
57
- }
58
-
59
- if (force) {
60
- for (const relativePath of plan.conflicts) {
61
- writeFile(baseDir, relativePath, templates[relativePath]);
62
- summary.updated.push(relativePath);
63
- }
64
- summary.conflicts = [];
65
- }
66
-
67
- return summary;
68
- }
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ function ensureDir(filePath) {
5
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
6
+ }
7
+
8
+ function readFileOrNull(filePath) {
9
+ if (!fs.existsSync(filePath)) return null;
10
+ return fs.readFileSync(filePath, 'utf-8');
11
+ }
12
+
13
+ function writeFile(baseDir, relativePath, content) {
14
+ const fullPath = path.join(baseDir, relativePath);
15
+ ensureDir(fullPath);
16
+ fs.writeFileSync(fullPath, content, 'utf-8');
17
+ }
18
+
19
+ export function buildTemplateUpgradePlan(baseDir, templates) {
20
+ const plan = {
21
+ created: [],
22
+ conflicts: [],
23
+ skipped: []
24
+ };
25
+
26
+ for (const [relativePath, templateContent] of Object.entries(templates)) {
27
+ const fullPath = path.join(baseDir, relativePath);
28
+ const currentContent = readFileOrNull(fullPath);
29
+
30
+ if (currentContent === null) {
31
+ plan.created.push(relativePath);
32
+ continue;
33
+ }
34
+
35
+ if (currentContent === templateContent) {
36
+ plan.skipped.push(relativePath);
37
+ continue;
38
+ }
39
+
40
+ plan.conflicts.push(relativePath);
41
+ }
42
+
43
+ return plan;
44
+ }
45
+
46
+ export function applyTemplateUpgradePlan(baseDir, templates, plan, force = false) {
47
+ const summary = {
48
+ created: [],
49
+ updated: [],
50
+ conflicts: [...plan.conflicts],
51
+ skipped: [...plan.skipped]
52
+ };
53
+
54
+ for (const relativePath of plan.created) {
55
+ writeFile(baseDir, relativePath, templates[relativePath]);
56
+ summary.created.push(relativePath);
57
+ }
58
+
59
+ if (force) {
60
+ for (const relativePath of plan.conflicts) {
61
+ writeFile(baseDir, relativePath, templates[relativePath]);
62
+ summary.updated.push(relativePath);
63
+ }
64
+ summary.conflicts = [];
65
+ }
66
+
67
+ return summary;
68
+ }
@@ -1,38 +1,38 @@
1
- import fs from 'fs';
2
- import { execSync } from 'child_process';
3
-
4
- function runCommand(command) {
5
- console.log(`→ ${command}`);
6
- execSync(command, { stdio: 'inherit' });
7
- }
8
-
9
- export function runValidation(baseDir = process.cwd()) {
10
- console.log('Running validation...');
11
-
12
- const packageJsonPath = `${baseDir}/package.json`;
13
- if (!fs.existsSync(packageJsonPath)) {
14
- console.log('No package.json found. Skipping validation.');
15
- return;
16
- }
17
-
18
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
19
- const scripts = pkg.scripts || {};
20
- const commands = [];
21
-
22
- if (scripts.test) commands.push('npm test');
23
- if (scripts.lint) commands.push('npm run lint');
24
- if (scripts.build) commands.push('npm run build');
25
-
26
- if (commands.length === 0) {
27
- console.log('No validation scripts found. Skipping validation.');
28
- return;
29
- }
30
-
31
- try {
32
- commands.forEach(runCommand);
33
- } catch {
34
- throw new Error('Validation failed');
35
- }
36
-
37
- console.log('Validation passed');
38
- }
1
+ import fs from 'fs';
2
+ import { execSync } from 'child_process';
3
+
4
+ function runCommand(command) {
5
+ console.log(`→ ${command}`);
6
+ execSync(command, { stdio: 'inherit' });
7
+ }
8
+
9
+ export function runValidation(baseDir = process.cwd()) {
10
+ console.log('Running validation...');
11
+
12
+ const packageJsonPath = `${baseDir}/package.json`;
13
+ if (!fs.existsSync(packageJsonPath)) {
14
+ console.log('No package.json found. Skipping validation.');
15
+ return;
16
+ }
17
+
18
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
19
+ const scripts = pkg.scripts || {};
20
+ const commands = [];
21
+
22
+ if (scripts.test) commands.push('npm test');
23
+ if (scripts.lint) commands.push('npm run lint');
24
+ if (scripts.build) commands.push('npm run build');
25
+
26
+ if (commands.length === 0) {
27
+ console.log('No validation scripts found. Skipping validation.');
28
+ return;
29
+ }
30
+
31
+ try {
32
+ commands.forEach(runCommand);
33
+ } catch {
34
+ throw new Error('Validation failed');
35
+ }
36
+
37
+ console.log('Validation passed');
38
+ }
@@ -0,0 +1,54 @@
1
+ import { execSync } from 'child_process';
2
+ import path from 'path';
3
+
4
+ function runGit(command, baseDir) {
5
+ return execSync(command, { cwd: baseDir, stdio: 'pipe', encoding: 'utf-8' }).trim();
6
+ }
7
+
8
+ function normalize(p) {
9
+ return path.resolve(String(p || '')).toLowerCase();
10
+ }
11
+
12
+ function detectWorktreeContext(baseDir = process.cwd()) {
13
+ try {
14
+ const topLevel = runGit('git rev-parse --show-toplevel', baseDir);
15
+ const gitDir = runGit('git rev-parse --git-dir', baseDir);
16
+ const commonDir = runGit('git rev-parse --git-common-dir', baseDir);
17
+
18
+ const isPrimaryWorktree = normalize(gitDir) === normalize(commonDir);
19
+
20
+ return {
21
+ isGitRepo: true,
22
+ topLevel,
23
+ isPrimaryWorktree
24
+ };
25
+ } catch {
26
+ return {
27
+ isGitRepo: false,
28
+ topLevel: null,
29
+ isPrimaryWorktree: false
30
+ };
31
+ }
32
+ }
33
+
34
+ export function enforceLinkedWorktree({
35
+ baseDir = process.cwd(),
36
+ commandName = 'command',
37
+ allowMainWorktree = false
38
+ }) {
39
+ const context = detectWorktreeContext(baseDir);
40
+ if (!context.isGitRepo) {
41
+ return context;
42
+ }
43
+
44
+ if (context.isPrimaryWorktree && !allowMainWorktree) {
45
+ throw new Error(
46
+ `PDD requires a linked worktree for "${commandName}". Current directory is the primary worktree.\n` +
47
+ 'Create one and run there:\n' +
48
+ ' git worktree add ../pdd-worktrees/<name> -b feature/<name>\n' +
49
+ 'Or override intentionally with --allow-main-worktree.'
50
+ );
51
+ }
52
+
53
+ return context;
54
+ }