@dezkareid/osddt 1.11.4 → 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.
- package/dist/commands/start-worktree.d.ts +2 -7
- package/dist/index.js +91 -53
- package/dist/utils/worktree.d.ts +7 -0
- package/package.json +1 -1
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
|
|
3
|
-
|
|
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
|
@@ -744,9 +744,7 @@ function findWorktreeByFeature(barePath, featureName) {
|
|
|
744
744
|
continue;
|
|
745
745
|
const worktreePath = match[1].trim();
|
|
746
746
|
const basename = path.basename(worktreePath);
|
|
747
|
-
|
|
748
|
-
const suffixMatch = segments.length > 1 && basename.slice(basename.indexOf('-') + 1) === featureName;
|
|
749
|
-
if (basename === featureName || suffixMatch) {
|
|
747
|
+
if (basename === featureName) {
|
|
750
748
|
return worktreePath;
|
|
751
749
|
}
|
|
752
750
|
}
|
|
@@ -769,6 +767,25 @@ async function runWorktreeChecks(barePath) {
|
|
|
769
767
|
}
|
|
770
768
|
return results.every(r => r.passed);
|
|
771
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
|
+
}
|
|
772
789
|
|
|
773
790
|
const CANONICAL_PACKAGE_NAME = '@dezkareid/osddt';
|
|
774
791
|
const NPX_COMMAND = 'npx osddt';
|
|
@@ -896,7 +913,7 @@ async function setupWorktreeEnvironment(cwd, worktreeRepository, agents) {
|
|
|
896
913
|
}
|
|
897
914
|
await writeAgentPointerFiles(cwd, agents, branch);
|
|
898
915
|
console.log('');
|
|
899
|
-
return { barePath, packageManager };
|
|
916
|
+
return { barePath, packageManager, mainBranch: branch };
|
|
900
917
|
}
|
|
901
918
|
async function runSetup(cwd, rawAgents, rawRepoType, rawWorktreeRepository) {
|
|
902
919
|
const agents = rawAgents !== undefined ? parseAgents(rawAgents) : await askAgents();
|
|
@@ -908,21 +925,15 @@ async function runSetup(cwd, rawAgents, rawRepoType, rawWorktreeRepository) {
|
|
|
908
925
|
const worktreeRepository = rawWorktreeRepository !== undefined ? rawWorktreeRepository : (await askWorktreeUrl()) || undefined;
|
|
909
926
|
if (rawWorktreeRepository === undefined)
|
|
910
927
|
console.log('');
|
|
911
|
-
let
|
|
912
|
-
let packageManager;
|
|
928
|
+
let worktreeConfig = {};
|
|
913
929
|
if (worktreeRepository) {
|
|
914
|
-
|
|
930
|
+
const { barePath, packageManager, mainBranch } = await setupWorktreeEnvironment(cwd, worktreeRepository, agents);
|
|
931
|
+
worktreeConfig = { 'worktree-repository': worktreeRepository, 'bare-path': barePath, packageManager, mainBranch };
|
|
915
932
|
}
|
|
916
933
|
const npxCommand = await resolveNpxCommand(cwd);
|
|
917
934
|
console.log('Setting up OSDDT command files...\n');
|
|
918
935
|
await writeAgentFiles(cwd, agents, npxCommand);
|
|
919
|
-
const config = { repoType, agents };
|
|
920
|
-
if (worktreeRepository)
|
|
921
|
-
config['worktree-repository'] = worktreeRepository;
|
|
922
|
-
if (barePath)
|
|
923
|
-
config['bare-path'] = barePath;
|
|
924
|
-
if (packageManager)
|
|
925
|
-
config['packageManager'] = packageManager;
|
|
936
|
+
const config = { repoType, agents, ...worktreeConfig };
|
|
926
937
|
await writeConfig(cwd, config);
|
|
927
938
|
console.log('\nSetup complete!');
|
|
928
939
|
console.log('Commands created: osddt.spec, osddt.plan, osddt.tasks, osddt.implement');
|
|
@@ -975,9 +986,12 @@ function todayPrefix() {
|
|
|
975
986
|
return `${yyyy}-${mm}-${dd}`;
|
|
976
987
|
}
|
|
977
988
|
async function runDone(featureName, cwd, worktree) {
|
|
978
|
-
const
|
|
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);
|
|
979
993
|
const destName = `${todayPrefix()}-${featureName}`;
|
|
980
|
-
const dest = path.join(
|
|
994
|
+
const dest = path.join(projectDir, 'done', destName);
|
|
981
995
|
if (!(await fs.pathExists(src))) {
|
|
982
996
|
console.error(`Error: working-on/${featureName} does not exist.`);
|
|
983
997
|
process.exit(1);
|
|
@@ -987,8 +1001,6 @@ async function runDone(featureName, cwd, worktree) {
|
|
|
987
1001
|
console.log(`Moved: working-on/${featureName} → done/${destName}`);
|
|
988
1002
|
if (!worktree)
|
|
989
1003
|
return;
|
|
990
|
-
const barePath = await resolveBarePath(process.cwd());
|
|
991
|
-
const worktreePath = findWorktreeByFeature(barePath, featureName);
|
|
992
1004
|
if (!worktreePath) {
|
|
993
1005
|
console.error(`Warning: No worktree found for "${featureName}". Skipping worktree cleanup.`);
|
|
994
1006
|
return;
|
|
@@ -1146,9 +1158,6 @@ function contextCommand() {
|
|
|
1146
1158
|
return cmd;
|
|
1147
1159
|
}
|
|
1148
1160
|
|
|
1149
|
-
function repoName(repoRoot) {
|
|
1150
|
-
return path.basename(repoRoot);
|
|
1151
|
-
}
|
|
1152
1161
|
function branchExists(branch, cwd) {
|
|
1153
1162
|
try {
|
|
1154
1163
|
execSync(`git rev-parse --verify ${branch}`, { cwd, stdio: 'ignore' });
|
|
@@ -1167,7 +1176,7 @@ function remoteBranchExists(branch, cwd) {
|
|
|
1167
1176
|
return false;
|
|
1168
1177
|
}
|
|
1169
1178
|
}
|
|
1170
|
-
async function prompt(question) {
|
|
1179
|
+
async function prompt$1(question) {
|
|
1171
1180
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1172
1181
|
return new Promise((resolve) => {
|
|
1173
1182
|
rl.question(question, (answer) => {
|
|
@@ -1176,10 +1185,10 @@ async function prompt(question) {
|
|
|
1176
1185
|
});
|
|
1177
1186
|
});
|
|
1178
1187
|
}
|
|
1179
|
-
async function createWorktree(branch, worktreePath, repoRoot) {
|
|
1188
|
+
async function createWorktree(branch, worktreePath, repoRoot, mainBranch) {
|
|
1180
1189
|
if (await fs.pathExists(worktreePath)) {
|
|
1181
1190
|
console.log(`\nDirectory already exists at: ${worktreePath}`);
|
|
1182
|
-
const answer = await prompt('Resume or Abort? [R/a] ');
|
|
1191
|
+
const answer = await prompt$1('Resume or Abort? [R/a] ');
|
|
1183
1192
|
if (answer.toLowerCase() === 'a') {
|
|
1184
1193
|
console.log('Aborted.');
|
|
1185
1194
|
process.exit(0);
|
|
@@ -1190,7 +1199,7 @@ async function createWorktree(branch, worktreePath, repoRoot) {
|
|
|
1190
1199
|
const remoteExists = !localExists && remoteBranchExists(branch, repoRoot);
|
|
1191
1200
|
if (localExists || remoteExists) {
|
|
1192
1201
|
console.log(`\nBranch "${branch}" already exists ${localExists ? 'locally' : 'on remote'}.`);
|
|
1193
|
-
const answer = await prompt('Resume or Abort? [R/a] ');
|
|
1202
|
+
const answer = await prompt$1('Resume or Abort? [R/a] ');
|
|
1194
1203
|
if (answer.toLowerCase() === 'a') {
|
|
1195
1204
|
console.log('Aborted.');
|
|
1196
1205
|
process.exit(0);
|
|
@@ -1198,7 +1207,7 @@ async function createWorktree(branch, worktreePath, repoRoot) {
|
|
|
1198
1207
|
execSync(`git worktree add "${worktreePath}" ${branch}`, { cwd: repoRoot, stdio: 'inherit' });
|
|
1199
1208
|
}
|
|
1200
1209
|
else {
|
|
1201
|
-
execSync(`git worktree add "${worktreePath}" -b ${branch}`, { cwd: repoRoot, stdio: 'inherit' });
|
|
1210
|
+
execSync(`git worktree add "${worktreePath}" -b ${branch} ${mainBranch}`, { cwd: repoRoot, stdio: 'inherit' });
|
|
1202
1211
|
}
|
|
1203
1212
|
}
|
|
1204
1213
|
function runInstall(worktreePath, packageManager) {
|
|
@@ -1224,7 +1233,7 @@ async function runStartWorktree(featureName, options) {
|
|
|
1224
1233
|
if (existingPath) {
|
|
1225
1234
|
const workingDir = path.join(existingPath, 'working-on', featureName);
|
|
1226
1235
|
console.log(`\nWorktree for "${featureName}" already exists at: ${existingPath}`);
|
|
1227
|
-
const answer = await prompt('Resume or Abort? [R/a] ');
|
|
1236
|
+
const answer = await prompt$1('Resume or Abort? [R/a] ');
|
|
1228
1237
|
if (answer.toLowerCase() === 'a') {
|
|
1229
1238
|
console.log('Aborted.');
|
|
1230
1239
|
process.exit(0);
|
|
@@ -1235,14 +1244,15 @@ async function runStartWorktree(featureName, options) {
|
|
|
1235
1244
|
console.log(` Working dir: ${workingDir}`);
|
|
1236
1245
|
return;
|
|
1237
1246
|
}
|
|
1238
|
-
// Resolve worktree path —
|
|
1239
|
-
const base = rc['worktreeBase'] ??
|
|
1240
|
-
const worktreePath = path.join(base,
|
|
1241
|
-
|
|
1247
|
+
// Resolve worktree path — inside .bare as <barePath>/<featureName>
|
|
1248
|
+
const base = rc['worktreeBase'] ?? repoRoot;
|
|
1249
|
+
const worktreePath = path.join(base, featureName);
|
|
1250
|
+
const mainBranch = rc['mainBranch'] ?? 'main';
|
|
1251
|
+
await createWorktree(branch, worktreePath, repoRoot, mainBranch);
|
|
1242
1252
|
// Resolve working dir
|
|
1243
1253
|
let projectPath;
|
|
1244
1254
|
if (rc['repoType'] === 'monorepo') {
|
|
1245
|
-
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): ');
|
|
1246
1256
|
projectPath = path.join(worktreePath, pkg);
|
|
1247
1257
|
}
|
|
1248
1258
|
else {
|
|
@@ -1268,38 +1278,66 @@ function startWorktreeCommand() {
|
|
|
1268
1278
|
return cmd;
|
|
1269
1279
|
}
|
|
1270
1280
|
|
|
1271
|
-
async function
|
|
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);
|
|
1272
1306
|
const rcPath = path.join(cwd, '.osddtrc');
|
|
1307
|
+
let mainBranch = 'main';
|
|
1273
1308
|
if (await fs.pathExists(rcPath)) {
|
|
1274
1309
|
const rc = await fs.readJson(rcPath);
|
|
1275
|
-
if (rc
|
|
1276
|
-
|
|
1310
|
+
if (rc.mainBranch)
|
|
1311
|
+
mainBranch = rc.mainBranch;
|
|
1277
1312
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
if (!(await fs.pathExists(stateFile))) {
|
|
1287
|
-
console.error(`No .osddt-worktrees file found at: ${stateFile}`);
|
|
1288
|
-
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
|
+
}
|
|
1289
1321
|
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
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
|
+
}
|
|
1295
1333
|
}
|
|
1296
1334
|
console.log(JSON.stringify({ worktreePath: entry.worktreePath, workingDir: entry.workingDir, branch: entry.branch }));
|
|
1297
1335
|
}
|
|
1298
1336
|
function worktreeInfoCommand() {
|
|
1299
1337
|
const cmd = new Command('worktree-info');
|
|
1300
1338
|
cmd
|
|
1301
|
-
.description('Look up worktree paths for a feature from
|
|
1302
|
-
.argument('
|
|
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)')
|
|
1303
1341
|
.action(async (featureName) => {
|
|
1304
1342
|
await runWorktreeInfo(featureName);
|
|
1305
1343
|
});
|
package/dist/utils/worktree.d.ts
CHANGED
|
@@ -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[];
|