@goodtek/vibeops 2.5.1 → 2.5.2

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/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to VibeOps are documented here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 2.5.2 - 2026-07-20
8
+
9
+ ### Fixed
10
+
11
+ - **`task add` / integration sync:** Governance-only dirt (`.vibeops.json`, `AGENTS.md`, `.vibeops/`, docs, `.cursor/rules/`) no longer blocks preflight. Right after `init`, `task add` works without stash. App-file dirt still fails and lists the blocking paths.
12
+
7
13
  ## 2.5.1 - 2026-07-20
8
14
 
9
15
  ### Fixed
package/README.md CHANGED
@@ -111,7 +111,7 @@ vibeops task release
111
111
 
112
112
  Only one TASK **In Progress** at a time (`task add` blocks otherwise). **Shipped** slices do not block the next add; merge on the host or with `task merge`, then optional `task sync`.
113
113
 
114
- `task add` fast-forward-pulls the integration branch first. If that fails (diverged / dirty / local-ahead), it prints the cause and fix commands and **does not** create a TASK file. If a previous run left a TASK file without a branch, rerun `task add` to resume.
114
+ `task add` fast-forward-pulls the integration branch first. If that fails (diverged / **app** dirty / local-ahead), it prints the cause and fix commands and **does not** create a TASK file. VibeOps/governance-only changes (e.g. `.vibeops.json` after `init`) do **not** block. If a previous run left a TASK file without a branch, rerun `task add` to resume.
115
115
 
116
116
  ## Status
117
117
 
@@ -1,4 +1,4 @@
1
- import { gitFetchRemote, gitLeftRightCount, gitPullFastForwardOnly, gitRemoteBranchExists, gitRemoteUrl, gitRevParse, gitSwitchToBranch, readGitInfo, } from "./git.js";
1
+ import { gitFetchRemote, gitGovernanceOnlyDirty, gitLeftRightCount, gitPullFastForwardOnly, gitRemoteBranchExists, gitRemoteUrl, gitRevParse, gitSwitchToBranch, readGitInfo, restoreGovernanceStashAfterSwitch, stashGovernanceIfBlocking, } from "./git.js";
2
2
  import { cyan, dim, log } from "./logger.js";
3
3
  function remoteRef(remote, branch) {
4
4
  return `${remote}/${branch}`;
@@ -6,6 +6,9 @@ function remoteRef(remote, branch) {
6
6
  /**
7
7
  * Classify why `git pull --ff-only` would fail for the integration branch.
8
8
  * Call after fetch when possible so `origin/branch` is current.
9
+ *
10
+ * Governance-only dirt (`.vibeops.json`, docs/, `.vibeops/`, …) does **not**
11
+ * block — `task add` right after `init` must work.
9
12
  */
10
13
  export async function diagnoseIntegrationSync(cwd, remote, integrationBranch) {
11
14
  if ((await gitRemoteUrl(cwd, remote)) === null) {
@@ -26,19 +29,28 @@ export async function diagnoseIntegrationSync(cwd, remote, integrationBranch) {
26
29
  }
27
30
  const git = await readGitInfo(cwd);
28
31
  if (git.dirty === true) {
29
- return {
30
- ok: false,
31
- kind: "dirty",
32
- summary: `Working tree is dirty — cannot fast-forward ${integrationBranch}.`,
33
- fixes: [
34
- "git status",
35
- "git stash push -u -m \"vibeops: before sync\"",
36
- `git checkout ${integrationBranch}`,
37
- `git pull --ff-only ${remote} ${integrationBranch}`,
38
- "git stash pop # if you stashed",
39
- "vibeops task add",
40
- ],
41
- };
32
+ const gov = await gitGovernanceOnlyDirty(cwd);
33
+ if (!gov.onlyGovernance) {
34
+ const blocking = gov.nonGovernancePaths.slice(0, 8);
35
+ const more = gov.nonGovernancePaths.length > blocking.length
36
+ ? ` (+${gov.nonGovernancePaths.length - blocking.length} more)`
37
+ : "";
38
+ return {
39
+ ok: false,
40
+ kind: "dirty",
41
+ summary: `Working tree has app changes — cannot fast-forward ${integrationBranch}.`,
42
+ fixes: [
43
+ "git status",
44
+ `# Blocking (non-governance): ${blocking.join(", ")}${more}`,
45
+ "git stash push -u -m \"vibeops: before sync\"",
46
+ `git checkout ${integrationBranch}`,
47
+ `git pull --ff-only ${remote} ${integrationBranch}`,
48
+ "git stash pop # if you stashed",
49
+ "vibeops task add",
50
+ ],
51
+ };
52
+ }
53
+ // Governance-only dirty (.vibeops.json after init, docs, …) — proceed.
42
54
  }
43
55
  const localSha = await gitRevParse(cwd, integrationBranch);
44
56
  const remoteSha = await gitRevParse(cwd, remoteRef(remote, integrationBranch));
@@ -129,6 +141,7 @@ export function printIntegrationSyncDiagnosis(d) {
129
141
  /**
130
142
  * Fetch + ensure local integration can be used as the base for a new task branch.
131
143
  * Switches to the integration branch, then `--ff-only` pull when needed. Never force-resets.
144
+ * Governance-only dirt is stashed around switch/pull so post-`init` `.vibeops.json` edits do not block.
132
145
  */
133
146
  export async function ensureIntegrationSynced(opts) {
134
147
  const { cwd, remote, integrationBranch } = opts;
@@ -184,6 +197,8 @@ export async function ensureIntegrationSynced(opts) {
184
197
  if (localSha !== null && remoteSha !== null && localSha === remoteSha) {
185
198
  return { ok: true, diagnosis, pulled: false };
186
199
  }
200
+ // Stash governance dirt so ff-only pull cannot be blocked by `.vibeops.json` etc.
201
+ const stashed = await stashGovernanceIfBlocking(cwd);
187
202
  try {
188
203
  await gitPullFastForwardOnly(cwd, remote, integrationBranch);
189
204
  diagnosis = {
@@ -211,4 +226,7 @@ export async function ensureIntegrationSynced(opts) {
211
226
  }
212
227
  return { ok: false, diagnosis, pulled: false };
213
228
  }
229
+ finally {
230
+ await restoreGovernanceStashAfterSwitch(cwd, stashed);
231
+ }
214
232
  }
package/dist/lib/git.js CHANGED
@@ -299,12 +299,17 @@ const GOVERNANCE_DOC_REL_PREFIXES = [
299
299
  "docs/project/",
300
300
  "docs/logs/",
301
301
  ".vibeops/",
302
+ ".cursor/rules/",
302
303
  ];
304
+ /** Root files owned by vibeops init / config (not under `.vibeops/`). */
305
+ const GOVERNANCE_DOC_EXACT = new Set([".vibeops.json", "AGENTS.md"]);
303
306
  function normalizeRepoRelPath(p) {
304
307
  return p.replace(/\\/g, "/").replace(/^\/+/, "");
305
308
  }
306
309
  export function isGovernanceDocumentationPath(repoRelativePath) {
307
310
  const n = normalizeRepoRelPath(repoRelativePath);
311
+ if (GOVERNANCE_DOC_EXACT.has(n))
312
+ return true;
308
313
  for (const prefix of GOVERNANCE_DOC_REL_PREFIXES) {
309
314
  if (n.startsWith(prefix))
310
315
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodtek/vibeops",
3
- "version": "2.5.1",
3
+ "version": "2.5.2",
4
4
  "description": "Minimal CLI rails for Cursor-based TASK-driven vibe coding.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@9.15.9",