@dezkareid/osddt 1.11.3 → 1.11.5
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 +101 -85
- 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,29 @@ 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
|
+
if (basename === featureName) {
|
|
748
|
+
return worktreePath;
|
|
749
|
+
}
|
|
742
750
|
}
|
|
751
|
+
return undefined;
|
|
743
752
|
}
|
|
744
753
|
function printCheckResult(result) {
|
|
745
754
|
const icon = result.passed ? '✓' : '✗';
|
|
@@ -831,8 +840,51 @@ function cloneBareRepository(cwd, repositoryUrl) {
|
|
|
831
840
|
console.log('');
|
|
832
841
|
return barePath;
|
|
833
842
|
}
|
|
834
|
-
|
|
843
|
+
function detectDefaultBranch(barePath) {
|
|
844
|
+
const output = execSync('git remote show origin', { cwd: barePath, encoding: 'utf-8' });
|
|
845
|
+
const match = output.match(/HEAD branch:\s*(\S+)/);
|
|
846
|
+
if (match)
|
|
847
|
+
return match[1];
|
|
848
|
+
// fallback: try main then master
|
|
849
|
+
try {
|
|
850
|
+
execSync('git rev-parse --verify main', { cwd: barePath, stdio: 'ignore' });
|
|
851
|
+
return 'main';
|
|
852
|
+
}
|
|
853
|
+
catch {
|
|
854
|
+
return 'master';
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
function addDefaultBranchWorktree(barePath, branch) {
|
|
858
|
+
const worktreePath = path.join(barePath, branch);
|
|
859
|
+
execSync(`git worktree add "${worktreePath}" ${branch}`, { cwd: barePath, stdio: 'inherit' });
|
|
860
|
+
}
|
|
861
|
+
async function detectPackageManager(worktreePath) {
|
|
862
|
+
if (await fs.pathExists(path.join(worktreePath, 'pnpm-lock.yaml')))
|
|
863
|
+
return 'pnpm';
|
|
864
|
+
if (await fs.pathExists(path.join(worktreePath, 'yarn.lock')))
|
|
865
|
+
return 'yarn';
|
|
866
|
+
if (await fs.pathExists(path.join(worktreePath, 'package-lock.json')))
|
|
867
|
+
return 'npm';
|
|
868
|
+
return 'npm';
|
|
869
|
+
}
|
|
870
|
+
async function writeAgentPointerFiles(cwd, agents, branch) {
|
|
871
|
+
if (agents.includes('claude')) {
|
|
872
|
+
const filePath = path.join(cwd, 'CLAUDE.md');
|
|
873
|
+
await fs.writeFile(filePath, `@.bare/${branch}/CLAUDE.md\n`, 'utf-8');
|
|
874
|
+
console.log(` Created: ${filePath}`);
|
|
875
|
+
}
|
|
876
|
+
if (agents.includes('gemini')) {
|
|
877
|
+
const filePath = path.join(cwd, 'GEMINI.md');
|
|
878
|
+
await fs.writeFile(filePath, `@.bare/${branch}/GEMINI.md\n`, 'utf-8');
|
|
879
|
+
console.log(` Created: ${filePath}`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
async function setupWorktreeEnvironment(cwd, worktreeRepository, agents) {
|
|
835
883
|
const barePath = cloneBareRepository(cwd, worktreeRepository);
|
|
884
|
+
const branch = detectDefaultBranch(barePath);
|
|
885
|
+
addDefaultBranchWorktree(barePath, branch);
|
|
886
|
+
const worktreePath = path.join(barePath, branch);
|
|
887
|
+
const packageManager = await detectPackageManager(worktreePath);
|
|
836
888
|
console.log('Checking environment for git worktree support...\n');
|
|
837
889
|
const allPassed = await runWorktreeChecks(barePath);
|
|
838
890
|
console.log('');
|
|
@@ -840,9 +892,9 @@ async function setupWorktreeEnvironment(cwd, worktreeRepository) {
|
|
|
840
892
|
console.log('Some checks failed. Resolve the issues above before using the worktree workflow.');
|
|
841
893
|
process.exit(1);
|
|
842
894
|
}
|
|
843
|
-
await
|
|
895
|
+
await writeAgentPointerFiles(cwd, agents, branch);
|
|
844
896
|
console.log('');
|
|
845
|
-
return barePath;
|
|
897
|
+
return { barePath, packageManager };
|
|
846
898
|
}
|
|
847
899
|
async function runSetup(cwd, rawAgents, rawRepoType, rawWorktreeRepository) {
|
|
848
900
|
const agents = rawAgents !== undefined ? parseAgents(rawAgents) : await askAgents();
|
|
@@ -855,8 +907,9 @@ async function runSetup(cwd, rawAgents, rawRepoType, rawWorktreeRepository) {
|
|
|
855
907
|
if (rawWorktreeRepository === undefined)
|
|
856
908
|
console.log('');
|
|
857
909
|
let barePath;
|
|
910
|
+
let packageManager;
|
|
858
911
|
if (worktreeRepository) {
|
|
859
|
-
barePath = await setupWorktreeEnvironment(cwd, worktreeRepository);
|
|
912
|
+
({ barePath, packageManager } = await setupWorktreeEnvironment(cwd, worktreeRepository, agents));
|
|
860
913
|
}
|
|
861
914
|
const npxCommand = await resolveNpxCommand(cwd);
|
|
862
915
|
console.log('Setting up OSDDT command files...\n');
|
|
@@ -866,6 +919,8 @@ async function runSetup(cwd, rawAgents, rawRepoType, rawWorktreeRepository) {
|
|
|
866
919
|
config['worktree-repository'] = worktreeRepository;
|
|
867
920
|
if (barePath)
|
|
868
921
|
config['bare-path'] = barePath;
|
|
922
|
+
if (packageManager)
|
|
923
|
+
config['packageManager'] = packageManager;
|
|
869
924
|
await writeConfig(cwd, config);
|
|
870
925
|
console.log('\nSetup complete!');
|
|
871
926
|
console.log('Commands created: osddt.spec, osddt.plan, osddt.tasks, osddt.implement');
|
|
@@ -917,18 +972,6 @@ function todayPrefix() {
|
|
|
917
972
|
const dd = String(now.getDate()).padStart(2, '0');
|
|
918
973
|
return `${yyyy}-${mm}-${dd}`;
|
|
919
974
|
}
|
|
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
975
|
async function runDone(featureName, cwd, worktree) {
|
|
933
976
|
const src = path.join(cwd, 'working-on', featureName);
|
|
934
977
|
const destName = `${todayPrefix()}-${featureName}`;
|
|
@@ -942,28 +985,19 @@ async function runDone(featureName, cwd, worktree) {
|
|
|
942
985
|
console.log(`Moved: working-on/${featureName} → done/${destName}`);
|
|
943
986
|
if (!worktree)
|
|
944
987
|
return;
|
|
945
|
-
const
|
|
946
|
-
const
|
|
947
|
-
if (!
|
|
948
|
-
console.error(`Warning:
|
|
988
|
+
const barePath = await resolveBarePath(process.cwd());
|
|
989
|
+
const worktreePath = findWorktreeByFeature(barePath, featureName);
|
|
990
|
+
if (!worktreePath) {
|
|
991
|
+
console.error(`Warning: No worktree found for "${featureName}". Skipping worktree cleanup.`);
|
|
949
992
|
return;
|
|
950
993
|
}
|
|
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}`);
|
|
994
|
+
if (await fs.pathExists(worktreePath)) {
|
|
995
|
+
execSync(`git worktree remove "${worktreePath}" --force`, { cwd: barePath, stdio: 'inherit' });
|
|
996
|
+
console.log(`Removed worktree: ${worktreePath}`);
|
|
960
997
|
}
|
|
961
998
|
else {
|
|
962
999
|
console.log(`Worktree path not found on filesystem, skipping git worktree remove.`);
|
|
963
1000
|
}
|
|
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
1001
|
}
|
|
968
1002
|
function doneCommand() {
|
|
969
1003
|
const cmd = new Command('done');
|
|
@@ -971,7 +1005,7 @@ function doneCommand() {
|
|
|
971
1005
|
.description('Move a feature from working-on/<feature-name> to done/<feature-name>')
|
|
972
1006
|
.argument('<feature-name>', 'name of the feature to mark as done')
|
|
973
1007
|
.option('-d, --dir <directory>', 'project directory', process.cwd())
|
|
974
|
-
.option('--worktree', 'also remove the git worktree
|
|
1008
|
+
.option('--worktree', 'also remove the git worktree')
|
|
975
1009
|
.action(async (featureName, options) => {
|
|
976
1010
|
const targetDir = path.resolve(options.dir);
|
|
977
1011
|
await runDone(featureName, targetDir, options.worktree ?? false);
|
|
@@ -1110,20 +1144,6 @@ function contextCommand() {
|
|
|
1110
1144
|
return cmd;
|
|
1111
1145
|
}
|
|
1112
1146
|
|
|
1113
|
-
function repoName(repoRoot) {
|
|
1114
|
-
return path.basename(repoRoot);
|
|
1115
|
-
}
|
|
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
1147
|
function branchExists(branch, cwd) {
|
|
1128
1148
|
try {
|
|
1129
1149
|
execSync(`git rev-parse --verify ${branch}`, { cwd, stdio: 'ignore' });
|
|
@@ -1151,15 +1171,6 @@ async function prompt(question) {
|
|
|
1151
1171
|
});
|
|
1152
1172
|
});
|
|
1153
1173
|
}
|
|
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
1174
|
async function createWorktree(branch, worktreePath, repoRoot) {
|
|
1164
1175
|
if (await fs.pathExists(worktreePath)) {
|
|
1165
1176
|
console.log(`\nDirectory already exists at: ${worktreePath}`);
|
|
@@ -1185,9 +1196,17 @@ async function createWorktree(branch, worktreePath, repoRoot) {
|
|
|
1185
1196
|
execSync(`git worktree add "${worktreePath}" -b ${branch}`, { cwd: repoRoot, stdio: 'inherit' });
|
|
1186
1197
|
}
|
|
1187
1198
|
}
|
|
1199
|
+
function runInstall(worktreePath, packageManager) {
|
|
1200
|
+
if (!packageManager) {
|
|
1201
|
+
console.log(`\n ℹ No packageManager configured in .osddtrc — skipping install. Run it manually inside ${worktreePath}`);
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
console.log(`\n Running ${packageManager} install...`);
|
|
1205
|
+
execSync(`${packageManager} install`, { cwd: worktreePath, stdio: 'inherit' });
|
|
1206
|
+
}
|
|
1188
1207
|
async function runStartWorktree(featureName, options) {
|
|
1189
1208
|
const cwd = process.cwd();
|
|
1190
|
-
const repoRoot = await
|
|
1209
|
+
const repoRoot = await resolveBarePath(cwd);
|
|
1191
1210
|
const branch = `feat/${featureName}`;
|
|
1192
1211
|
// Read .osddtrc
|
|
1193
1212
|
const rcPath = path.join(cwd, '.osddtrc');
|
|
@@ -1195,30 +1214,29 @@ async function runStartWorktree(featureName, options) {
|
|
|
1195
1214
|
if (await fs.pathExists(rcPath)) {
|
|
1196
1215
|
rc = await fs.readJson(rcPath);
|
|
1197
1216
|
}
|
|
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}`);
|
|
1217
|
+
// Check for existing worktree via git worktree list
|
|
1218
|
+
const existingPath = findWorktreeByFeature(repoRoot, featureName);
|
|
1219
|
+
if (existingPath) {
|
|
1220
|
+
const workingDir = path.join(existingPath, 'working-on', featureName);
|
|
1221
|
+
console.log(`\nWorktree for "${featureName}" already exists at: ${existingPath}`);
|
|
1207
1222
|
const answer = await prompt('Resume or Abort? [R/a] ');
|
|
1208
1223
|
if (answer.toLowerCase() === 'a') {
|
|
1209
1224
|
console.log('Aborted.');
|
|
1210
1225
|
process.exit(0);
|
|
1211
1226
|
}
|
|
1212
1227
|
console.log(`\nResuming existing worktree.`);
|
|
1213
|
-
console.log(` Branch: ${
|
|
1214
|
-
console.log(` Worktree path: ${
|
|
1215
|
-
console.log(` Working dir: ${
|
|
1228
|
+
console.log(` Branch: ${branch}`);
|
|
1229
|
+
console.log(` Worktree path: ${existingPath}`);
|
|
1230
|
+
console.log(` Working dir: ${workingDir}`);
|
|
1216
1231
|
return;
|
|
1217
1232
|
}
|
|
1233
|
+
// Resolve worktree path — inside .bare as <barePath>/<featureName>
|
|
1234
|
+
const base = rc['worktreeBase'] ?? repoRoot;
|
|
1235
|
+
const worktreePath = path.join(base, featureName);
|
|
1218
1236
|
await createWorktree(branch, worktreePath, repoRoot);
|
|
1219
1237
|
// Resolve working dir
|
|
1220
1238
|
let projectPath;
|
|
1221
|
-
if (rc
|
|
1239
|
+
if (rc['repoType'] === 'monorepo') {
|
|
1222
1240
|
const pkg = options.dir ?? await prompt('Package path (e.g. packages/my-package): ');
|
|
1223
1241
|
projectPath = path.join(worktreePath, pkg);
|
|
1224
1242
|
}
|
|
@@ -1227,9 +1245,7 @@ async function runStartWorktree(featureName, options) {
|
|
|
1227
1245
|
}
|
|
1228
1246
|
const workingDir = path.join(projectPath, 'working-on', featureName);
|
|
1229
1247
|
await fs.ensureDir(workingDir);
|
|
1230
|
-
|
|
1231
|
-
entries.push({ featureName, branch, worktreePath, workingDir, repoRoot });
|
|
1232
|
-
await writeStateFile(stateFile, entries);
|
|
1248
|
+
runInstall(worktreePath, rc['packageManager']);
|
|
1233
1249
|
console.log(`\nWorktree feature started:`);
|
|
1234
1250
|
console.log(` Branch: ${branch}`);
|
|
1235
1251
|
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>;
|