@goodtek/vibeops 0.2.0 → 0.3.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.
- package/CHANGELOG.md +13 -0
- package/README.md +5 -4
- package/dist/cli.js +32 -5
- package/dist/commands/plan.js +242 -13
- package/dist/commands/task-start.js +16 -4
- package/dist/lib/brief.js +11 -4
- package/dist/lib/git.js +42 -0
- package/dist/lib/plan-apply-planner.js +131 -0
- package/dist/lib/plan-codex-auth.js +183 -0
- package/dist/lib/plan-codex-pkce.js +234 -0
- package/dist/lib/plan-codex-responses.js +228 -0
- package/dist/lib/plan-llm-cursor-agent.js +59 -0
- package/dist/lib/plan-llm-detect.js +74 -0
- package/dist/lib/plan-llm-models.js +237 -0
- package/dist/lib/plan-llm-normalize.js +146 -0
- package/dist/lib/plan-llm-openai.js +123 -0
- package/dist/lib/plan-llm-session.js +681 -0
- package/dist/lib/plan-llm-types.js +1 -0
- package/dist/lib/plan-post-apply-setup.js +116 -0
- package/dist/lib/prompt-builder.js +11 -1
- package/package.json +1 -1
- package/templates/.cursor/rules/03-git-safety.mdc +1 -1
- package/templates/.vibeops/workflows/task-lifecycle.md +1 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { githubInitCommand } from "../commands/github-init.js";
|
|
2
|
+
import { notionInitCommand } from "../commands/notion-init.js";
|
|
3
|
+
import { notionSyncCommand } from "../commands/notion-sync.js";
|
|
4
|
+
import { askYesNo } from "./inquirer-helpers.js";
|
|
5
|
+
import { cyan, dim, log } from "./logger.js";
|
|
6
|
+
import { gitInit, gitRemoteUrl, gitSetDefaultBranch, isGitRepository } from "./git.js";
|
|
7
|
+
import { loadSyncContext } from "./notion-sync.js";
|
|
8
|
+
const DEFAULT_BRANCH = "main";
|
|
9
|
+
/**
|
|
10
|
+
* Ensures `cwd` is a Git work tree before planner `git add` / `git commit`.
|
|
11
|
+
* @returns false when commit should be skipped
|
|
12
|
+
*/
|
|
13
|
+
export async function ensureGitForPlannerCommit(params) {
|
|
14
|
+
if (await isGitRepository(params.cwd)) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (!params.interactive) {
|
|
18
|
+
log.warn(`No Git repository in this folder — skipping commit. Run ${cyan("git init")} or ${cyan("vibeops init")} with Git enabled, then commit manually.`);
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const ok = await askYesNo({
|
|
22
|
+
message: `No Git repository here yet. Run ${cyan("git init")} and set default branch ${cyan(DEFAULT_BRANCH)} now?`,
|
|
23
|
+
nonInteractive: false,
|
|
24
|
+
defaultValue: true,
|
|
25
|
+
});
|
|
26
|
+
if (!ok) {
|
|
27
|
+
log.info(dim("Skipping git commit. Initialize Git when ready, then commit the planner files."));
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
await gitInit(params.cwd);
|
|
31
|
+
await gitSetDefaultBranch(params.cwd, DEFAULT_BRANCH);
|
|
32
|
+
log.ok(`Git initialized (default branch ${DEFAULT_BRANCH}).`);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Runs `notion sync`, or when context is missing and stdin is interactive,
|
|
37
|
+
* offers to run `vibeops notion init` first then retries sync once.
|
|
38
|
+
*/
|
|
39
|
+
export async function runNotionSyncWithOptionalInit(params) {
|
|
40
|
+
let ctx = await loadSyncContext(params.cwd);
|
|
41
|
+
if (ctx.ok) {
|
|
42
|
+
const priorFailure = process.exitCode === 1;
|
|
43
|
+
await notionSyncCommand({ cwd: params.cwd });
|
|
44
|
+
if (priorFailure) {
|
|
45
|
+
process.exitCode = 1;
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!params.interactive) {
|
|
50
|
+
log.warn(`Notion sync skipped: ${ctx.message}`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const setup = await askYesNo({
|
|
54
|
+
message: `Notion is not ready (${ctx.message}). Run ${cyan("vibeops notion init")} setup here now?`,
|
|
55
|
+
nonInteractive: false,
|
|
56
|
+
defaultValue: true,
|
|
57
|
+
});
|
|
58
|
+
if (!setup) {
|
|
59
|
+
log.info(dim("Skipping Notion sync. When ready, run `vibeops notion init` then `vibeops notion sync`."));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const priorFailure = process.exitCode === 1;
|
|
63
|
+
await notionInitCommand({ cwd: params.cwd, nonInteractive: false, dryRun: false });
|
|
64
|
+
if (process.exitCode === 1) {
|
|
65
|
+
log.warn("Notion setup exited with an error — sync skipped. Fix issues and run `vibeops notion sync`.");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
ctx = await loadSyncContext(params.cwd);
|
|
69
|
+
if (!ctx.ok) {
|
|
70
|
+
log.warn(`Notion still not ready after setup: ${ctx.message}`);
|
|
71
|
+
log.info(dim("Try `vibeops notion test` and re-run `vibeops notion sync`."));
|
|
72
|
+
if (priorFailure) {
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
await notionSyncCommand({ cwd: params.cwd });
|
|
78
|
+
if (priorFailure) {
|
|
79
|
+
process.exitCode = 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Before `git push`, optionally runs the same flow as `vibeops github init`
|
|
84
|
+
* (connect existing GitHub repo or create new, `gh auth`, `origin`, config).
|
|
85
|
+
*/
|
|
86
|
+
export async function offerGithubInitBeforePush(params) {
|
|
87
|
+
const originUrl = await gitRemoteUrl(params.cwd, "origin");
|
|
88
|
+
const hasOrigin = Boolean(originUrl && originUrl.trim().length > 0);
|
|
89
|
+
if (!params.interactive) {
|
|
90
|
+
if (!hasOrigin) {
|
|
91
|
+
log.warn(`No ${cyan("origin")} remote — ${cyan("git push")} may fail. Run ${cyan("vibeops github init")} or add a remote, then push again.`);
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const wants = await askYesNo({
|
|
96
|
+
message: hasOrigin
|
|
97
|
+
? `Run ${cyan("vibeops github init")} before push? (connect another repo, create a new GitHub repo, or verify remotes / ${cyan("gh auth")})`
|
|
98
|
+
: `No ${cyan("origin")} remote yet. Run ${cyan("vibeops github init")} now (connect an existing GitHub repo or create a new one)?`,
|
|
99
|
+
nonInteractive: false,
|
|
100
|
+
defaultValue: !hasOrigin,
|
|
101
|
+
});
|
|
102
|
+
if (!wants) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
log.blank();
|
|
106
|
+
log.step("GitHub / remote setup (same wizard as `vibeops github init`)");
|
|
107
|
+
const priorFailure = process.exitCode === 1;
|
|
108
|
+
await githubInitCommand({ cwd: params.cwd });
|
|
109
|
+
if (process.exitCode === 1) {
|
|
110
|
+
log.warn("GitHub setup exited with an error — push may still fail.");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (priorFailure) {
|
|
114
|
+
process.exitCode = 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -42,9 +42,19 @@ export function buildPlanPrompt(inputs) {
|
|
|
42
42
|
const assumptions = meta.assumptions.length > 0
|
|
43
43
|
? meta.assumptions.map((a) => `- ${a}`).join("\n")
|
|
44
44
|
: "- _(none recorded)_";
|
|
45
|
+
const viaLlm = meta.source === "llm-openai" ||
|
|
46
|
+
meta.source === "llm-codex-oauth" ||
|
|
47
|
+
meta.source === "llm-cursor-agent";
|
|
48
|
+
const opener = viaLlm
|
|
49
|
+
? meta.source === "llm-codex-oauth"
|
|
50
|
+
? "This file was produced by `vibeops plan` using **Codex (ChatGPT OAuth)** tokens from your Codex CLI session (`~/.codex/auth.json`). Open a new chat in **Cursor** and paste the full contents of this file. The Planner Agent receives this input and fills in `docs/project/*` plus the initial backlog."
|
|
51
|
+
: meta.source === "llm-openai"
|
|
52
|
+
? "This file was produced by `vibeops plan` using an **OpenAI platform API key**. Open a new chat in **Cursor** and paste the full contents of this file. The Planner Agent receives this input and fills in `docs/project/*` plus the initial backlog."
|
|
53
|
+
: "This file was produced by `vibeops plan` using the **Cursor Agent CLI**. Open a new chat in **Cursor** and paste the full contents of this file. The Planner Agent receives this input and fills in `docs/project/*` plus the initial backlog."
|
|
54
|
+
: "This file was produced by `vibeops plan`. Open a new chat in **Cursor** and paste the full contents of this file. VibeOps does not call LLMs for this path — the Planner Agent receives this input and fills in `docs/project/*` plus the initial backlog.";
|
|
45
55
|
return `# VibeOps Plan Prompt — Cursor Planner Agent
|
|
46
56
|
|
|
47
|
-
>
|
|
57
|
+
> ${opener}
|
|
48
58
|
|
|
49
59
|
- Brief location: \`${briefRelativePath}\`
|
|
50
60
|
- VibeOps version: ${meta.vibeopsVersion}
|
package/package.json
CHANGED
|
@@ -21,7 +21,7 @@ alwaysApply: true
|
|
|
21
21
|
- `vibeops task rollback` produces **guidance by default**. Destructive Git actions run only with `--confirm`.
|
|
22
22
|
- **No force-push** (especially on shared branches).
|
|
23
23
|
- No automatic merge, no automatic push. A human does both explicitly.
|
|
24
|
-
- `task start` refuses a dirty working tree (
|
|
24
|
+
- `task start` refuses a dirty working tree unless every changed path is under governance docs (`docs/tasks/`, `docs/project/`, `docs/logs/`, `.vibeops/state/`); then it proceeds with a warning. `--allow-dirty` overrides for any dirty path.
|
|
25
25
|
|
|
26
26
|
## Forbidden
|
|
27
27
|
|
|
@@ -12,7 +12,7 @@ Run `vibeops status` to identify the next TASK. Typically run `in_progress` item
|
|
|
12
12
|
vibeops task start TASK-NNN
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
- A dirty working tree is refused (
|
|
15
|
+
- A dirty working tree is refused unless changes are limited to governance docs (`docs/tasks/`, `docs/project/`, `docs/logs/`, `.vibeops/state/`); then `task start` warns and continues. Use `--allow-dirty` only when non-doc paths are dirty intentionally.
|
|
16
16
|
- Records base branch / base commit / task branch in `.vibeops/state/tasks/TASK-NNN.json`.
|
|
17
17
|
- Creates and checks out the `task/NNN-<slug>` branch.
|
|
18
18
|
- Sets the TASK file's Status to `in_progress`.
|