@goodtek/vibeops 1.0.0 → 1.1.1
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 +31 -0
- package/README.md +51 -60
- package/dist/bootstrap/installer.js +38 -4
- package/dist/bootstrap/manifest.js +23 -3
- package/dist/cli.js +64 -15
- package/dist/commands/init.js +279 -114
- package/dist/commands/llm.js +177 -0
- package/dist/commands/status.js +37 -1
- package/dist/commands/task-add.js +33 -6
- package/dist/commands/task-done.js +27 -31
- package/dist/lib/config.js +84 -4
- package/dist/lib/env.js +32 -0
- package/dist/lib/git-config.js +21 -0
- package/dist/lib/git-host.js +12 -0
- package/dist/lib/git-policy.js +67 -0
- package/dist/lib/git-remote.js +108 -0
- package/dist/lib/git.js +43 -0
- package/dist/lib/init-clients.js +55 -0
- package/dist/lib/llm-complete.js +18 -19
- package/dist/lib/llm-connect.js +150 -0
- package/dist/lib/llm-preference.js +102 -0
- package/dist/lib/llm-status.js +19 -0
- package/dist/lib/pr-create.js +66 -0
- package/dist/lib/task-add-llm.js +8 -1
- package/dist/lib/task-done-llm.js +8 -1
- package/dist/lib/task-pr-llm.js +50 -0
- package/dist/lib/task-push-pr.js +131 -0
- package/dist/lib/task-start.js +46 -18
- package/dist/lib/task.js +15 -0
- package/package.json +13 -11
- package/templates/.cursor/rules/02-git-safety.mdc +5 -4
- package/templates/AGENTS.md +2 -1
- package/templates/clients/claude/CLAUDE.md +28 -0
- package/templates/clients/cursor/.cursor/rules/02-git-safety.mdc +11 -0
- package/templates/core/AGENTS.md +44 -0
- package/templates/{.cursor/skills → skills-shared}/implement-task/SKILL.md +1 -1
- package/templates/{.cursor/skills → skills-shared}/plan-task/SKILL.md +3 -3
- package/dist/lib/task-merge.js +0 -115
- /package/templates/{.cursor → clients/cursor/.cursor}/rules/01-task-source-of-truth.mdc +0 -0
- /package/templates/{.cursor → clients/cursor/.cursor}/rules/03-docs-on-done.mdc +0 -0
- /package/templates/{docs → core/docs}/logs/README.md +0 -0
- /package/templates/{docs → core/docs}/project/03-architecture.md +0 -0
- /package/templates/{docs → core/docs}/project/05-current-state.md +0 -0
- /package/templates/{docs → core/docs}/project/06-decisions.md +0 -0
- /package/templates/{docs → core/docs}/tasks/TASK-000-template.md +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,8 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to VibeOps are documented here.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Removed dead `task-merge.ts` (local merge no longer used).
|
|
10
|
+
- `task add` checks out integration branch from `origin/<branch>` when only remote exists.
|
|
11
|
+
- `task done` sets Status **Done** only after push/MR succeed; skips duplicate PR when URL exists.
|
|
12
|
+
- Re-init creates missing integration branch on existing repos.
|
|
13
|
+
- `status` shows branch policy and MR/PR URL.
|
|
14
|
+
|
|
15
|
+
## 1.1.0 - 2026-05-27
|
|
16
|
+
|
|
17
|
+
### Breaking
|
|
18
|
+
|
|
19
|
+
- **`task done` no longer merges locally.** It pushes the task branch and opens a **pull/merge request** to the integration branch (`gh` / `glab`). Merge and deploy happen on the host (CI).
|
|
20
|
+
- **`init` requires a git remote** (`origin`) unless `--allow-no-remote` (smoke/CI).
|
|
21
|
+
- **`.vibeops.json` gains a `git` block** (integration/production branches, host). Re-run `vibeops init` on older projects.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **Branch policy at init:** GitFlow lite (`develop` + `main`) or trunk (`main` only); `--git-policy`, `--integration-branch`, `--production-branch`.
|
|
26
|
+
- **`task add`** branches from the configured **integration** branch, not arbitrary HEAD.
|
|
27
|
+
- **LLM-generated MR/PR** title and body on `task done`; URL stored in TASK **Git Context**.
|
|
28
|
+
- **`--no-pr`** on `task done` to push only.
|
|
29
|
+
|
|
5
30
|
## 1.0.0 - 2026-05-27
|
|
6
31
|
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **`vibeops init --clients`**: choose **cursor**, **claude**, **codex** (≥1 required). Installs core + per-client rules/skills (`CLAUDE.md`, `.agents/skills/`, etc.).
|
|
35
|
+
- **Re-init guard**: existing projects prompt before overwriting templates; `docs/tasks/TASK-*.md` are preserved. Use `--yes` for non-interactive re-init.
|
|
36
|
+
- **`vibeops llm`**: `connect`, `status`, `use` for task add/done LLM providers.
|
|
37
|
+
|
|
7
38
|
### Breaking
|
|
8
39
|
|
|
9
40
|
- **Four commands only:** `init`, `task add`, `task done`, `status`.
|
package/README.md
CHANGED
|
@@ -1,104 +1,95 @@
|
|
|
1
1
|
# VibeOps
|
|
2
2
|
|
|
3
|
-
> Minimal CLI rails for
|
|
3
|
+
> Minimal CLI rails for TASK-driven vibe coding (Cursor, Claude Code, Codex).
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@goodtek/vibeops)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
VibeOps bootstraps
|
|
8
|
+
VibeOps bootstraps an **agent-friendly repo**, starts numbered **TASK** files on Git branches, and closes them while updating **project memory** in Git. You plan and implement in your chosen agent; the CLI handles files, Git, and short LLM assists.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Commands
|
|
11
11
|
|
|
12
12
|
| Command | Purpose |
|
|
13
13
|
|---------|---------|
|
|
14
|
-
| `vibeops init` |
|
|
15
|
-
| `vibeops task add` | New `TASK-NNN` file + task branch
|
|
16
|
-
| `vibeops task done` | LLM
|
|
17
|
-
| `vibeops status` | Briefing:
|
|
14
|
+
| `vibeops init` | Core docs + agent packs (cursor / claude / codex) |
|
|
15
|
+
| `vibeops task add` | New `TASK-NNN` file + task branch |
|
|
16
|
+
| `vibeops task done` | LLM summary, push branch, open MR/PR (no local merge) |
|
|
17
|
+
| `vibeops status` | Briefing: TASK, Git, LLM, clients |
|
|
18
|
+
| `vibeops llm` | Connect LLM providers (`connect` · `status` · `use`) |
|
|
18
19
|
|
|
19
|
-
##
|
|
20
|
+
## Init
|
|
20
21
|
|
|
21
22
|
```bash
|
|
22
|
-
|
|
23
|
+
# Interactive: pick agents (≥1), then Git
|
|
24
|
+
vibeops init
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# Build in Cursor Agent: same file + Scope / Acceptance Criteria
|
|
26
|
+
# Non-interactive (GitFlow: develop + main, origin required unless CI)
|
|
27
|
+
vibeops init --clients cursor,claude,codex --git --initial-commit --git-policy gitflow
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
vibeops
|
|
29
|
+
# Re-init templates on an existing project
|
|
30
|
+
vibeops init --clients cursor --yes
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
| Pack | Installed |
|
|
34
|
+
|------|-----------|
|
|
35
|
+
| **core** (always) | `AGENTS.md`, `docs/tasks`, `docs/project`, `docs/logs` |
|
|
36
|
+
| **cursor** | `.cursor/rules/`, `.cursor/skills/` |
|
|
37
|
+
| **claude** | `CLAUDE.md`, `.claude/skills/` |
|
|
38
|
+
| **codex** | `.agents/skills/` |
|
|
39
|
+
|
|
40
|
+
Re-init **overwrites** templates (rules, skills, doc stubs). **`docs/tasks/TASK-*.md` are kept.**
|
|
33
41
|
|
|
34
|
-
##
|
|
42
|
+
## Workflow
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
| `AGENTS.md` | Source of truth, read order, four CLI commands |
|
|
39
|
-
| `.cursor/rules/` | Task source of truth, Git safety, docs on done |
|
|
40
|
-
| `.cursor/skills/` | Optional `plan-task` and `implement-task` skills |
|
|
41
|
-
| `docs/tasks/TASK-000-template.md` | Section template for new TASKs |
|
|
42
|
-
| `docs/project/05-current-state.md` | Facts: stage, active TASK, next step |
|
|
43
|
-
| `docs/project/06-decisions.md` | Append-only decision log |
|
|
44
|
-
| `docs/project/03-architecture.md` | Living architecture |
|
|
45
|
-
| `docs/logs/README.md` | Daily log convention |
|
|
46
|
-
| `.vibeops.json` | Project metadata (no Notion) |
|
|
44
|
+
```bash
|
|
45
|
+
vibeops init --clients cursor --git --initial-commit
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
vibeops task add
|
|
48
|
+
# Plan / build using your agent and the current TASK file
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
| Project memory | `docs/project/05-current-state.md`, `03-architecture.md`, `06-decisions.md` |
|
|
54
|
-
| Change history | Git commits on task branches |
|
|
55
|
-
| **Not** trusted | Cursor chat, Slack |
|
|
50
|
+
vibeops task done
|
|
51
|
+
vibeops status
|
|
52
|
+
```
|
|
56
53
|
|
|
57
|
-
|
|
54
|
+
If a TASK is **In Progress**, `task add` exits with a guide — it does not auto-run `task done`.
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
## LLM (optional)
|
|
60
57
|
|
|
61
|
-
|
|
58
|
+
For **`task add`** / **`task done`** only (not for coding in the IDE):
|
|
62
59
|
|
|
63
|
-
|
|
60
|
+
```bash
|
|
61
|
+
vibeops llm connect
|
|
62
|
+
vibeops llm use auto # auto | codex-oauth | cursor-agent | openai
|
|
63
|
+
```
|
|
64
64
|
|
|
65
65
|
## Installation
|
|
66
66
|
|
|
67
|
-
Node.js 20
|
|
67
|
+
Node.js 20+.
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
70
|
npm install -g @goodtek/vibeops
|
|
71
|
-
vibeops --help
|
|
72
71
|
```
|
|
73
72
|
|
|
74
|
-
|
|
73
|
+
Development:
|
|
75
74
|
|
|
76
75
|
```bash
|
|
77
|
-
pnpm install
|
|
78
|
-
pnpm build
|
|
79
|
-
pnpm smoke # typecheck + build + smoke cases
|
|
80
|
-
node dist/cli.js --help
|
|
76
|
+
pnpm install && pnpm build && pnpm smoke
|
|
81
77
|
```
|
|
82
78
|
|
|
83
79
|
## Flags (common)
|
|
84
80
|
|
|
85
|
-
- **`init`**: `--dry-run`, `--
|
|
86
|
-
- **`task add`**: `--dry-run`, `--non-interactive --idea "…"`
|
|
87
|
-
- **`task done`**: `--dry-run`, `--no-
|
|
88
|
-
- **`status`**: `--json
|
|
89
|
-
|
|
90
|
-
## Git conventions
|
|
91
|
-
|
|
92
|
-
- Task branch: `task/<slug>` from current HEAD
|
|
93
|
-
- Commit message: `feat(task-nnn): <short title>`
|
|
94
|
-
- Merge via `task done` (confirm in TTY; `--yes` to skip prompt)
|
|
95
|
-
- No force-push
|
|
96
|
-
|
|
97
|
-
## Breaking change: 1.0.0
|
|
81
|
+
- **`init`**: `--clients`, `--yes`, `--dry-run`, `--force`, `--git`, `--initial-commit`, `--git-policy gitflow|trunk`, `--integration-branch`, `--production-branch`, `--allow-no-remote`, `--cwd`
|
|
82
|
+
- **`task add`**: `--dry-run`, `--non-interactive --idea "…"`
|
|
83
|
+
- **`task done`**: `--dry-run`, `--no-pr` (push only)
|
|
84
|
+
- **`status`**: `--json`
|
|
98
85
|
|
|
99
|
-
|
|
86
|
+
## Git
|
|
100
87
|
|
|
101
|
-
|
|
88
|
+
- **Init** records branch policy in `.vibeops.json` (`integrationBranch`, `productionBranch`, `host`).
|
|
89
|
+
- **`task add`**: branch `task/<slug>` from the **integration** branch (e.g. `develop`).
|
|
90
|
+
- **`task done`**: commit if needed → `git push` → `gh pr create` / `glab mr create` (LLM title/body). **You merge on GitHub/GitLab**; CI deploys.
|
|
91
|
+
- Commit messages: `feat(task-nnn): …`
|
|
92
|
+
- No force-push to shared branches
|
|
102
93
|
|
|
103
94
|
## License
|
|
104
95
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
1
2
|
import { join } from "node:path";
|
|
2
|
-
import { ensureDir, pathExists, readText, readTextOrNull, writeText } from "../lib/filesystem.js";
|
|
3
|
+
import { ensureDir, pathExists, readText, readTextOrNull, writeText, } from "../lib/filesystem.js";
|
|
4
|
+
import { CLIENT_SKILL_ROOT } from "../lib/init-clients.js";
|
|
3
5
|
import { dim, green, log, yellow } from "../lib/logger.js";
|
|
4
|
-
import { projectPaths, VIBEOPS_ENV_FILE } from "../lib/paths.js";
|
|
5
|
-
import {
|
|
6
|
+
import { projectPaths, TEMPLATES_ROOT, VIBEOPS_ENV_FILE } from "../lib/paths.js";
|
|
7
|
+
import { loadInstallManifest, resolveDestination } from "./manifest.js";
|
|
6
8
|
import { applySubstitutions, buildSubstitutions, isTextPath, } from "./substitute.js";
|
|
7
9
|
import { configToJson } from "../lib/config.js";
|
|
8
10
|
function envExampleContents() {
|
|
@@ -74,19 +76,51 @@ async function ensureGitignoreEntry(projectRoot, dryRun) {
|
|
|
74
76
|
relativePath: ".gitignore",
|
|
75
77
|
};
|
|
76
78
|
}
|
|
79
|
+
async function listSharedSkillNames() {
|
|
80
|
+
const sharedRoot = join(TEMPLATES_ROOT, "skills-shared");
|
|
81
|
+
if (!(await pathExists(sharedRoot)))
|
|
82
|
+
return [];
|
|
83
|
+
const entries = await readdir(sharedRoot, { withFileTypes: true });
|
|
84
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
85
|
+
}
|
|
86
|
+
async function installSharedSkills(options, subs) {
|
|
87
|
+
const outcomes = [];
|
|
88
|
+
const skillNames = await listSharedSkillNames();
|
|
89
|
+
const sharedRoot = join(TEMPLATES_ROOT, "skills-shared");
|
|
90
|
+
for (const client of options.clients) {
|
|
91
|
+
const skillRoot = CLIENT_SKILL_ROOT[client];
|
|
92
|
+
for (const skillName of skillNames) {
|
|
93
|
+
const sourceAbs = join(sharedRoot, skillName, "SKILL.md");
|
|
94
|
+
if (!(await pathExists(sourceAbs)))
|
|
95
|
+
continue;
|
|
96
|
+
const relativePath = join(skillRoot, skillName, "SKILL.md");
|
|
97
|
+
const dest = join(options.projectRoot, relativePath);
|
|
98
|
+
const entry = { relativePath, sourceAbs };
|
|
99
|
+
if (!options.dryRun) {
|
|
100
|
+
await ensureDir(join(dest, ".."));
|
|
101
|
+
}
|
|
102
|
+
outcomes.push(await copyOne(entry, dest, subs, options));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return outcomes;
|
|
106
|
+
}
|
|
77
107
|
export async function install(options) {
|
|
78
108
|
const paths = projectPaths(options.projectRoot);
|
|
79
109
|
const subs = buildSubstitutions(options.config);
|
|
80
110
|
if (!options.dryRun) {
|
|
81
111
|
await ensureDir(paths.root);
|
|
82
112
|
}
|
|
83
|
-
const manifest = await
|
|
113
|
+
const manifest = await loadInstallManifest(options.clients);
|
|
84
114
|
const outcomes = [];
|
|
85
115
|
for (const entry of manifest) {
|
|
86
116
|
const dest = resolveDestination(entry, paths.root);
|
|
117
|
+
if (!options.dryRun) {
|
|
118
|
+
await ensureDir(join(dest, ".."));
|
|
119
|
+
}
|
|
87
120
|
const outcome = await copyOne(entry, dest, subs, options);
|
|
88
121
|
outcomes.push(outcome);
|
|
89
122
|
}
|
|
123
|
+
outcomes.push(...(await installSharedSkills(options, subs)));
|
|
90
124
|
const configOutcome = await writeBlobIfNeeded(paths.config, ".vibeops.json", configToJson(options.config), options);
|
|
91
125
|
outcomes.push(configOutcome);
|
|
92
126
|
const envExampleOutcome = await writeBlobIfNeeded(paths.envExample, ".vibeops.env.example", envExampleContents(), options);
|
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import { join, relative } from "node:path";
|
|
2
2
|
import { walk } from "../lib/filesystem.js";
|
|
3
|
+
import { pathExists } from "../lib/filesystem.js";
|
|
3
4
|
import { TEMPLATES_ROOT } from "../lib/paths.js";
|
|
5
|
+
async function loadManifestFromDirectory(rootDir) {
|
|
6
|
+
if (!(await pathExists(rootDir)))
|
|
7
|
+
return [];
|
|
8
|
+
const files = await walk(rootDir);
|
|
9
|
+
return files.map((abs) => ({
|
|
10
|
+
relativePath: relative(rootDir, abs),
|
|
11
|
+
sourceAbs: abs,
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
/** Core tree + selected client packs (not shared skills — those are copied separately). */
|
|
15
|
+
export async function loadInstallManifest(clients) {
|
|
16
|
+
const entries = [];
|
|
17
|
+
entries.push(...(await loadManifestFromDirectory(join(TEMPLATES_ROOT, "core"))));
|
|
18
|
+
for (const id of clients) {
|
|
19
|
+
entries.push(...(await loadManifestFromDirectory(join(TEMPLATES_ROOT, "clients", id))));
|
|
20
|
+
}
|
|
21
|
+
return entries.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
22
|
+
}
|
|
23
|
+
export function resolveDestination(entry, projectRoot) {
|
|
24
|
+
return join(projectRoot, entry.relativePath);
|
|
25
|
+
}
|
|
26
|
+
/** @deprecated Use loadInstallManifest — walks entire templates/ (legacy). */
|
|
4
27
|
export async function loadManifest() {
|
|
5
28
|
const files = await walk(TEMPLATES_ROOT);
|
|
6
29
|
return files
|
|
@@ -10,6 +33,3 @@ export async function loadManifest() {
|
|
|
10
33
|
}))
|
|
11
34
|
.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
12
35
|
}
|
|
13
|
-
export function resolveDestination(entry, projectRoot) {
|
|
14
|
-
return join(projectRoot, entry.relativePath);
|
|
15
|
-
}
|
package/dist/cli.js
CHANGED
|
@@ -1,30 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { initCommand } from "./commands/init.js";
|
|
4
|
+
import { llmConnectCommand, llmStatusCommand, llmUseCommand } from "./commands/llm.js";
|
|
4
5
|
import { statusCommand } from "./commands/status.js";
|
|
5
6
|
import { taskAddCommand } from "./commands/task-add.js";
|
|
6
7
|
import { taskDoneCommand } from "./commands/task-done.js";
|
|
8
|
+
import { loadVibeopsEnv } from "./lib/env.js";
|
|
7
9
|
import { VERSION } from "./version.js";
|
|
8
10
|
const program = new Command();
|
|
9
11
|
program
|
|
10
12
|
.name("vibeops")
|
|
11
|
-
.description("VibeOps v3 — Cursor-friendly TASK workflow (init · task
|
|
13
|
+
.description("VibeOps v3 — Cursor-friendly TASK workflow (init · task · status · llm)")
|
|
12
14
|
.version(VERSION, "-v, --version", "Print the VibeOps version");
|
|
15
|
+
program.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
16
|
+
const opts = actionCommand.opts();
|
|
17
|
+
const cwd = typeof opts.cwd === "string" ? opts.cwd : process.cwd();
|
|
18
|
+
await loadVibeopsEnv(cwd);
|
|
19
|
+
});
|
|
13
20
|
program
|
|
14
21
|
.command("init")
|
|
15
|
-
.description("Install
|
|
22
|
+
.description("Install core + agent client packs (cursor, claude, codex)")
|
|
16
23
|
.option("--dry-run", "Show what would be created")
|
|
17
|
-
.option("--force", "Overwrite existing files")
|
|
24
|
+
.option("--force", "Overwrite existing template files (also implied when re-initializing with --yes)")
|
|
25
|
+
.option("--yes", "Re-init an existing project without prompting")
|
|
18
26
|
.option("--cwd <path>", "Target directory")
|
|
19
27
|
.option("--name <projectName>", "Project name in .vibeops.json")
|
|
20
|
-
.option("--
|
|
21
|
-
.option("--
|
|
28
|
+
.option("--clients <list>", "Agent packs: comma-separated cursor, claude, codex (required in non-interactive mode)")
|
|
29
|
+
.option("--git", "Initialize Git (always on; flag kept for scripts)")
|
|
22
30
|
.option("--initial-commit", "Create initial commit")
|
|
23
31
|
.option("--no-initial-commit", "Skip initial commit")
|
|
24
|
-
.option("--default-branch <name>", "Default branch (default main)")
|
|
25
32
|
.option("--commit-message <message>", "Initial commit message")
|
|
33
|
+
.option("--git-policy <preset>", "Branch policy: gitflow (develop+main) or trunk (main only)")
|
|
34
|
+
.option("--integration-branch <name>", "Integration branch (with --production-branch)")
|
|
35
|
+
.option("--production-branch <name>", "Production branch")
|
|
36
|
+
.option("--git-host <host>", "github or gitlab (default: detect from remote)")
|
|
37
|
+
.option("--allow-no-remote", "Smoke/CI: skip origin requirement")
|
|
26
38
|
.action(async (opts) => {
|
|
27
|
-
await initCommand(
|
|
39
|
+
await initCommand({
|
|
40
|
+
dryRun: Boolean(opts.dryRun),
|
|
41
|
+
force: Boolean(opts.force),
|
|
42
|
+
yes: Boolean(opts.yes),
|
|
43
|
+
cwd: opts.cwd,
|
|
44
|
+
name: opts.name,
|
|
45
|
+
clients: opts.clients,
|
|
46
|
+
git: opts.git,
|
|
47
|
+
initialCommit: opts.initialCommit,
|
|
48
|
+
commitMessage: opts.commitMessage,
|
|
49
|
+
allowNoRemote: Boolean(opts.allowNoRemote),
|
|
50
|
+
gitPolicy: opts.gitPolicy,
|
|
51
|
+
integrationBranch: opts.integrationBranch,
|
|
52
|
+
productionBranch: opts.productionBranch,
|
|
53
|
+
gitHost: opts.gitHost,
|
|
54
|
+
});
|
|
28
55
|
});
|
|
29
56
|
const task = program.command("task").description("TASK lifecycle");
|
|
30
57
|
task
|
|
@@ -44,24 +71,20 @@ task
|
|
|
44
71
|
});
|
|
45
72
|
task
|
|
46
73
|
.command("done [taskRef]")
|
|
47
|
-
.description("Close TASK: LLM summary,
|
|
74
|
+
.description("Close TASK: LLM summary, push branch, open MR/PR (no local merge)")
|
|
48
75
|
.option("--dry-run", "Plan only")
|
|
49
|
-
.option("--no-
|
|
50
|
-
.option("--allow-dirty", "Allow dirty tree during merge")
|
|
51
|
-
.option("--yes", "Merge without prompting")
|
|
76
|
+
.option("--no-pr", "Push only; skip creating MR/PR")
|
|
52
77
|
.option("--cwd <path>", "Target directory")
|
|
53
78
|
.action(async (taskRef, opts) => {
|
|
54
79
|
await taskDoneCommand(taskRef, {
|
|
55
80
|
dryRun: Boolean(opts.dryRun),
|
|
56
|
-
|
|
57
|
-
allowDirty: Boolean(opts.allowDirty),
|
|
58
|
-
yes: Boolean(opts.yes),
|
|
81
|
+
noPr: Boolean(opts.noPr),
|
|
59
82
|
cwd: opts.cwd,
|
|
60
83
|
});
|
|
61
84
|
});
|
|
62
85
|
program
|
|
63
86
|
.command("status")
|
|
64
|
-
.description("Briefing: active TASK, Git, next hint")
|
|
87
|
+
.description("Briefing: active TASK, Git, LLM, next hint")
|
|
65
88
|
.option("--json", "JSON output")
|
|
66
89
|
.option("--cwd <path>", "Target directory")
|
|
67
90
|
.action(async (opts) => {
|
|
@@ -70,6 +93,32 @@ program
|
|
|
70
93
|
cwd: opts.cwd,
|
|
71
94
|
});
|
|
72
95
|
});
|
|
96
|
+
const llm = program.command("llm").description("LLM providers for task add / task done");
|
|
97
|
+
llm
|
|
98
|
+
.command("connect")
|
|
99
|
+
.description("Interactive menu: connect providers and choose default")
|
|
100
|
+
.option("--cwd <path>", "Target directory")
|
|
101
|
+
.action(async (opts) => {
|
|
102
|
+
await llmConnectCommand({ cwd: opts.cwd });
|
|
103
|
+
});
|
|
104
|
+
llm
|
|
105
|
+
.command("status")
|
|
106
|
+
.description("Show connected providers and project preference")
|
|
107
|
+
.option("--json", "JSON output")
|
|
108
|
+
.option("--cwd <path>", "Target directory")
|
|
109
|
+
.action(async (opts) => {
|
|
110
|
+
await llmStatusCommand({
|
|
111
|
+
json: Boolean(opts.json),
|
|
112
|
+
cwd: opts.cwd,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
llm
|
|
116
|
+
.command("use [provider]")
|
|
117
|
+
.description("Set default provider: auto | codex-oauth | cursor-agent | openai")
|
|
118
|
+
.option("--cwd <path>", "Target directory")
|
|
119
|
+
.action(async (provider, opts) => {
|
|
120
|
+
await llmUseCommand(provider, { cwd: opts.cwd });
|
|
121
|
+
});
|
|
73
122
|
program.parseAsync(process.argv).catch((err) => {
|
|
74
123
|
console.error("[vibeops] error:", err);
|
|
75
124
|
process.exitCode = 1;
|