@goodtek/vibeops 2.1.1 → 2.1.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 +10 -4
- package/README.md +1 -1
- package/dist/cli.js +0 -0
- package/dist/commands/task-reship.js +10 -36
- package/dist/commands/task-ship.js +16 -56
- package/package.json +13 -11
package/CHANGELOG.md
CHANGED
|
@@ -4,14 +4,13 @@ All notable changes to VibeOps are documented here.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
-
## 2.1.
|
|
7
|
+
## 2.1.2 - 2026-06-08
|
|
8
8
|
|
|
9
9
|
### Changed
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
- **`task sync` CLI help:** description no longer says "mark Done".
|
|
11
|
+
- **`task ship`** / **`task reship`**: commit ship/reship metadata **before** push and MR/PR creation so the branch is pushed once (avoids duplicate `pull_request` CI runs on `opened` + `synchronize`).
|
|
13
12
|
|
|
14
|
-
## 2.1.
|
|
13
|
+
## 2.1.1 - 2026-06-08
|
|
15
14
|
|
|
16
15
|
### Added
|
|
17
16
|
|
|
@@ -19,6 +18,13 @@ All notable changes to VibeOps are documented here.
|
|
|
19
18
|
|
|
20
19
|
### Changed
|
|
21
20
|
|
|
21
|
+
- **Docs:** living markdown, templates, and Cursor rules aligned with 2.1 lifecycle (`task reship`, two-status model, sync does not edit TASK md).
|
|
22
|
+
- **`task sync` CLI help:** description no longer says "mark Done".
|
|
23
|
+
|
|
24
|
+
## 2.1.0 - 2026-06-03
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
22
28
|
- **TASK md status:** only **In Progress** → **Shipped** (`task ship`). Merge (CLI/UI) and **`task sync`** do not edit the TASK file.
|
|
23
29
|
- **`task sync`**: Git cleanup only (integration pull, delete `task/*` branches).
|
|
24
30
|
- **`task add`**: blocks only another **In Progress** TASK (not **Shipped**).
|
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ pnpm install && pnpm build && pnpm smoke
|
|
|
106
106
|
|
|
107
107
|
- **Init** records branch policy in `.vibeops.json` (`integrationBranch`, `productionBranch`, `host`).
|
|
108
108
|
- **`task add`**: `task/<slug>` from **integration** (e.g. `develop`).
|
|
109
|
-
- **`task ship`**: commit
|
|
109
|
+
- **`task ship`**: commit implementation + ship metadata (Status **Shipped**) → push once → MR/PR.
|
|
110
110
|
- **`task reship`**: Shipped TASK only; integrates `develop`; opens **new** PR (archives previous MR in Git Context).
|
|
111
111
|
- **`task merge`**: merge MR/PR into integration (CLI or host UI; TASK md unchanged).
|
|
112
112
|
- **`task sync`**: integration ff-only pull → delete `task/*` branches (TASK md unchanged).
|
package/dist/cli.js
CHANGED
|
File without changes
|
|
@@ -3,36 +3,21 @@ import { readText } from "../lib/filesystem.js";
|
|
|
3
3
|
import { bold, cyan, dim, green, log, yellow } from "../lib/logger.js";
|
|
4
4
|
import { projectPaths } from "../lib/paths.js";
|
|
5
5
|
import { relPath } from "../lib/task-context.js";
|
|
6
|
-
import { commitDirtyWorkingTree, docsCommitMessageFor, featCommitMessageFor,
|
|
6
|
+
import { commitDirtyWorkingTree, docsCommitMessageFor, featCommitMessageFor, } from "../lib/task-git-commit.js";
|
|
7
7
|
import { finishTaskWithPullRequest } from "../lib/task-push-pr.js";
|
|
8
8
|
import { applyTaskShipMemory, llmCompleteTaskReship, writeTaskResultSections, } from "../lib/task-ship-llm.js";
|
|
9
9
|
import { assertMergeRequestNotOpen, ensureTaskBranchForReship, integrateIntegrationIntoTaskBranch, loadGitConfigForReship, prepareGitContextForReship, refreshGitContextBaseCommit, resolveReshipTarget, } from "../lib/task-reship.js";
|
|
10
10
|
import { hasNonEmptySection, parseTaskFilename, readGitContext, upsertGitContext, } from "../lib/task.js";
|
|
11
11
|
import { listWorkingTreeRelPaths } from "../lib/git.js";
|
|
12
|
-
async function
|
|
12
|
+
async function commitReshipMetadata(opts) {
|
|
13
13
|
if (opts.dryRun) {
|
|
14
14
|
log.info(dim(" would commit reship metadata (Status stays Shipped)"));
|
|
15
|
-
|
|
16
|
-
log.info(dim(` would git push ${opts.push.remote} ${opts.push.branch} (reship metadata)`));
|
|
17
|
-
}
|
|
18
|
-
return true;
|
|
15
|
+
return;
|
|
19
16
|
}
|
|
20
17
|
const committed = await commitDirtyWorkingTree(opts.cwd, docsCommitMessageFor(opts.taskId, "reship metadata"), false);
|
|
21
18
|
if (!committed) {
|
|
22
19
|
log.info(dim("No reship metadata commit (Git Context already on disk)."));
|
|
23
20
|
}
|
|
24
|
-
if (opts.push === null)
|
|
25
|
-
return true;
|
|
26
|
-
try {
|
|
27
|
-
await pushBranch(opts.cwd, opts.push.remote, opts.push.branch, false);
|
|
28
|
-
log.ok(`Pushed reship metadata → ${opts.push.remote}/${opts.push.branch}`);
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
catch (e) {
|
|
32
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
33
|
-
log.error(`Reship metadata push failed: ${msg}`);
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
21
|
}
|
|
37
22
|
export async function taskReshipCommand(taskRef, options = {}) {
|
|
38
23
|
const cwd = resolve(options.cwd ?? process.cwd());
|
|
@@ -129,8 +114,7 @@ export async function taskReshipCommand(taskRef, options = {}) {
|
|
|
129
114
|
log.blank();
|
|
130
115
|
if (dryRun) {
|
|
131
116
|
log.info(bold("dry-run — would also:"));
|
|
132
|
-
log.info(" ·
|
|
133
|
-
log.info(" · feat commit + reship metadata push");
|
|
117
|
+
log.info(" · feat commit + reship metadata, then push once and open MR/PR (unless --no-pr)");
|
|
134
118
|
log.blank();
|
|
135
119
|
log.info(`Next: ${cyan("vibeops task merge")}, then optional ${cyan("task sync")}`);
|
|
136
120
|
return;
|
|
@@ -157,6 +141,12 @@ export async function taskReshipCommand(taskRef, options = {}) {
|
|
|
157
141
|
gitCtx = await refreshGitContextBaseCommit(taskFile, gitCtx, cwd);
|
|
158
142
|
gitCtx = await prepareGitContextForReship(taskFile, gitCtx);
|
|
159
143
|
await upsertGitContext(taskFile, gitCtx);
|
|
144
|
+
await commitReshipMetadata({
|
|
145
|
+
cwd,
|
|
146
|
+
taskFile,
|
|
147
|
+
taskId: parts.id,
|
|
148
|
+
dryRun: false,
|
|
149
|
+
});
|
|
160
150
|
const prResult = await finishTaskWithPullRequest({
|
|
161
151
|
cwd,
|
|
162
152
|
taskFile,
|
|
@@ -171,22 +161,6 @@ export async function taskReshipCommand(taskRef, options = {}) {
|
|
|
171
161
|
if (prResult.mergeRequestUrl) {
|
|
172
162
|
log.info(` ${dim("MR/PR")} ${prResult.mergeRequestUrl}`);
|
|
173
163
|
}
|
|
174
|
-
const gitCtxAfter = await readGitContext(taskFile);
|
|
175
|
-
let pushTarget = null;
|
|
176
|
-
if (gitCtxAfter !== null && gitCfg !== null) {
|
|
177
|
-
pushTarget = { remote: gitCfg.remote, branch: gitCtxAfter.taskBranch };
|
|
178
|
-
}
|
|
179
|
-
const pushed = await commitReshipMetadataAndPush({
|
|
180
|
-
cwd,
|
|
181
|
-
taskFile,
|
|
182
|
-
taskId: parts.id,
|
|
183
|
-
dryRun: false,
|
|
184
|
-
push: pushTarget,
|
|
185
|
-
});
|
|
186
|
-
if (!pushed) {
|
|
187
|
-
process.exitCode = 1;
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
164
|
log.blank();
|
|
191
165
|
log.info(`Next: ${cyan("vibeops task merge")} (or merge in the host UI), then ${cyan("vibeops task sync")}`);
|
|
192
166
|
}
|
|
@@ -1,42 +1,24 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { readText } from "../lib/filesystem.js";
|
|
3
|
-
import { requireGitConfig } from "../lib/git-config.js";
|
|
4
3
|
import { bold, cyan, dim, green, log, yellow } from "../lib/logger.js";
|
|
5
4
|
import { projectPaths } from "../lib/paths.js";
|
|
6
5
|
import { resolveTask, taskNotFoundMessage } from "../lib/resolve-task.js";
|
|
7
6
|
import { relPath } from "../lib/task-context.js";
|
|
8
|
-
import { commitDirtyWorkingTree, docsCommitMessageFor, featCommitMessageFor,
|
|
7
|
+
import { commitDirtyWorkingTree, docsCommitMessageFor, featCommitMessageFor, } from "../lib/task-git-commit.js";
|
|
9
8
|
import { applyTaskShipMemory, fallbackResultSections, llmCompleteTaskShip, writeTaskResultSections, } from "../lib/task-ship-llm.js";
|
|
10
9
|
import { finishTaskWithPullRequest } from "../lib/task-push-pr.js";
|
|
11
10
|
import { hasNonEmptySection, parseTaskFilename, readGitContext, readTaskFile, updateInlineStatus, } from "../lib/task.js";
|
|
12
|
-
async function
|
|
11
|
+
async function commitShipMetadata(opts) {
|
|
13
12
|
if (opts.dryRun) {
|
|
14
13
|
log.info(dim(" would set Status → Shipped"));
|
|
15
14
|
log.info(dim(" would commit ship metadata"));
|
|
16
|
-
|
|
17
|
-
log.info(dim(` would git push ${opts.push.remote} ${opts.push.branch} (ship metadata)`));
|
|
18
|
-
}
|
|
19
|
-
return true;
|
|
15
|
+
return;
|
|
20
16
|
}
|
|
21
17
|
await updateInlineStatus(opts.taskFile, "shipped");
|
|
22
18
|
log.ok("Status → Shipped");
|
|
23
19
|
const committed = await commitDirtyWorkingTree(opts.cwd, docsCommitMessageFor(opts.taskId, "mark shipped"), false);
|
|
24
20
|
if (!committed) {
|
|
25
21
|
log.warn("No file changes for ship metadata commit — check TASK Status on the remote branch.");
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
if (opts.push === null)
|
|
29
|
-
return true;
|
|
30
|
-
try {
|
|
31
|
-
await pushBranch(opts.cwd, opts.push.remote, opts.push.branch, false);
|
|
32
|
-
log.ok(`Pushed ship metadata → ${opts.push.remote}/${opts.push.branch}`);
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
catch (e) {
|
|
36
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
37
|
-
log.error(`Ship metadata push failed: ${msg}`);
|
|
38
|
-
log.info(dim(`Fix auth/remote, then: git push ${opts.push.remote} ${opts.push.branch}`));
|
|
39
|
-
return false;
|
|
40
22
|
}
|
|
41
23
|
}
|
|
42
24
|
export async function taskShipCommand(taskRef, options = {}) {
|
|
@@ -104,24 +86,20 @@ export async function taskShipCommand(taskRef, options = {}) {
|
|
|
104
86
|
if (!resultOk || !testOk) {
|
|
105
87
|
log.info(" · LLM fill Result / Test Result + patch docs/project/*");
|
|
106
88
|
}
|
|
107
|
-
log.info(" · commit implementation
|
|
108
|
-
log.info(" ·
|
|
109
|
-
|
|
89
|
+
log.info(" · commit implementation + ship metadata (Status → Shipped)");
|
|
90
|
+
log.info(" · push task branch once, open MR/PR (unless --no-pr)");
|
|
91
|
+
const partsDry = parseTaskFilename(taskFile);
|
|
92
|
+
await commitShipMetadata({
|
|
110
93
|
cwd,
|
|
111
94
|
taskFile,
|
|
95
|
+
taskId: partsDry.id,
|
|
112
96
|
dryRun: true,
|
|
113
|
-
skipPr: options.noPr === true,
|
|
114
97
|
});
|
|
115
|
-
|
|
116
|
-
const gitCfgDry = gitCtxDry !== null ? await requireGitConfig(cwd).catch(() => null) : null;
|
|
117
|
-
await commitShipMetadataAndPush({
|
|
98
|
+
await finishTaskWithPullRequest({
|
|
118
99
|
cwd,
|
|
119
100
|
taskFile,
|
|
120
|
-
taskId,
|
|
121
101
|
dryRun: true,
|
|
122
|
-
|
|
123
|
-
? { remote: gitCfgDry.remote, branch: gitCtxDry.taskBranch }
|
|
124
|
-
: null,
|
|
102
|
+
skipPr: options.noPr === true,
|
|
125
103
|
});
|
|
126
104
|
return;
|
|
127
105
|
}
|
|
@@ -129,6 +107,12 @@ export async function taskShipCommand(taskRef, options = {}) {
|
|
|
129
107
|
await commitDirtyWorkingTree(cwd, featCommitMessageFor(parts.id, meta.title), false);
|
|
130
108
|
const gitCtx = await readGitContext(taskFile);
|
|
131
109
|
if (gitCtx !== null) {
|
|
110
|
+
await commitShipMetadata({
|
|
111
|
+
cwd,
|
|
112
|
+
taskFile,
|
|
113
|
+
taskId: parts.id,
|
|
114
|
+
dryRun: false,
|
|
115
|
+
});
|
|
132
116
|
const prResult = await finishTaskWithPullRequest({
|
|
133
117
|
cwd,
|
|
134
118
|
taskFile,
|
|
@@ -146,30 +130,6 @@ export async function taskShipCommand(taskRef, options = {}) {
|
|
|
146
130
|
else {
|
|
147
131
|
log.info(dim("No Git Context — push/MR skipped."));
|
|
148
132
|
}
|
|
149
|
-
const gitCtxAfter = await readGitContext(taskFile);
|
|
150
|
-
let pushTarget = null;
|
|
151
|
-
if (gitCtxAfter !== null) {
|
|
152
|
-
try {
|
|
153
|
-
const gitCfg = await requireGitConfig(cwd);
|
|
154
|
-
pushTarget = { remote: gitCfg.remote, branch: gitCtxAfter.taskBranch };
|
|
155
|
-
}
|
|
156
|
-
catch (e) {
|
|
157
|
-
if (e instanceof Error)
|
|
158
|
-
log.warn(e.message);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
const shipped = await commitShipMetadataAndPush({
|
|
162
|
-
cwd,
|
|
163
|
-
taskFile,
|
|
164
|
-
taskId: parts.id,
|
|
165
|
-
dryRun: false,
|
|
166
|
-
push: pushTarget,
|
|
167
|
-
});
|
|
168
|
-
if (!shipped) {
|
|
169
|
-
log.error("Ship metadata push failed — fix manually, then merge the MR.");
|
|
170
|
-
process.exitCode = 1;
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
133
|
log.blank();
|
|
174
134
|
log.info(`Next: ${cyan("vibeops task merge")} (or merge in the host UI), then ${cyan("vibeops task sync")}`);
|
|
175
135
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodtek/vibeops",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "Minimal CLI rails for Cursor-based TASK-driven vibe coding.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@9.15.9",
|
|
6
7
|
"author": "VibeOps contributors",
|
|
7
8
|
"license": "MIT",
|
|
8
9
|
"repository": {
|
|
@@ -29,6 +30,16 @@
|
|
|
29
30
|
"engines": {
|
|
30
31
|
"node": ">=20"
|
|
31
32
|
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"dev": "tsx src/cli.ts",
|
|
35
|
+
"build": "tsc -p tsconfig.json",
|
|
36
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
37
|
+
"start": "node dist/cli.js",
|
|
38
|
+
"smoke": "pnpm typecheck && pnpm build && node scripts/smoke.mjs",
|
|
39
|
+
"clean": "rm -rf dist",
|
|
40
|
+
"prepack": "pnpm clean && pnpm build",
|
|
41
|
+
"publish:dry": "pnpm publish --dry-run --no-git-checks"
|
|
42
|
+
},
|
|
32
43
|
"keywords": [
|
|
33
44
|
"cursor",
|
|
34
45
|
"ai",
|
|
@@ -47,14 +58,5 @@
|
|
|
47
58
|
"@types/node": "^20.14.0",
|
|
48
59
|
"tsx": "^4.19.0",
|
|
49
60
|
"typescript": "^5.5.4"
|
|
50
|
-
},
|
|
51
|
-
"scripts": {
|
|
52
|
-
"dev": "tsx src/cli.ts",
|
|
53
|
-
"build": "tsc -p tsconfig.json",
|
|
54
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
55
|
-
"start": "node dist/cli.js",
|
|
56
|
-
"smoke": "pnpm typecheck && pnpm build && node scripts/smoke.mjs",
|
|
57
|
-
"clean": "rm -rf dist",
|
|
58
|
-
"publish:dry": "pnpm publish --dry-run --no-git-checks"
|
|
59
61
|
}
|
|
60
|
-
}
|
|
62
|
+
}
|