@dezkareid/osddt 1.11.5 → 1.11.6

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,9 +1,4 @@
1
1
  import { Command } from 'commander';
2
- export interface WorktreeEntry {
3
- featureName: string;
4
- branch: string;
5
- worktreePath: string;
6
- workingDir: string;
7
- repoRoot: string;
8
- }
2
+ import { type WorktreeEntry } from '../utils/worktree.js';
3
+ export type { WorktreeEntry };
9
4
  export declare function startWorktreeCommand(): Command;
package/dist/index.js CHANGED
@@ -767,6 +767,25 @@ async function runWorktreeChecks(barePath) {
767
767
  }
768
768
  return results.every(r => r.passed);
769
769
  }
770
+ function listFeatureWorktrees(barePath, mainBranch) {
771
+ const output = execSync('git worktree list --porcelain', { cwd: barePath, encoding: 'utf-8' });
772
+ const blocks = output.trim().split(/\n\n+/);
773
+ const entries = [];
774
+ for (const block of blocks) {
775
+ const pathMatch = block.match(/^worktree (.+)$/m);
776
+ const branchMatch = block.match(/^branch (.+)$/m);
777
+ if (!pathMatch)
778
+ continue;
779
+ const worktreePath = pathMatch[1].trim();
780
+ const featureName = path.basename(worktreePath);
781
+ if (featureName === mainBranch)
782
+ continue;
783
+ const branch = branchMatch ? branchMatch[1].trim().replace(/^refs\/heads\//, '') : featureName;
784
+ const workingDir = path.join(worktreePath, 'working-on', featureName);
785
+ entries.push({ featureName, branch, worktreePath, workingDir });
786
+ }
787
+ return entries;
788
+ }
770
789
 
771
790
  const CANONICAL_PACKAGE_NAME = '@dezkareid/osddt';
772
791
  const NPX_COMMAND = 'npx osddt';
@@ -894,7 +913,7 @@ async function setupWorktreeEnvironment(cwd, worktreeRepository, agents) {
894
913
  }
895
914
  await writeAgentPointerFiles(cwd, agents, branch);
896
915
  console.log('');
897
- return { barePath, packageManager };
916
+ return { barePath, packageManager, mainBranch: branch };
898
917
  }
899
918
  async function runSetup(cwd, rawAgents, rawRepoType, rawWorktreeRepository) {
900
919
  const agents = rawAgents !== undefined ? parseAgents(rawAgents) : await askAgents();
@@ -906,21 +925,15 @@ async function runSetup(cwd, rawAgents, rawRepoType, rawWorktreeRepository) {
906
925
  const worktreeRepository = rawWorktreeRepository !== undefined ? rawWorktreeRepository : (await askWorktreeUrl()) || undefined;
907
926
  if (rawWorktreeRepository === undefined)
908
927
  console.log('');
909
- let barePath;
910
- let packageManager;
928
+ let worktreeConfig = {};
911
929
  if (worktreeRepository) {
912
- ({ barePath, packageManager } = await setupWorktreeEnvironment(cwd, worktreeRepository, agents));
930
+ const { barePath, packageManager, mainBranch } = await setupWorktreeEnvironment(cwd, worktreeRepository, agents);
931
+ worktreeConfig = { 'worktree-repository': worktreeRepository, 'bare-path': barePath, packageManager, mainBranch };
913
932
  }
914
933
  const npxCommand = await resolveNpxCommand(cwd);
915
934
  console.log('Setting up OSDDT command files...\n');
916
935
  await writeAgentFiles(cwd, agents, npxCommand);
917
- const config = { repoType, agents };
918
- if (worktreeRepository)
919
- config['worktree-repository'] = worktreeRepository;
920
- if (barePath)
921
- config['bare-path'] = barePath;
922
- if (packageManager)
923
- config['packageManager'] = packageManager;
936
+ const config = { repoType, agents, ...worktreeConfig };
924
937
  await writeConfig(cwd, config);
925
938
  console.log('\nSetup complete!');
926
939
  console.log('Commands created: osddt.spec, osddt.plan, osddt.tasks, osddt.implement');
@@ -973,9 +986,12 @@ function todayPrefix() {
973
986
  return `${yyyy}-${mm}-${dd}`;
974
987
  }
975
988
  async function runDone(featureName, cwd, worktree) {
976
- const src = path.join(cwd, 'working-on', featureName);
989
+ const barePath = worktree ? await resolveBarePath(process.cwd()) : undefined;
990
+ const worktreePath = worktree ? findWorktreeByFeature(barePath, featureName) : undefined;
991
+ const projectDir = worktreePath ?? cwd;
992
+ const src = path.join(projectDir, 'working-on', featureName);
977
993
  const destName = `${todayPrefix()}-${featureName}`;
978
- const dest = path.join(cwd, 'done', destName);
994
+ const dest = path.join(projectDir, 'done', destName);
979
995
  if (!(await fs.pathExists(src))) {
980
996
  console.error(`Error: working-on/${featureName} does not exist.`);
981
997
  process.exit(1);
@@ -985,8 +1001,6 @@ async function runDone(featureName, cwd, worktree) {
985
1001
  console.log(`Moved: working-on/${featureName} → done/${destName}`);
986
1002
  if (!worktree)
987
1003
  return;
988
- const barePath = await resolveBarePath(process.cwd());
989
- const worktreePath = findWorktreeByFeature(barePath, featureName);
990
1004
  if (!worktreePath) {
991
1005
  console.error(`Warning: No worktree found for "${featureName}". Skipping worktree cleanup.`);
992
1006
  return;
@@ -1162,7 +1176,7 @@ function remoteBranchExists(branch, cwd) {
1162
1176
  return false;
1163
1177
  }
1164
1178
  }
1165
- async function prompt(question) {
1179
+ async function prompt$1(question) {
1166
1180
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1167
1181
  return new Promise((resolve) => {
1168
1182
  rl.question(question, (answer) => {
@@ -1171,10 +1185,10 @@ async function prompt(question) {
1171
1185
  });
1172
1186
  });
1173
1187
  }
1174
- async function createWorktree(branch, worktreePath, repoRoot) {
1188
+ async function createWorktree(branch, worktreePath, repoRoot, mainBranch) {
1175
1189
  if (await fs.pathExists(worktreePath)) {
1176
1190
  console.log(`\nDirectory already exists at: ${worktreePath}`);
1177
- const answer = await prompt('Resume or Abort? [R/a] ');
1191
+ const answer = await prompt$1('Resume or Abort? [R/a] ');
1178
1192
  if (answer.toLowerCase() === 'a') {
1179
1193
  console.log('Aborted.');
1180
1194
  process.exit(0);
@@ -1185,7 +1199,7 @@ async function createWorktree(branch, worktreePath, repoRoot) {
1185
1199
  const remoteExists = !localExists && remoteBranchExists(branch, repoRoot);
1186
1200
  if (localExists || remoteExists) {
1187
1201
  console.log(`\nBranch "${branch}" already exists ${localExists ? 'locally' : 'on remote'}.`);
1188
- const answer = await prompt('Resume or Abort? [R/a] ');
1202
+ const answer = await prompt$1('Resume or Abort? [R/a] ');
1189
1203
  if (answer.toLowerCase() === 'a') {
1190
1204
  console.log('Aborted.');
1191
1205
  process.exit(0);
@@ -1193,7 +1207,7 @@ async function createWorktree(branch, worktreePath, repoRoot) {
1193
1207
  execSync(`git worktree add "${worktreePath}" ${branch}`, { cwd: repoRoot, stdio: 'inherit' });
1194
1208
  }
1195
1209
  else {
1196
- execSync(`git worktree add "${worktreePath}" -b ${branch}`, { cwd: repoRoot, stdio: 'inherit' });
1210
+ execSync(`git worktree add "${worktreePath}" -b ${branch} ${mainBranch}`, { cwd: repoRoot, stdio: 'inherit' });
1197
1211
  }
1198
1212
  }
1199
1213
  function runInstall(worktreePath, packageManager) {
@@ -1219,7 +1233,7 @@ async function runStartWorktree(featureName, options) {
1219
1233
  if (existingPath) {
1220
1234
  const workingDir = path.join(existingPath, 'working-on', featureName);
1221
1235
  console.log(`\nWorktree for "${featureName}" already exists at: ${existingPath}`);
1222
- const answer = await prompt('Resume or Abort? [R/a] ');
1236
+ const answer = await prompt$1('Resume or Abort? [R/a] ');
1223
1237
  if (answer.toLowerCase() === 'a') {
1224
1238
  console.log('Aborted.');
1225
1239
  process.exit(0);
@@ -1233,11 +1247,12 @@ async function runStartWorktree(featureName, options) {
1233
1247
  // Resolve worktree path — inside .bare as <barePath>/<featureName>
1234
1248
  const base = rc['worktreeBase'] ?? repoRoot;
1235
1249
  const worktreePath = path.join(base, featureName);
1236
- await createWorktree(branch, worktreePath, repoRoot);
1250
+ const mainBranch = rc['mainBranch'] ?? 'main';
1251
+ await createWorktree(branch, worktreePath, repoRoot, mainBranch);
1237
1252
  // Resolve working dir
1238
1253
  let projectPath;
1239
1254
  if (rc['repoType'] === 'monorepo') {
1240
- const pkg = options.dir ?? await prompt('Package path (e.g. packages/my-package): ');
1255
+ const pkg = options.dir ?? await prompt$1('Package path (e.g. packages/my-package): ');
1241
1256
  projectPath = path.join(worktreePath, pkg);
1242
1257
  }
1243
1258
  else {
@@ -1263,38 +1278,66 @@ function startWorktreeCommand() {
1263
1278
  return cmd;
1264
1279
  }
1265
1280
 
1266
- async function resolveRepoRoot(cwd) {
1281
+ async function prompt(question) {
1282
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1283
+ return new Promise((resolve) => {
1284
+ rl.question(question, (answer) => {
1285
+ rl.close();
1286
+ resolve(answer.trim());
1287
+ });
1288
+ });
1289
+ }
1290
+ async function selectWorktree(entries) {
1291
+ console.log('\nMultiple feature worktrees found:');
1292
+ entries.forEach((e, i) => {
1293
+ console.log(` ${i + 1}) ${e.featureName} (${e.branch})`);
1294
+ });
1295
+ const answer = await prompt(`Select a feature [1-${entries.length}]: `);
1296
+ const index = parseInt(answer, 10) - 1;
1297
+ if (index < 0 || index >= entries.length || isNaN(index)) {
1298
+ console.error('Invalid selection.');
1299
+ process.exit(1);
1300
+ }
1301
+ return entries[index];
1302
+ }
1303
+ async function runWorktreeInfo(featureName) {
1304
+ const cwd = process.cwd();
1305
+ const barePath = await resolveBarePath(cwd);
1267
1306
  const rcPath = path.join(cwd, '.osddtrc');
1307
+ let mainBranch = 'main';
1268
1308
  if (await fs.pathExists(rcPath)) {
1269
1309
  const rc = await fs.readJson(rcPath);
1270
- if (rc['bare-path'])
1271
- return rc['bare-path'];
1310
+ if (rc.mainBranch)
1311
+ mainBranch = rc.mainBranch;
1272
1312
  }
1273
- return execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim();
1274
- }
1275
- function stateFilePath(repoRoot) {
1276
- return path.join(path.dirname(repoRoot), '.osddt-worktrees');
1277
- }
1278
- async function runWorktreeInfo(featureName) {
1279
- const repoRoot = await resolveRepoRoot(process.cwd());
1280
- const stateFile = stateFilePath(repoRoot);
1281
- if (!(await fs.pathExists(stateFile))) {
1282
- console.error(`No .osddt-worktrees file found at: ${stateFile}`);
1283
- process.exit(1);
1313
+ const entries = listFeatureWorktrees(barePath, mainBranch);
1314
+ let entry;
1315
+ if (featureName) {
1316
+ entry = entries.find(e => e.featureName === featureName);
1317
+ if (!entry) {
1318
+ console.error(`No worktree found for feature: ${featureName}`);
1319
+ process.exit(1);
1320
+ }
1284
1321
  }
1285
- const entries = await fs.readJson(stateFile);
1286
- const entry = entries.find(e => e.featureName === featureName);
1287
- if (!entry) {
1288
- console.error(`No worktree entry found for feature: ${featureName}`);
1289
- process.exit(1);
1322
+ else {
1323
+ if (entries.length === 0) {
1324
+ console.error('No feature worktrees found.');
1325
+ process.exit(1);
1326
+ }
1327
+ else if (entries.length === 1) {
1328
+ entry = entries[0];
1329
+ }
1330
+ else {
1331
+ entry = await selectWorktree(entries);
1332
+ }
1290
1333
  }
1291
1334
  console.log(JSON.stringify({ worktreePath: entry.worktreePath, workingDir: entry.workingDir, branch: entry.branch }));
1292
1335
  }
1293
1336
  function worktreeInfoCommand() {
1294
1337
  const cmd = new Command('worktree-info');
1295
1338
  cmd
1296
- .description('Look up worktree paths for a feature from the state file')
1297
- .argument('<feature-name>', 'feature name to look up')
1339
+ .description('Look up worktree paths for a feature from git worktree list')
1340
+ .argument('[feature-name]', 'feature name to look up (optional if only one worktree exists)')
1298
1341
  .action(async (featureName) => {
1299
1342
  await runWorktreeInfo(featureName);
1300
1343
  });
@@ -1,3 +1,9 @@
1
+ export interface WorktreeEntry {
2
+ featureName: string;
3
+ branch: string;
4
+ worktreePath: string;
5
+ workingDir: string;
6
+ }
1
7
  export interface CheckResult {
2
8
  label: string;
3
9
  passed: boolean;
@@ -9,3 +15,4 @@ export declare function resolveBarePath(cwd: string): Promise<string>;
9
15
  export declare function findWorktreeByFeature(barePath: string, featureName: string): string | undefined;
10
16
  export declare function printCheckResult(result: CheckResult): void;
11
17
  export declare function runWorktreeChecks(barePath: string): Promise<boolean>;
18
+ export declare function listFeatureWorktrees(barePath: string, mainBranch: string): WorktreeEntry[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dezkareid/osddt",
3
- "version": "1.11.5",
3
+ "version": "1.11.6",
4
4
  "description": "Package for Spec-Driven Development workflow",
5
5
  "keywords": [
6
6
  "spec-driven",