@goodtek/vibeops 2.5.2 → 3.1.0

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,6 +1,7 @@
1
1
  import { relative, resolve } from "node:path";
2
2
  import { branchNameForTaskFile, readGitContext, readTaskFile, updateInlineStatus, upsertGitContext, } from "./task.js";
3
- import { gitBranchExists, gitCheckout, gitCheckoutNewBranch, gitGovernanceOnlyDirty, gitHeadCommit, gitSwitchToBranch, readGitInfo, } from "./git.js";
3
+ import { gitBranchExists, gitCheckout, gitCheckoutNewBranch, gitGovernanceOnlyDirty, gitRevParse, readGitInfo, } from "./git.js";
4
+ import { resolveIntegrationBaseRef } from "./git-worktree.js";
4
5
  import { ensureIntegrationSynced, printIntegrationSyncDiagnosis, } from "./git-integration-sync.js";
5
6
  import { cyan, dim, log } from "./logger.js";
6
7
  function relOrAbs(root, p) {
@@ -42,40 +43,48 @@ export async function startTaskBranch(opts) {
42
43
  const existingCtx = await readGitContext(taskFile);
43
44
  const branchExists = await gitBranchExists(cwd, taskBranch);
44
45
  if (opts.dryRun) {
46
+ const base = (await resolveIntegrationBaseRef(cwd, remote, integrationBranch)) ??
47
+ `${remote}/${integrationBranch}`;
45
48
  log.info(` ${dim("integration")} ${integrationBranch}`);
46
49
  log.info(` ${dim("task branch")} ${cyan(taskBranch)}`);
47
50
  log.info(dim(branchExists
48
51
  ? `dry-run — would git switch ${taskBranch}`
49
- : `dry-run — would checkout ${integrationBranch}, then git switch -c ${taskBranch}`));
52
+ : `dry-run — would git fetch ${remote} ${integrationBranch}, then git switch -c ${taskBranch} --no-track ${base}`));
50
53
  return true;
51
54
  }
52
- if (!branchExists && git.branch !== integrationBranch) {
53
- const ok = await gitSwitchToBranch(cwd, integrationBranch, remote);
54
- if (!ok) {
55
- log.error(`Integration branch "${integrationBranch}" not found locally or on ${remote}. Run vibeops init or create the branch.`);
56
- return false;
55
+ // Worktree-safe (D-006): never check out the integration branch — another
56
+ // worktree may own it. New task branches start from <remote>/<integration>.
57
+ let baseRef = null;
58
+ if (!branchExists) {
59
+ if (opts.skipIntegrationPull === true) {
60
+ baseRef = await resolveIntegrationBaseRef(cwd, remote, integrationBranch);
57
61
  }
58
- }
59
- // Ensure integration branch is up-to-date before creating a new task branch.
60
- if (!branchExists && opts.skipIntegrationPull !== true) {
61
- const synced = await ensureIntegrationSynced({
62
- cwd,
63
- remote,
64
- integrationBranch,
65
- fetch: true,
66
- });
67
- if (!synced.ok) {
68
- printIntegrationSyncDiagnosis(synced.diagnosis);
69
- log.blank();
70
- log.info(dim(`TASK file is already created — after fixing sync, rerun ${cyan("vibeops task add")} to resume the branch (does not create a second TASK).`));
71
- return false;
62
+ else {
63
+ const synced = await ensureIntegrationSynced({
64
+ cwd,
65
+ remote,
66
+ integrationBranch,
67
+ fetch: true,
68
+ });
69
+ if (!synced.ok) {
70
+ printIntegrationSyncDiagnosis(synced.diagnosis);
71
+ log.blank();
72
+ log.info(dim(`TASK file is already created — after fixing sync, rerun ${cyan("vibeops task add")} to resume the branch (does not create a second TASK).`));
73
+ return false;
74
+ }
75
+ if (synced.pulled) {
76
+ log.info(dim(synced.diagnosis.summary));
77
+ }
78
+ baseRef = synced.baseRef;
72
79
  }
73
- if (synced.pulled) {
74
- log.info(dim(synced.diagnosis.summary));
80
+ if (baseRef === null) {
81
+ log.error(`Integration branch "${integrationBranch}" not found locally or on ${remote}. Run vibeops init or create the branch.`);
82
+ return false;
75
83
  }
76
84
  }
77
85
  const baseBranch = integrationBranch;
78
- const baseCommit = (await gitHeadCommit(cwd)) ?? "";
86
+ // Full SHA: unambiguous base for later diff ranges (display is shortened below).
87
+ const baseCommit = baseRef !== null ? ((await gitRevParse(cwd, `${baseRef}^{commit}`)) ?? "") : "";
79
88
  if (baseCommit.length === 0 && !branchExists) {
80
89
  log.error("No commits on integration branch. Create an initial commit first.");
81
90
  return false;
@@ -86,7 +95,7 @@ export async function startTaskBranch(opts) {
86
95
  taskBranch,
87
96
  startedAt: existingCtx?.startedAt ?? new Date().toISOString(),
88
97
  };
89
- log.info(` ${dim("integration")} ${baseBranch} @ ${ctx.baseCommit.slice(0, 7)}`);
98
+ log.info(` ${dim("integration")} ${baseRef ?? baseBranch} @ ${ctx.baseCommit.slice(0, 7)}`);
90
99
  log.info(` ${dim("task branch")} ${cyan(taskBranch)}`);
91
100
  if (branchExists) {
92
101
  await gitCheckout(cwd, taskBranch);
@@ -99,7 +108,7 @@ export async function startTaskBranch(opts) {
99
108
  log.ok(`Resumed ${taskBranch}`);
100
109
  }
101
110
  else {
102
- await gitCheckoutNewBranch(cwd, taskBranch, baseBranch);
111
+ await gitCheckoutNewBranch(cwd, taskBranch, baseRef, { noTrack: true });
103
112
  await updateInlineStatus(taskFile, "in_progress");
104
113
  await upsertGitContext(taskFile, ctx);
105
114
  log.ok(`Branch ${taskBranch} ready`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodtek/vibeops",
3
- "version": "2.5.2",
3
+ "version": "3.1.0",
4
4
  "description": "Minimal CLI rails for Cursor-based TASK-driven vibe coding.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@9.15.9",