@dezkareid/osddt 1.11.3 → 1.11.4
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/setup.d.ts +6 -0
- package/dist/index.js +103 -82
- package/dist/utils/worktree.d.ts +2 -1
- package/package.json +1 -1
package/dist/commands/setup.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import { type AgentType } from '../utils/prompt.js';
|
|
2
3
|
export declare function resolveNpxCommand(cwd: string): Promise<string>;
|
|
4
|
+
export declare function cloneBareRepository(cwd: string, repositoryUrl: string): string;
|
|
5
|
+
export declare function detectDefaultBranch(barePath: string): string;
|
|
6
|
+
export declare function addDefaultBranchWorktree(barePath: string, branch: string): void;
|
|
7
|
+
export declare function detectPackageManager(worktreePath: string): Promise<string>;
|
|
8
|
+
export declare function writeAgentPointerFiles(cwd: string, agents: AgentType[], branch: string): Promise<void>;
|
|
3
9
|
export declare function setupCommand(): Command;
|
package/dist/index.js
CHANGED
|
@@ -726,20 +726,31 @@ async function checkTargetWritable(barePath) {
|
|
|
726
726
|
return { label: 'Worktree target directory is writable', passed: false, detail: `${targetBase} is not writable` };
|
|
727
727
|
}
|
|
728
728
|
}
|
|
729
|
-
async function
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
}
|
|
736
|
-
else {
|
|
737
|
-
console.log(` ✓ Worktree state file already exists: ${stateFile}`);
|
|
738
|
-
}
|
|
729
|
+
async function resolveBarePath(cwd) {
|
|
730
|
+
const rcPath = path.join(cwd, '.osddtrc');
|
|
731
|
+
if (await fs.pathExists(rcPath)) {
|
|
732
|
+
const rc = await fs.readJson(rcPath);
|
|
733
|
+
if (rc['bare-path'])
|
|
734
|
+
return rc['bare-path'];
|
|
739
735
|
}
|
|
740
|
-
|
|
741
|
-
|
|
736
|
+
return execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim();
|
|
737
|
+
}
|
|
738
|
+
function findWorktreeByFeature(barePath, featureName) {
|
|
739
|
+
const output = execSync('git worktree list --porcelain', { cwd: barePath, encoding: 'utf-8' });
|
|
740
|
+
const blocks = output.trim().split(/\n\n+/);
|
|
741
|
+
for (const block of blocks) {
|
|
742
|
+
const match = block.match(/^worktree (.+)$/m);
|
|
743
|
+
if (!match)
|
|
744
|
+
continue;
|
|
745
|
+
const worktreePath = match[1].trim();
|
|
746
|
+
const basename = path.basename(worktreePath);
|
|
747
|
+
const segments = basename.split('-');
|
|
748
|
+
const suffixMatch = segments.length > 1 && basename.slice(basename.indexOf('-') + 1) === featureName;
|
|
749
|
+
if (basename === featureName || suffixMatch) {
|
|
750
|
+
return worktreePath;
|
|
751
|
+
}
|
|
742
752
|
}
|
|
753
|
+
return undefined;
|
|
743
754
|
}
|
|
744
755
|
function printCheckResult(result) {
|
|
745
756
|
const icon = result.passed ? '✓' : '✗';
|
|
@@ -831,8 +842,51 @@ function cloneBareRepository(cwd, repositoryUrl) {
|
|
|
831
842
|
console.log('');
|
|
832
843
|
return barePath;
|
|
833
844
|
}
|
|
834
|
-
|
|
845
|
+
function detectDefaultBranch(barePath) {
|
|
846
|
+
const output = execSync('git remote show origin', { cwd: barePath, encoding: 'utf-8' });
|
|
847
|
+
const match = output.match(/HEAD branch:\s*(\S+)/);
|
|
848
|
+
if (match)
|
|
849
|
+
return match[1];
|
|
850
|
+
// fallback: try main then master
|
|
851
|
+
try {
|
|
852
|
+
execSync('git rev-parse --verify main', { cwd: barePath, stdio: 'ignore' });
|
|
853
|
+
return 'main';
|
|
854
|
+
}
|
|
855
|
+
catch {
|
|
856
|
+
return 'master';
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
function addDefaultBranchWorktree(barePath, branch) {
|
|
860
|
+
const worktreePath = path.join(barePath, branch);
|
|
861
|
+
execSync(`git worktree add "${worktreePath}" ${branch}`, { cwd: barePath, stdio: 'inherit' });
|
|
862
|
+
}
|
|
863
|
+
async function detectPackageManager(worktreePath) {
|
|
864
|
+
if (await fs.pathExists(path.join(worktreePath, 'pnpm-lock.yaml')))
|
|
865
|
+
return 'pnpm';
|
|
866
|
+
if (await fs.pathExists(path.join(worktreePath, 'yarn.lock')))
|
|
867
|
+
return 'yarn';
|
|
868
|
+
if (await fs.pathExists(path.join(worktreePath, 'package-lock.json')))
|
|
869
|
+
return 'npm';
|
|
870
|
+
return 'npm';
|
|
871
|
+
}
|
|
872
|
+
async function writeAgentPointerFiles(cwd, agents, branch) {
|
|
873
|
+
if (agents.includes('claude')) {
|
|
874
|
+
const filePath = path.join(cwd, 'CLAUDE.md');
|
|
875
|
+
await fs.writeFile(filePath, `@.bare/${branch}/CLAUDE.md\n`, 'utf-8');
|
|
876
|
+
console.log(` Created: ${filePath}`);
|
|
877
|
+
}
|
|
878
|
+
if (agents.includes('gemini')) {
|
|
879
|
+
const filePath = path.join(cwd, 'GEMINI.md');
|
|
880
|
+
await fs.writeFile(filePath, `@.bare/${branch}/GEMINI.md\n`, 'utf-8');
|
|
881
|
+
console.log(` Created: ${filePath}`);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
async function setupWorktreeEnvironment(cwd, worktreeRepository, agents) {
|
|
835
885
|
const barePath = cloneBareRepository(cwd, worktreeRepository);
|
|
886
|
+
const branch = detectDefaultBranch(barePath);
|
|
887
|
+
addDefaultBranchWorktree(barePath, branch);
|
|
888
|
+
const worktreePath = path.join(barePath, branch);
|
|
889
|
+
const packageManager = await detectPackageManager(worktreePath);
|
|
836
890
|
console.log('Checking environment for git worktree support...\n');
|
|
837
891
|
const allPassed = await runWorktreeChecks(barePath);
|
|
838
892
|
console.log('');
|
|
@@ -840,9 +894,9 @@ async function setupWorktreeEnvironment(cwd, worktreeRepository) {
|
|
|
840
894
|
console.log('Some checks failed. Resolve the issues above before using the worktree workflow.');
|
|
841
895
|
process.exit(1);
|
|
842
896
|
}
|
|
843
|
-
await
|
|
897
|
+
await writeAgentPointerFiles(cwd, agents, branch);
|
|
844
898
|
console.log('');
|
|
845
|
-
return barePath;
|
|
899
|
+
return { barePath, packageManager };
|
|
846
900
|
}
|
|
847
901
|
async function runSetup(cwd, rawAgents, rawRepoType, rawWorktreeRepository) {
|
|
848
902
|
const agents = rawAgents !== undefined ? parseAgents(rawAgents) : await askAgents();
|
|
@@ -855,8 +909,9 @@ async function runSetup(cwd, rawAgents, rawRepoType, rawWorktreeRepository) {
|
|
|
855
909
|
if (rawWorktreeRepository === undefined)
|
|
856
910
|
console.log('');
|
|
857
911
|
let barePath;
|
|
912
|
+
let packageManager;
|
|
858
913
|
if (worktreeRepository) {
|
|
859
|
-
barePath = await setupWorktreeEnvironment(cwd, worktreeRepository);
|
|
914
|
+
({ barePath, packageManager } = await setupWorktreeEnvironment(cwd, worktreeRepository, agents));
|
|
860
915
|
}
|
|
861
916
|
const npxCommand = await resolveNpxCommand(cwd);
|
|
862
917
|
console.log('Setting up OSDDT command files...\n');
|
|
@@ -866,6 +921,8 @@ async function runSetup(cwd, rawAgents, rawRepoType, rawWorktreeRepository) {
|
|
|
866
921
|
config['worktree-repository'] = worktreeRepository;
|
|
867
922
|
if (barePath)
|
|
868
923
|
config['bare-path'] = barePath;
|
|
924
|
+
if (packageManager)
|
|
925
|
+
config['packageManager'] = packageManager;
|
|
869
926
|
await writeConfig(cwd, config);
|
|
870
927
|
console.log('\nSetup complete!');
|
|
871
928
|
console.log('Commands created: osddt.spec, osddt.plan, osddt.tasks, osddt.implement');
|
|
@@ -917,18 +974,6 @@ function todayPrefix() {
|
|
|
917
974
|
const dd = String(now.getDate()).padStart(2, '0');
|
|
918
975
|
return `${yyyy}-${mm}-${dd}`;
|
|
919
976
|
}
|
|
920
|
-
async function resolveRepoRoot$2(cwd) {
|
|
921
|
-
const rcPath = path.join(cwd, '.osddtrc');
|
|
922
|
-
if (await fs.pathExists(rcPath)) {
|
|
923
|
-
const rc = await fs.readJson(rcPath);
|
|
924
|
-
if (rc['bare-path'])
|
|
925
|
-
return rc['bare-path'];
|
|
926
|
-
}
|
|
927
|
-
return execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim();
|
|
928
|
-
}
|
|
929
|
-
function stateFilePath$2(repoRoot) {
|
|
930
|
-
return path.join(path.dirname(repoRoot), '.osddt-worktrees');
|
|
931
|
-
}
|
|
932
977
|
async function runDone(featureName, cwd, worktree) {
|
|
933
978
|
const src = path.join(cwd, 'working-on', featureName);
|
|
934
979
|
const destName = `${todayPrefix()}-${featureName}`;
|
|
@@ -942,28 +987,19 @@ async function runDone(featureName, cwd, worktree) {
|
|
|
942
987
|
console.log(`Moved: working-on/${featureName} → done/${destName}`);
|
|
943
988
|
if (!worktree)
|
|
944
989
|
return;
|
|
945
|
-
const
|
|
946
|
-
const
|
|
947
|
-
if (!
|
|
948
|
-
console.error(`Warning:
|
|
990
|
+
const barePath = await resolveBarePath(process.cwd());
|
|
991
|
+
const worktreePath = findWorktreeByFeature(barePath, featureName);
|
|
992
|
+
if (!worktreePath) {
|
|
993
|
+
console.error(`Warning: No worktree found for "${featureName}". Skipping worktree cleanup.`);
|
|
949
994
|
return;
|
|
950
995
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
console.error(`Warning: No worktree entry found for "${featureName}". Skipping worktree cleanup.`);
|
|
955
|
-
return;
|
|
956
|
-
}
|
|
957
|
-
if (await fs.pathExists(entry.worktreePath)) {
|
|
958
|
-
execSync(`git worktree remove "${entry.worktreePath}" --force`, { cwd: repoRoot, stdio: 'inherit' });
|
|
959
|
-
console.log(`Removed worktree: ${entry.worktreePath}`);
|
|
996
|
+
if (await fs.pathExists(worktreePath)) {
|
|
997
|
+
execSync(`git worktree remove "${worktreePath}" --force`, { cwd: barePath, stdio: 'inherit' });
|
|
998
|
+
console.log(`Removed worktree: ${worktreePath}`);
|
|
960
999
|
}
|
|
961
1000
|
else {
|
|
962
1001
|
console.log(`Worktree path not found on filesystem, skipping git worktree remove.`);
|
|
963
1002
|
}
|
|
964
|
-
const updated = entries.filter(e => e.featureName !== featureName);
|
|
965
|
-
await fs.writeJson(stateFile, updated, { spaces: 2 });
|
|
966
|
-
console.log(`Removed state entry for "${featureName}" from .osddt-worktrees`);
|
|
967
1003
|
}
|
|
968
1004
|
function doneCommand() {
|
|
969
1005
|
const cmd = new Command('done');
|
|
@@ -971,7 +1007,7 @@ function doneCommand() {
|
|
|
971
1007
|
.description('Move a feature from working-on/<feature-name> to done/<feature-name>')
|
|
972
1008
|
.argument('<feature-name>', 'name of the feature to mark as done')
|
|
973
1009
|
.option('-d, --dir <directory>', 'project directory', process.cwd())
|
|
974
|
-
.option('--worktree', 'also remove the git worktree
|
|
1010
|
+
.option('--worktree', 'also remove the git worktree')
|
|
975
1011
|
.action(async (featureName, options) => {
|
|
976
1012
|
const targetDir = path.resolve(options.dir);
|
|
977
1013
|
await runDone(featureName, targetDir, options.worktree ?? false);
|
|
@@ -1113,17 +1149,6 @@ function contextCommand() {
|
|
|
1113
1149
|
function repoName(repoRoot) {
|
|
1114
1150
|
return path.basename(repoRoot);
|
|
1115
1151
|
}
|
|
1116
|
-
function stateFilePath$1(repoRoot) {
|
|
1117
|
-
return path.join(path.dirname(repoRoot), '.osddt-worktrees');
|
|
1118
|
-
}
|
|
1119
|
-
async function readStateFile(stateFile) {
|
|
1120
|
-
if (!(await fs.pathExists(stateFile)))
|
|
1121
|
-
return [];
|
|
1122
|
-
return fs.readJson(stateFile);
|
|
1123
|
-
}
|
|
1124
|
-
async function writeStateFile(stateFile, entries) {
|
|
1125
|
-
await fs.writeJson(stateFile, entries, { spaces: 2 });
|
|
1126
|
-
}
|
|
1127
1152
|
function branchExists(branch, cwd) {
|
|
1128
1153
|
try {
|
|
1129
1154
|
execSync(`git rev-parse --verify ${branch}`, { cwd, stdio: 'ignore' });
|
|
@@ -1151,15 +1176,6 @@ async function prompt(question) {
|
|
|
1151
1176
|
});
|
|
1152
1177
|
});
|
|
1153
1178
|
}
|
|
1154
|
-
async function resolveRepoRoot$1(cwd) {
|
|
1155
|
-
const rcPath = path.join(cwd, '.osddtrc');
|
|
1156
|
-
if (await fs.pathExists(rcPath)) {
|
|
1157
|
-
const rc = await fs.readJson(rcPath);
|
|
1158
|
-
if (rc['bare-path'])
|
|
1159
|
-
return rc['bare-path'];
|
|
1160
|
-
}
|
|
1161
|
-
return execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim();
|
|
1162
|
-
}
|
|
1163
1179
|
async function createWorktree(branch, worktreePath, repoRoot) {
|
|
1164
1180
|
if (await fs.pathExists(worktreePath)) {
|
|
1165
1181
|
console.log(`\nDirectory already exists at: ${worktreePath}`);
|
|
@@ -1185,9 +1201,17 @@ async function createWorktree(branch, worktreePath, repoRoot) {
|
|
|
1185
1201
|
execSync(`git worktree add "${worktreePath}" -b ${branch}`, { cwd: repoRoot, stdio: 'inherit' });
|
|
1186
1202
|
}
|
|
1187
1203
|
}
|
|
1204
|
+
function runInstall(worktreePath, packageManager) {
|
|
1205
|
+
if (!packageManager) {
|
|
1206
|
+
console.log(`\n ℹ No packageManager configured in .osddtrc — skipping install. Run it manually inside ${worktreePath}`);
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
console.log(`\n Running ${packageManager} install...`);
|
|
1210
|
+
execSync(`${packageManager} install`, { cwd: worktreePath, stdio: 'inherit' });
|
|
1211
|
+
}
|
|
1188
1212
|
async function runStartWorktree(featureName, options) {
|
|
1189
1213
|
const cwd = process.cwd();
|
|
1190
|
-
const repoRoot = await
|
|
1214
|
+
const repoRoot = await resolveBarePath(cwd);
|
|
1191
1215
|
const branch = `feat/${featureName}`;
|
|
1192
1216
|
// Read .osddtrc
|
|
1193
1217
|
const rcPath = path.join(cwd, '.osddtrc');
|
|
@@ -1195,30 +1219,29 @@ async function runStartWorktree(featureName, options) {
|
|
|
1195
1219
|
if (await fs.pathExists(rcPath)) {
|
|
1196
1220
|
rc = await fs.readJson(rcPath);
|
|
1197
1221
|
}
|
|
1198
|
-
//
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
const entries = await readStateFile(stateFile);
|
|
1204
|
-
const existing = entries.find(e => e.featureName === featureName);
|
|
1205
|
-
if (existing) {
|
|
1206
|
-
console.log(`\nWorktree for "${featureName}" already exists at: ${existing.worktreePath}`);
|
|
1222
|
+
// Check for existing worktree via git worktree list
|
|
1223
|
+
const existingPath = findWorktreeByFeature(repoRoot, featureName);
|
|
1224
|
+
if (existingPath) {
|
|
1225
|
+
const workingDir = path.join(existingPath, 'working-on', featureName);
|
|
1226
|
+
console.log(`\nWorktree for "${featureName}" already exists at: ${existingPath}`);
|
|
1207
1227
|
const answer = await prompt('Resume or Abort? [R/a] ');
|
|
1208
1228
|
if (answer.toLowerCase() === 'a') {
|
|
1209
1229
|
console.log('Aborted.');
|
|
1210
1230
|
process.exit(0);
|
|
1211
1231
|
}
|
|
1212
1232
|
console.log(`\nResuming existing worktree.`);
|
|
1213
|
-
console.log(` Branch: ${
|
|
1214
|
-
console.log(` Worktree path: ${
|
|
1215
|
-
console.log(` Working dir: ${
|
|
1233
|
+
console.log(` Branch: ${branch}`);
|
|
1234
|
+
console.log(` Worktree path: ${existingPath}`);
|
|
1235
|
+
console.log(` Working dir: ${workingDir}`);
|
|
1216
1236
|
return;
|
|
1217
1237
|
}
|
|
1238
|
+
// Resolve worktree path — sibling of repoRoot (i.e. sibling of .bare)
|
|
1239
|
+
const base = rc['worktreeBase'] ?? path.dirname(repoRoot);
|
|
1240
|
+
const worktreePath = path.join(base, `${repoName(repoRoot)}-${featureName}`);
|
|
1218
1241
|
await createWorktree(branch, worktreePath, repoRoot);
|
|
1219
1242
|
// Resolve working dir
|
|
1220
1243
|
let projectPath;
|
|
1221
|
-
if (rc
|
|
1244
|
+
if (rc['repoType'] === 'monorepo') {
|
|
1222
1245
|
const pkg = options.dir ?? await prompt('Package path (e.g. packages/my-package): ');
|
|
1223
1246
|
projectPath = path.join(worktreePath, pkg);
|
|
1224
1247
|
}
|
|
@@ -1227,9 +1250,7 @@ async function runStartWorktree(featureName, options) {
|
|
|
1227
1250
|
}
|
|
1228
1251
|
const workingDir = path.join(projectPath, 'working-on', featureName);
|
|
1229
1252
|
await fs.ensureDir(workingDir);
|
|
1230
|
-
|
|
1231
|
-
entries.push({ featureName, branch, worktreePath, workingDir, repoRoot });
|
|
1232
|
-
await writeStateFile(stateFile, entries);
|
|
1253
|
+
runInstall(worktreePath, rc['packageManager']);
|
|
1233
1254
|
console.log(`\nWorktree feature started:`);
|
|
1234
1255
|
console.log(` Branch: ${branch}`);
|
|
1235
1256
|
console.log(` Worktree path: ${worktreePath}`);
|
package/dist/utils/worktree.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface CheckResult {
|
|
|
5
5
|
}
|
|
6
6
|
export declare function checkGitVersion(): CheckResult;
|
|
7
7
|
export declare function checkTargetWritable(barePath: string): Promise<CheckResult>;
|
|
8
|
-
export declare function
|
|
8
|
+
export declare function resolveBarePath(cwd: string): Promise<string>;
|
|
9
|
+
export declare function findWorktreeByFeature(barePath: string, featureName: string): string | undefined;
|
|
9
10
|
export declare function printCheckResult(result: CheckResult): void;
|
|
10
11
|
export declare function runWorktreeChecks(barePath: string): Promise<boolean>;
|