@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.
- package/dist/commands/start-worktree.d.ts +2 -7
- package/dist/index.js +87 -44
- 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
|
@@ -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
|
|
910
|
-
let packageManager;
|
|
928
|
+
let worktreeConfig = {};
|
|
911
929
|
if (worktreeRepository) {
|
|
912
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
|
1271
|
-
|
|
1310
|
+
if (rc.mainBranch)
|
|
1311
|
+
mainBranch = rc.mainBranch;
|
|
1272
1312
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
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
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
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
|
|
1297
|
-
.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)')
|
|
1298
1341
|
.action(async (featureName) => {
|
|
1299
1342
|
await runWorktreeInfo(featureName);
|
|
1300
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[];
|