@dezkareid/osddt 1.11.13 → 1.12.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.
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function copyEnvCommand(): Command;
package/dist/index.js CHANGED
@@ -254,13 +254,24 @@ ${npxCommand} start-worktree <feature-name> --dir <package-path>
254
254
 
255
255
  4. Parse the command output to extract \`worktreePath\` and \`workingDir\`.
256
256
 
257
- 5. Navigate into the worktree directory to locate the project:
257
+ 5. Copy environment files into the new worktree. Read \`bare-path\` and \`mainBranch\` from \`.osddtrc\` to construct the source path:
258
+ - Single repo (\`repoType: "single"\`):
259
+ \`\`\`
260
+ ${npxCommand} copy-env --source <bare-path>/<mainBranch> --target <worktreePath>
261
+ \`\`\`
262
+ - Monorepo (\`repoType: "monorepo"\`):
263
+ \`\`\`
264
+ ${npxCommand} copy-env --source <bare-path>/<mainBranch>/<package-path> --target <worktreePath>/<package-path>
265
+ \`\`\`
266
+ If the command finds no files, it exits silently — this is not an error.
267
+
268
+ 6. Navigate into the worktree directory to locate the project:
258
269
  - Enter \`<worktreePath>\` — this is the isolated git worktree for this feature.
259
270
  - If \`repoType\` is \`"single"\`: the project root is \`<worktreePath>\`.
260
271
  - If \`repoType\` is \`"monorepo"\`: the project root is \`<worktreePath>/<package-path>\`.
261
272
  - The planning files will live under \`<workingDir>\` (i.e. \`<project-root>/working-on/<feature-name>/\`).
262
273
 
263
- 6. Report the branch name, worktree path, project root, and working directory.
274
+ 7. Report the branch name, worktree path, project root, and working directory.
264
275
 
265
276
  ---
266
277
 
@@ -519,6 +530,17 @@ ${npxCommand} start-worktree <feature-name> --dir <package-path>
519
530
 
520
531
  4. Parse the command output to extract \`worktreePath\` and \`workingDir\`. Navigate into \`<worktreePath>\` to locate the project root.
521
532
 
533
+ 5. Copy environment files into the new worktree. Read \`bare-path\` and \`mainBranch\` from \`.osddtrc\` to construct the source path:
534
+ - Single repo (\`repoType: "single"\`):
535
+ \`\`\`
536
+ ${npxCommand} copy-env --source <bare-path>/<mainBranch> --target <worktreePath>
537
+ \`\`\`
538
+ - Monorepo (\`repoType: "monorepo"\`):
539
+ \`\`\`
540
+ ${npxCommand} copy-env --source <bare-path>/<mainBranch>/<package-path> --target <worktreePath>/<package-path>
541
+ \`\`\`
542
+ If the command finds no files, it exits silently — this is not an error.
543
+
522
544
  #### If \`worktree-repository\` is **absent** — Standard workflow
523
545
 
524
546
  3. Check whether the branch already exists locally or remotely:
@@ -603,21 +625,13 @@ ${npxCommand} worktree-info <feature-name>
603
625
 
604
626
  **If it exits with code 0 (worktree feature):** parse the JSON to get \`worktreePath\` and \`branch\`, derive \`<project-path>\` from \`workingDir\`, then continue below.
605
627
 
606
- 4. Run the done command with the \`--worktree\` flag:
607
- \`\`\`
608
- ${npxCommand} done <feature-name> --dir <project-path> --worktree
609
- \`\`\`
610
-
611
- 5. The command will automatically prefix the destination folder name with today's date in \`YYYY-MM-DD\` format.
612
- For example, \`working-on/feature-a\` will be moved to \`done/YYYY-MM-DD-feature-a\`.
613
-
614
- 6. Check for uncommitted changes inside the worktree:
628
+ 4. Check for uncommitted changes inside the worktree:
615
629
 
616
630
  \`\`\`
617
631
  git -C <worktreePath> status --porcelain
618
632
  \`\`\`
619
633
 
620
- 7. If there are **uncommitted changes**:
634
+ 5. If there are **uncommitted changes**:
621
635
  1. Run \`git -C <worktreePath> diff\` to inspect them.
622
636
  2. Derive a concise commit message in **conventional commit** format (e.g. \`feat: add payment gateway integration\`) based on the diff.
623
637
  3. Present the proposed message to the user: _"Use this commit message, or provide your own?"_
@@ -627,12 +641,20 @@ ${npxCommand} worktree-info <feature-name>
627
641
  git -C <worktreePath> commit -m "<confirmed-message>"
628
642
  \`\`\`
629
643
 
630
- 8. Push the branch to remote (covers both first push and subsequent pushes):
644
+ 6. Push the branch to remote (covers both first push and subsequent pushes):
631
645
 
632
646
  \`\`\`
633
647
  git -C <worktreePath> push --set-upstream origin <branch>
634
648
  \`\`\`
635
649
 
650
+ 7. Run the done command with the \`--worktree\` flag:
651
+ \`\`\`
652
+ ${npxCommand} done <feature-name> --dir <project-path> --worktree
653
+ \`\`\`
654
+
655
+ 8. The command will automatically prefix the destination folder name with today's date in \`YYYY-MM-DD\` format.
656
+ For example, \`working-on/feature-a\` will be moved to \`done/YYYY-MM-DD-feature-a\`.
657
+
636
658
  9. Report the result of the command, including the full destination path
637
659
 
638
660
  ${getCustomContextStep(npxCommand, 'done')}`,
@@ -1019,6 +1041,19 @@ async function runDone(featureName, cwd, worktree) {
1019
1041
  console.error(`Error: working-on/${featureName} does not exist.`);
1020
1042
  process.exit(1);
1021
1043
  }
1044
+ if (worktree && worktreePath && (await fs.pathExists(worktreePath))) {
1045
+ try {
1046
+ const status = execSync('git status --porcelain', { cwd: worktreePath, encoding: 'utf8', stdio: 'pipe' });
1047
+ if (status.trim().length > 0) {
1048
+ console.error('Error: Worktree has uncommitted changes. Commit or stash them before running osddt done.');
1049
+ process.exit(1);
1050
+ }
1051
+ }
1052
+ catch {
1053
+ console.error('Error: Could not verify worktree status. Ensure the worktree is a valid git repository and has no uncommitted changes before proceeding.');
1054
+ process.exit(1);
1055
+ }
1056
+ }
1022
1057
  await fs.ensureDir(path.dirname(dest));
1023
1058
  await fs.move(src, dest);
1024
1059
  console.log(`Moved: working-on/${featureName} → done/${destName}`);
@@ -1379,6 +1414,76 @@ function worktreeInfoCommand() {
1379
1414
  return cmd;
1380
1415
  }
1381
1416
 
1417
+ function matchesPatterns(filename, patterns) {
1418
+ return patterns.some((pattern) => {
1419
+ // Convert glob pattern to regex: only support leading/trailing * and literal chars
1420
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
1421
+ return new RegExp(`^${escaped}$`).test(filename);
1422
+ });
1423
+ }
1424
+ async function readRc(cwd) {
1425
+ const rcPath = path.join(cwd, '.osddtrc');
1426
+ if (await fs.pathExists(rcPath)) {
1427
+ return await fs.readJson(rcPath);
1428
+ }
1429
+ return {};
1430
+ }
1431
+ async function copyFile(src, dest, force, dryRun) {
1432
+ if (dryRun) {
1433
+ console.log(`[dry-run] Would copy: ${src} → ${dest}`);
1434
+ return;
1435
+ }
1436
+ if ((await fs.pathExists(dest)) && !force) {
1437
+ console.log(`Skipped: ${dest} (already exists)`);
1438
+ return;
1439
+ }
1440
+ await fs.copy(src, dest, { overwrite: true });
1441
+ console.log(`Copied: ${src} → ${dest}`);
1442
+ }
1443
+ async function findMatchedFiles(source, patterns) {
1444
+ if (!(await fs.pathExists(source)))
1445
+ return [];
1446
+ const entries = await fs.readdir(source);
1447
+ return entries.filter(entry => matchesPatterns(entry, patterns));
1448
+ }
1449
+ async function runCopyEnv(options) {
1450
+ const cwd = process.cwd();
1451
+ if (!options.target)
1452
+ return;
1453
+ const rc = await readRc(cwd);
1454
+ const source = options.source ?? rc['copy-env']?.source;
1455
+ if (!source)
1456
+ return;
1457
+ const rawPattern = options.pattern ?? rc['copy-env']?.pattern ?? '.env*';
1458
+ const patterns = rawPattern.split(',').map(p => p.trim()).filter(Boolean);
1459
+ const matched = await findMatchedFiles(source, patterns);
1460
+ if (matched.length === 0)
1461
+ return;
1462
+ await fs.ensureDir(options.target);
1463
+ for (const filename of matched) {
1464
+ await copyFile(path.join(source, filename), path.join(options.target, filename), options.force ?? false, options.dryRun ?? false);
1465
+ }
1466
+ }
1467
+ function copyEnvCommand() {
1468
+ const cmd = new Command('copy-env');
1469
+ cmd
1470
+ .description('Copy environment files from a source directory to a target directory')
1471
+ .option('--source <path>', 'source directory to copy env files from')
1472
+ .option('--target <path>', 'target directory to copy env files into')
1473
+ .option('--pattern <globs>', 'comma-separated glob patterns (default: .env*)')
1474
+ .option('--force', 'overwrite existing files in target')
1475
+ .option('--dry-run', 'print what would be copied without writing')
1476
+ .action(async (options) => {
1477
+ try {
1478
+ await runCopyEnv(options);
1479
+ }
1480
+ catch {
1481
+ // fail silently
1482
+ }
1483
+ });
1484
+ return cmd;
1485
+ }
1486
+
1382
1487
  const __filename$1 = fileURLToPath(import.meta.url);
1383
1488
  const __dirname$1 = path.dirname(__filename$1);
1384
1489
  const pkgPath = path.join(__dirname$1, '..', 'package.json');
@@ -1395,4 +1500,5 @@ program.addCommand(updateCommand());
1395
1500
  program.addCommand(contextCommand());
1396
1501
  program.addCommand(startWorktreeCommand());
1397
1502
  program.addCommand(worktreeInfoCommand());
1503
+ program.addCommand(copyEnvCommand());
1398
1504
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dezkareid/osddt",
3
- "version": "1.11.13",
3
+ "version": "1.12.0",
4
4
  "description": "Package for Spec-Driven Development workflow",
5
5
  "keywords": [
6
6
  "spec-driven",