@goodtek/vibeops 0.7.22 → 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.
Files changed (144) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +51 -380
  3. package/dist/bootstrap/installer.js +42 -20
  4. package/dist/bootstrap/manifest.js +23 -3
  5. package/dist/cli.js +69 -154
  6. package/dist/commands/init.js +279 -114
  7. package/dist/commands/llm.js +177 -0
  8. package/dist/commands/status.js +181 -8
  9. package/dist/commands/task-add.js +89 -162
  10. package/dist/commands/task-done.js +112 -73
  11. package/dist/lib/config.js +77 -113
  12. package/dist/lib/env.js +32 -0
  13. package/dist/lib/git-config.js +21 -0
  14. package/dist/lib/git-host.js +12 -0
  15. package/dist/lib/git-policy.js +67 -0
  16. package/dist/lib/git-remote.js +108 -0
  17. package/dist/lib/git.js +44 -1
  18. package/dist/lib/init-clients.js +55 -0
  19. package/dist/lib/llm-complete.js +18 -19
  20. package/dist/lib/llm-connect.js +150 -0
  21. package/dist/lib/llm-preference.js +102 -0
  22. package/dist/lib/llm-status.js +19 -0
  23. package/dist/lib/paths.js +7 -3
  24. package/dist/lib/plan-llm-openai.js +0 -63
  25. package/dist/lib/pr-create.js +66 -0
  26. package/dist/lib/resolve-task.js +34 -0
  27. package/dist/lib/task-add-llm.js +53 -233
  28. package/dist/lib/task-context.js +68 -0
  29. package/dist/lib/task-done-llm.js +155 -0
  30. package/dist/lib/task-pr-llm.js +50 -0
  31. package/dist/lib/task-push-pr.js +131 -0
  32. package/dist/lib/task-scaffold.js +39 -43
  33. package/dist/lib/task-start.js +100 -0
  34. package/dist/lib/task-summary.js +12 -239
  35. package/dist/lib/task.js +30 -24
  36. package/dist/types/config.js +0 -16
  37. package/package.json +14 -14
  38. package/templates/.cursor/rules/02-git-safety.mdc +12 -0
  39. package/templates/AGENTS.md +27 -87
  40. package/templates/clients/claude/CLAUDE.md +28 -0
  41. package/templates/clients/cursor/.cursor/rules/01-task-source-of-truth.mdc +12 -0
  42. package/templates/clients/cursor/.cursor/rules/02-git-safety.mdc +11 -0
  43. package/templates/clients/cursor/.cursor/rules/03-docs-on-done.mdc +13 -0
  44. package/templates/core/AGENTS.md +44 -0
  45. package/templates/core/docs/logs/README.md +7 -0
  46. package/templates/core/docs/project/03-architecture.md +15 -0
  47. package/templates/core/docs/project/05-current-state.md +15 -0
  48. package/templates/core/docs/project/06-decisions.md +9 -0
  49. package/templates/core/docs/tasks/TASK-000-template.md +37 -0
  50. package/templates/skills-shared/implement-task/SKILL.md +22 -0
  51. package/templates/skills-shared/plan-task/SKILL.md +19 -0
  52. package/dist/agent/loader.js +0 -71
  53. package/dist/agent/prompt.js +0 -66
  54. package/dist/commands/agent-list.js +0 -32
  55. package/dist/commands/agent-prompt.js +0 -59
  56. package/dist/commands/agent-show.js +0 -26
  57. package/dist/commands/done.js +0 -160
  58. package/dist/commands/github-init.js +0 -554
  59. package/dist/commands/github-status.js +0 -164
  60. package/dist/commands/next.js +0 -283
  61. package/dist/commands/notion-init.js +0 -764
  62. package/dist/commands/notion-sync.js +0 -405
  63. package/dist/commands/notion-test.js +0 -595
  64. package/dist/commands/plan.js +0 -146
  65. package/dist/commands/rollback.js +0 -126
  66. package/dist/commands/start.js +0 -129
  67. package/dist/commands/task-check.js +0 -155
  68. package/dist/commands/task-generate.js +0 -206
  69. package/dist/commands/task-pull.js +0 -277
  70. package/dist/commands/task-rollback.js +0 -174
  71. package/dist/commands/task-start.js +0 -102
  72. package/dist/commands/task-status.js +0 -122
  73. package/dist/lib/brief.js +0 -356
  74. package/dist/lib/cursor-task-prompt.js +0 -60
  75. package/dist/lib/done-follow-up.js +0 -308
  76. package/dist/lib/github-cli.js +0 -247
  77. package/dist/lib/guide-state.js +0 -69
  78. package/dist/lib/mvp-artifacts.js +0 -204
  79. package/dist/lib/mvp-constants.js +0 -15
  80. package/dist/lib/mvp-diff-summary.js +0 -43
  81. package/dist/lib/mvp-done-summary.js +0 -335
  82. package/dist/lib/notion-client.js +0 -459
  83. package/dist/lib/notion-discovery.js +0 -671
  84. package/dist/lib/notion-env.js +0 -140
  85. package/dist/lib/notion-mappers.js +0 -148
  86. package/dist/lib/notion-schema.js +0 -272
  87. package/dist/lib/notion-sync.js +0 -337
  88. package/dist/lib/notion-target.js +0 -247
  89. package/dist/lib/package-json.js +0 -133
  90. package/dist/lib/plan-apply-planner.js +0 -131
  91. package/dist/lib/plan-codex-pkce.js +0 -234
  92. package/dist/lib/plan-llm-models.js +0 -237
  93. package/dist/lib/plan-llm-normalize.js +0 -146
  94. package/dist/lib/plan-llm-session.js +0 -682
  95. package/dist/lib/plan-post-apply-setup.js +0 -116
  96. package/dist/lib/project-docs.js +0 -95
  97. package/dist/lib/prompt-builder.js +0 -135
  98. package/dist/lib/resolve-command-task.js +0 -40
  99. package/dist/lib/task-add-build-prompt.js +0 -39
  100. package/dist/lib/task-add-cursor.js +0 -81
  101. package/dist/lib/task-generator.js +0 -183
  102. package/dist/lib/task-merge.js +0 -220
  103. package/dist/lib/task-prompt.js +0 -23
  104. package/dist/lib/task-pull.js +0 -354
  105. package/dist/lib/task-status-brief.js +0 -132
  106. package/dist/lib/workflow-guide.js +0 -485
  107. package/dist/status/collector.js +0 -103
  108. package/dist/status/format.js +0 -181
  109. package/dist/types/brief.js +0 -126
  110. package/templates/.cursor/rules/00-project-governance.mdc +0 -28
  111. package/templates/.cursor/rules/01-agent-orchestration.mdc +0 -48
  112. package/templates/.cursor/rules/02-task-workflow.mdc +0 -38
  113. package/templates/.cursor/rules/03-git-safety.mdc +0 -30
  114. package/templates/.cursor/rules/04-docs-update.mdc +0 -22
  115. package/templates/.vibeops/agents/architect.md +0 -47
  116. package/templates/.vibeops/agents/builder.md +0 -38
  117. package/templates/.vibeops/agents/docs.md +0 -54
  118. package/templates/.vibeops/agents/orchestrator.md +0 -40
  119. package/templates/.vibeops/agents/planner.md +0 -60
  120. package/templates/.vibeops/agents/recovery.md +0 -49
  121. package/templates/.vibeops/agents/reviewer.md +0 -47
  122. package/templates/.vibeops/agents/tester.md +0 -43
  123. package/templates/.vibeops/prompts/create-plan.md +0 -33
  124. package/templates/.vibeops/prompts/generate-tasks.md +0 -41
  125. package/templates/.vibeops/prompts/implement-task.md +0 -39
  126. package/templates/.vibeops/prompts/review-task.md +0 -34
  127. package/templates/.vibeops/prompts/rollback.md +0 -32
  128. package/templates/.vibeops/prompts/start-project.md +0 -39
  129. package/templates/.vibeops/workflows/notion-sync.md +0 -53
  130. package/templates/.vibeops/workflows/project-start.md +0 -73
  131. package/templates/.vibeops/workflows/rollback.md +0 -45
  132. package/templates/.vibeops/workflows/task-lifecycle.md +0 -71
  133. package/templates/docs/logs/README.md +0 -38
  134. package/templates/docs/project/00-overview.md +0 -27
  135. package/templates/docs/project/01-requirements.md +0 -30
  136. package/templates/docs/project/02-mvp-scope.md +0 -36
  137. package/templates/docs/project/03-architecture.md +0 -34
  138. package/templates/docs/project/04-tech-stack.md +0 -29
  139. package/templates/docs/project/05-current-state.md +0 -35
  140. package/templates/docs/project/06-decisions.md +0 -20
  141. package/templates/docs/project/07-backlog.md +0 -23
  142. package/templates/docs/project/08-env.md +0 -29
  143. package/templates/docs/project/09-deployment.md +0 -28
  144. package/templates/docs/tasks/TASK-000-template.md +0 -72
@@ -1,116 +0,0 @@
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
- }
@@ -1,95 +0,0 @@
1
- import { join, relative } from "node:path";
2
- import { readTextOrNull } from "./filesystem.js";
3
- import { projectPaths } from "./paths.js";
4
- /**
5
- * Canonical project documentation slots that `task generate` looks at.
6
- * Files that don't exist are returned with `content: null` so the caller
7
- * can decide whether to mention them as "missing" in the generated prompt.
8
- */
9
- const PROJECT_SLOTS = [
10
- { label: "Backlog (primary)", fileName: "07-backlog.md", category: "backlog", primary: true },
11
- { label: "Overview", fileName: "00-overview.md", category: "overview" },
12
- { label: "Requirements", fileName: "01-requirements.md", category: "requirements" },
13
- { label: "MVP Scope", fileName: "02-mvp-scope.md", category: "mvp-scope" },
14
- { label: "Architecture", fileName: "03-architecture.md", category: "architecture" },
15
- { label: "Tech Stack", fileName: "04-tech-stack.md", category: "tech-stack" },
16
- { label: "Current State", fileName: "05-current-state.md", category: "current-state" },
17
- { label: "Decisions", fileName: "06-decisions.md", category: "decisions" },
18
- { label: "Env", fileName: "08-env.md", category: "env" },
19
- { label: "Deployment", fileName: "09-deployment.md", category: "deployment" },
20
- ];
21
- /**
22
- * Fallback set of project doc filenames for repositories that pre-date the
23
- * 10-file template layout (e.g. this VibeOps repo itself uses 00–05).
24
- * Only consulted when a slot above is missing.
25
- */
26
- const LEGACY_FALLBACKS = new Map([
27
- ["07-backlog.md", ["05-backlog.md"]],
28
- ["00-overview.md", []],
29
- ["03-architecture.md", ["01-architecture.md"]],
30
- ["04-tech-stack.md", ["02-tech-stack.md"]],
31
- ["05-current-state.md", ["03-current-state.md"]],
32
- ["06-decisions.md", ["04-decisions.md"]],
33
- ]);
34
- function relDisplay(root, abs) {
35
- const r = relative(root, abs);
36
- return r.length === 0 ? "." : r;
37
- }
38
- async function readSlotWithFallback(cwd, spec) {
39
- const paths = projectPaths(cwd);
40
- const canonical = join(paths.docsProject, spec.fileName);
41
- let abs = canonical;
42
- let content = await readTextOrNull(abs);
43
- if (content === null) {
44
- for (const fallback of LEGACY_FALLBACKS.get(spec.fileName) ?? []) {
45
- abs = join(paths.docsProject, fallback);
46
- content = await readTextOrNull(abs);
47
- if (content !== null)
48
- break;
49
- }
50
- if (content === null)
51
- abs = canonical;
52
- }
53
- return {
54
- label: spec.label,
55
- path: abs,
56
- relativePath: relDisplay(cwd, abs),
57
- content,
58
- primary: spec.primary === true,
59
- category: spec.category,
60
- };
61
- }
62
- export async function collectInputDocs(inputs) {
63
- const slots = [];
64
- for (const spec of PROJECT_SLOTS) {
65
- slots.push(await readSlotWithFallback(inputs.cwd, spec));
66
- }
67
- const briefContent = await readTextOrNull(inputs.briefAbs);
68
- const brief = {
69
- label: "Project Brief",
70
- path: inputs.briefAbs,
71
- relativePath: relDisplay(inputs.cwd, inputs.briefAbs),
72
- content: briefContent,
73
- primary: false,
74
- category: "brief",
75
- };
76
- let from = null;
77
- if (typeof inputs.fromAbs === "string" && inputs.fromAbs.length > 0) {
78
- const content = await readTextOrNull(inputs.fromAbs);
79
- from = {
80
- label: "Custom input (--from)",
81
- path: inputs.fromAbs,
82
- relativePath: relDisplay(inputs.cwd, inputs.fromAbs),
83
- content,
84
- primary: true,
85
- category: "custom",
86
- };
87
- }
88
- return { slots, brief, from };
89
- }
90
- export function presentSlots(docs) {
91
- return docs.slots.filter((s) => s.content !== null);
92
- }
93
- export function missingSlots(docs) {
94
- return docs.slots.filter((s) => s.content === null);
95
- }
@@ -1,135 +0,0 @@
1
- function renderTopList(values) {
2
- if (values.length === 0)
3
- return "- _(none selected)_";
4
- return values.map((v) => `- ${v}`).join("\n");
5
- }
6
- function renderInlineList(label, values) {
7
- if (values.length === 0)
8
- return `- **${label}**: _(none selected)_`;
9
- const head = `- **${label}**:`;
10
- const items = values.map((v) => ` - ${v}`).join("\n");
11
- return `${head}\n${items}`;
12
- }
13
- function renderBool(value) {
14
- return value ? "yes" : "no";
15
- }
16
- function summary(brief) {
17
- const lines = [];
18
- lines.push(`- **Project name**: ${brief.projectName}`);
19
- lines.push(`- **One-line idea**: ${brief.oneLineIdea}`);
20
- lines.push(`- **Project type**: ${brief.projectType}`);
21
- lines.push(renderInlineList("Target users", brief.targetUsers));
22
- lines.push(`- **Core problem**: ${brief.coreProblem}`);
23
- lines.push(renderInlineList("MVP must-have features", brief.mvpFeatures));
24
- lines.push(renderInlineList("Out of scope for MVP", brief.outOfScope));
25
- lines.push(`- **Frontend**: ${brief.frontend}`);
26
- lines.push(`- **Backend**: ${brief.backend}`);
27
- lines.push(`- **Database**: ${brief.database}`);
28
- lines.push(`- **ORM / DB layer**: ${brief.dbLayer}`);
29
- lines.push(`- **Package manager**: ${brief.packageManager}`);
30
- lines.push(renderInlineList("Deployment target", brief.deploymentTargets));
31
- lines.push(renderInlineList("Auth requirement", brief.authRequirements));
32
- lines.push(renderInlineList("External integrations", brief.integrations));
33
- lines.push(`- **Use Notion dashboard sync**: ${renderBool(brief.useNotion)}`);
34
- lines.push(`- **Use Git task branch workflow**: ${renderBool(brief.useGitWorkflow)}`);
35
- lines.push(`- **Agent workflow level**: ${brief.agentWorkflowLevel}`);
36
- lines.push(renderInlineList("Risk areas", brief.risks));
37
- lines.push(`- **Success criteria**: ${brief.successCriteria}`);
38
- return lines.join("\n");
39
- }
40
- export function buildPlanPrompt(inputs) {
41
- const { brief, meta, briefRelativePath } = inputs;
42
- const assumptions = meta.assumptions.length > 0
43
- ? meta.assumptions.map((a) => `- ${a}`).join("\n")
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.";
55
- return `# VibeOps Plan Prompt — Cursor Planner Agent
56
-
57
- > ${opener}
58
-
59
- - Brief location: \`${briefRelativePath}\`
60
- - VibeOps version: ${meta.vibeopsVersion}
61
- - Generated: ${meta.generatedAt}
62
- - Source: ${meta.source} · schemaVersion: ${meta.schemaVersion}
63
-
64
- ---
65
-
66
- ## Role: Planner Agent
67
-
68
- Follow the definition in \`.vibeops/agents/planner.md\`. You do not write application code. You produce:
69
-
70
- 1. Updates to \`docs/project/*\` — vision / requirements / scope / architecture draft / tech stack / decisions / backlog / environment / deployment notes.
71
- 2. An initial backlog of \`docs/tasks/TASK-NNN-*.md\` files (3-6 to start). Each TASK contains the sections Status · MVP Phase · Goal · Scope · Out of Scope · Acceptance Criteria · Test Plan · Result · Test Result.
72
-
73
- ## Hard rules (failure if violated)
74
-
75
- - Do not produce application code. The deliverable for this round is limited to \`docs/**\`.
76
- - Do not touch VibeOps' own config (\`.vibeops/\`, \`.vibeops.json\`, \`templates/\`).
77
- - Source-of-truth rules: \`docs/tasks/*.md\` = AI execution baseline, \`docs/project/*.md\` = design / current-state baseline, Git commits/branches = change history, Notion = human dashboard (summary / status / priority / docs path only), chat is never a baseline.
78
- - One TASK, one focus. Surface inter-TASK dependencies in the body.
79
- - Never hide assumptions. Record them in both the docs/project bodies and the closing "Assumptions" section.
80
- - Notion / Git workflow / agent workflow level follow the ProjectBrief values below as-is. Do not change them on a whim.
81
-
82
- ## ProjectBrief (user answers)
83
-
84
- ${summary(brief)}
85
-
86
- ### Assumptions inherited from the brief
87
-
88
- ${assumptions}
89
-
90
- ## Output format
91
-
92
- Produce the response in this exact order:
93
-
94
- 1. **Plan Summary** — 5 to 8 bullets capturing the direction derived from the ProjectBrief (audience, scope, tech selections, key risks).
95
- 2. **docs/project/\\*** — emit the 8 files below, each in its own fenced code block. The first line of each block is \`<!-- file: docs/project/XX-name.md -->\`. (\`03-architecture\` and \`05-current-state\` are not produced in this round.)
96
- - \`docs/project/00-overview.md\`
97
- - \`docs/project/01-requirements.md\`
98
- - \`docs/project/02-mvp-scope.md\`
99
- - \`docs/project/04-tech-stack.md\`
100
- - \`docs/project/06-decisions.md\`
101
- - \`docs/project/07-backlog.md\`
102
- - \`docs/project/08-env.md\`
103
- - \`docs/project/09-deployment.md\`
104
- 3. **docs/tasks/TASK-NNN-\\*** — 3 to 6 initial backlog items. Each in a fenced block beginning with \`<!-- file: docs/tasks/TASK-NNN-slug.md -->\`.
105
- 4. **Changed file list** — the full list of files produced above.
106
- 5. **Assumptions** — decisions the user must reconfirm (\`(none)\` if there are none).
107
-
108
- ## Field mapping (brief field → docs file)
109
-
110
- - \`00-overview.md\` ← projectName, oneLineIdea, projectType, targetUsers, coreProblem, successCriteria
111
- - \`01-requirements.md\` ← mvpFeatures, authRequirements, integrations, targetUsers
112
- - \`02-mvp-scope.md\` ← mvpFeatures (IN), outOfScope (OUT), successCriteria
113
- - \`04-tech-stack.md\` ← frontend, backend, database, dbLayer, packageManager
114
- - \`06-decisions.md\` ← useNotion, useGitWorkflow, agentWorkflowLevel, packageManager, plus any auto-derived decisions.
115
- - \`07-backlog.md\` ← decompose mvpFeatures into TASKs (1-2 per feature + 1 setup). Include priority and definition of done.
116
- - \`08-env.md\` ← env variables per integration (e.g. OpenAI → OPENAI_API_KEY) and their purpose.
117
- - \`09-deployment.md\` ← deployment notes per deploymentTargets. If only "Not sure" is selected, state that explicitly and mark the decision as pending.
118
-
119
- ## Notion / Git / Agent workflow handling
120
-
121
- - Use Notion dashboard sync: ${renderBool(brief.useNotion)} → record in \`06-decisions.md\`. ${brief.useNotion ? "Decision: VibeOps will sync Notion DB metadata." : "Decision: no Notion sync (revisit in a future TASK if needed)."}
122
- - Use Git task branch workflow: ${renderBool(brief.useGitWorkflow)} → ${brief.useGitWorkflow ? "TASK lifecycle assumes the task/TASK-NNN-slug branch model." : "Record that the Git task-branch model is not used (linear workflow)."}
123
- - Agent workflow level: \`${brief.agentWorkflowLevel}\` → fix the agent line-up in \`06-decisions.md\`.
124
-
125
- ## Risk areas → docs
126
-
127
- ${renderTopList(brief.risks)}
128
-
129
- Record each risk in \`07-backlog.md\` or in the Risks section of the corresponding TASK. Operational risks such as "Authentication/security" or "Browser automation reliability" become candidates for their own TASK.
130
-
131
- ---
132
-
133
- Apply the rules above and produce the response. After you respond, a human reviews via \`git diff\` and commits.
134
- `;
135
- }
@@ -1,40 +0,0 @@
1
- import { findMvpTaskFile } from "./mvp-artifacts.js";
2
- import { TASK_MVP_ID, resolveTaskRef } from "./mvp-constants.js";
3
- import { findTaskFile, pickActiveTask, readTaskFile } from "./task.js";
4
- /**
5
- * Resolve which TASK file `start` / `done` / `rollback` target.
6
- * Default (no ref): TASK-mvp if present, else active/next backlog TASK (legacy projects).
7
- */
8
- export async function resolveCommandTask(paths, cwd, taskRef) {
9
- const trimmed = taskRef?.trim();
10
- if (trimmed !== undefined && trimmed.length > 0) {
11
- const taskId = resolveTaskRef(trimmed);
12
- const taskFile = await findTaskFile(paths.docsTasks, taskId);
13
- if (taskFile === null)
14
- return null;
15
- return { taskId, taskFile, source: "explicit" };
16
- }
17
- const mvpFile = await findMvpTaskFile(paths.docsTasks);
18
- if (mvpFile !== null) {
19
- const meta = await readTaskFile(mvpFile);
20
- return { taskId: meta.id, taskFile: mvpFile, source: "mvp-default" };
21
- }
22
- const active = await pickActiveTask(paths.docsTasks, cwd);
23
- if (active !== null) {
24
- return {
25
- taskId: active.id,
26
- taskFile: active.filePath,
27
- source: "backlog-active",
28
- };
29
- }
30
- return null;
31
- }
32
- export function commandTaskNotFoundMessage(taskRef) {
33
- const trimmed = taskRef?.trim();
34
- if (trimmed !== undefined && trimmed.length > 0) {
35
- const id = resolveTaskRef(trimmed);
36
- return `TASK not found: ${id}. Check docs/tasks/ or the TASK id.`;
37
- }
38
- return (`No TASK found (no ${TASK_MVP_ID} — run ${"vibeops plan"} for a new MVP slice, ` +
39
- `or add docs/tasks/TASK-NNN-*.md and run vibeops start TASK-NNN).`);
40
- }
@@ -1,39 +0,0 @@
1
- import { join } from "node:path";
2
- import { VERSION } from "../version.js";
3
- export function taskBuildPromptRel(taskId) {
4
- const slug = taskId.toLowerCase().replace(/[^a-z0-9-]+/g, "-");
5
- return `.vibeops/generated/task-build-${slug}.md`;
6
- }
7
- export function buildTaskBuildPrompt(params) {
8
- return `# Build ${params.taskId} — ${params.projectName}
9
-
10
- You are the **implementer** in Cursor for this repository. Deliver everything in the TASK file below. VibeOps owns Git/docs rails; **you** own code and verification.
11
-
12
- ## Source of truth (read first)
13
-
14
- 1. \`AGENTS.md\` and \`.cursor/rules/\`
15
- 2. \`docs/project/05-current-state.md\` (or \`03-current-state.md\` if present)
16
- 3. \`docs/project/02-mvp-scope.md\`, \`03-architecture.md\`, \`04-tech-stack.md\`, \`06-decisions.md\`
17
- 4. TASK file: \`${params.taskRelativePath}\` — work **only** inside Scope / Acceptance Criteria
18
-
19
- ## How to work
20
-
21
- - Implement **only** \`${params.taskId}\`. Do not start other TASK ids unless the human asks.
22
- - **Search** the repo before adding files; reuse existing patterns.
23
- - Fill **Result** and **Test Result** in the TASK file with facts (paths, commands, outcomes).
24
- - No \`git push\` or merge — the human runs \`vibeops done ${params.taskId}\` when ready.
25
-
26
- ## Definition of done
27
-
28
- 1. Every **Acceptance Criteria** item in \`${params.taskRelativePath}\` is met.
29
- 2. **Result** and **Test Result** are filled (not placeholders).
30
- 3. Working tree is ready to commit on the task branch if needed.
31
-
32
- ---
33
-
34
- *Generated by \`vibeops task add\` (VibeOps ${VERSION}).*
35
- `;
36
- }
37
- export function taskBuildPromptAbs(cwd, taskId) {
38
- return join(cwd, taskBuildPromptRel(taskId));
39
- }
@@ -1,81 +0,0 @@
1
- import { join } from "node:path";
2
- import { cursorAgentPrint, extractAgentAssistantText } from "./plan-llm-cursor-agent.js";
3
- import { parsePlannerFenceFiles, writePlannerFiles } from "./plan-apply-planner.js";
4
- import { probeCursorAgentCli } from "./plan-llm-detect.js";
5
- import { REQUIRED_TASK_SECTIONS } from "./task-generator.js";
6
- import { bold, log } from "./logger.js";
7
- const SINGLE_TASK_SYSTEM = `You are the VibeOps Planner Agent. Create exactly ONE TASK markdown file.
8
-
9
- Rules:
10
- - Output normal markdown with exactly one fenced code block.
11
- - First line inside the block MUST be: <!-- file: docs/tasks/TASK-NNN-slug.md --> (use the path given in the user message).
12
- - Fill every section listed in the template. Status must be "planned". Git Context and Result sections use placeholders only.
13
- - Do not create application code. Do not edit files outside docs/tasks/.
14
- - Acceptance Criteria must be numbered and testable.`;
15
- export async function generateTaskFileViaCursor(input) {
16
- const relPath = `docs/tasks/${input.taskId}-${input.slug}.md`;
17
- const phase = input.phase?.trim() || "MVP 1";
18
- if (input.dryRun === true) {
19
- return { ok: true, writtenPath: relPath };
20
- }
21
- const probe = await probeCursorAgentCli(input.cwd);
22
- if (!probe.ok) {
23
- return {
24
- ok: false,
25
- reason: probe.reason ?? "Cursor Agent CLI is not ready. Run `agent login` or use quick skeleton mode.",
26
- };
27
- }
28
- const userPrompt = `${SINGLE_TASK_SYSTEM}
29
-
30
- Create this file:
31
-
32
- <!-- file: ${relPath} -->
33
-
34
- Template sections (all required):
35
- ${REQUIRED_TASK_SECTIONS.map((s) => `- ${s}`).join("\n")}
36
-
37
- TASK id: ${input.taskId}
38
- Title: ${input.title}
39
- MVP Phase: ${phase}
40
-
41
- User description:
42
- ${input.idea}
43
-
44
- Write the complete TASK markdown body now.`;
45
- log.step(bold(`Cursor Agent is writing ${relPath}…`));
46
- try {
47
- const raw = await cursorAgentPrint({
48
- cwd: input.cwd,
49
- prompt: userPrompt,
50
- model: input.model,
51
- });
52
- const text = extractAgentAssistantText(raw);
53
- const parsed = parsePlannerFenceFiles(text);
54
- const match = parsed.find((f) => f.relativePath === relPath) ??
55
- parsed.find((f) => f.relativePath.endsWith(`${input.taskId}-${input.slug}.md`)) ??
56
- parsed[0];
57
- if (!match) {
58
- return {
59
- ok: false,
60
- reason: "Cursor output did not include a valid <!-- file: docs/tasks/... --> block.",
61
- };
62
- }
63
- if (match.relativePath !== relPath) {
64
- log.warn(`Model used path ${match.relativePath}; writing to planned ${relPath}.`);
65
- }
66
- const written = await writePlannerFiles(input.cwd, [
67
- { relativePath: relPath, content: match.content },
68
- ]);
69
- if (written.length === 0) {
70
- return { ok: false, reason: "Failed to write TASK file." };
71
- }
72
- return { ok: true, writtenPath: relPath };
73
- }
74
- catch (e) {
75
- const msg = e instanceof Error ? e.message : String(e);
76
- return { ok: false, reason: msg };
77
- }
78
- }
79
- export function plannedTaskRelPath(taskId, slug) {
80
- return join("docs", "tasks", `${taskId}-${slug}.md`);
81
- }