@goodtek/vibeops 0.7.22 → 1.0.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.
Files changed (121) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/README.md +62 -382
  3. package/dist/bootstrap/installer.js +4 -16
  4. package/dist/cli.js +26 -160
  5. package/dist/commands/init.js +3 -3
  6. package/dist/commands/status.js +145 -8
  7. package/dist/commands/task-add.js +64 -164
  8. package/dist/commands/task-done.js +117 -74
  9. package/dist/lib/config.js +2 -118
  10. package/dist/lib/git.js +1 -1
  11. package/dist/lib/paths.js +7 -3
  12. package/dist/lib/plan-llm-openai.js +0 -63
  13. package/dist/lib/resolve-task.js +34 -0
  14. package/dist/lib/task-add-llm.js +46 -233
  15. package/dist/lib/task-context.js +68 -0
  16. package/dist/lib/task-done-llm.js +148 -0
  17. package/dist/lib/task-merge.js +54 -159
  18. package/dist/lib/task-scaffold.js +39 -43
  19. package/dist/lib/task-start.js +72 -0
  20. package/dist/lib/task-summary.js +12 -239
  21. package/dist/lib/task.js +15 -24
  22. package/dist/types/config.js +0 -16
  23. package/package.json +3 -5
  24. package/templates/.cursor/rules/01-task-source-of-truth.mdc +12 -0
  25. package/templates/.cursor/rules/02-git-safety.mdc +11 -0
  26. package/templates/.cursor/rules/03-docs-on-done.mdc +13 -0
  27. package/templates/.cursor/skills/implement-task/SKILL.md +22 -0
  28. package/templates/.cursor/skills/plan-task/SKILL.md +19 -0
  29. package/templates/AGENTS.md +26 -87
  30. package/templates/docs/logs/README.md +4 -35
  31. package/templates/docs/project/03-architecture.md +10 -29
  32. package/templates/docs/project/05-current-state.md +7 -27
  33. package/templates/docs/project/06-decisions.md +6 -17
  34. package/templates/docs/tasks/TASK-000-template.md +9 -44
  35. package/dist/agent/loader.js +0 -71
  36. package/dist/agent/prompt.js +0 -66
  37. package/dist/commands/agent-list.js +0 -32
  38. package/dist/commands/agent-prompt.js +0 -59
  39. package/dist/commands/agent-show.js +0 -26
  40. package/dist/commands/done.js +0 -160
  41. package/dist/commands/github-init.js +0 -554
  42. package/dist/commands/github-status.js +0 -164
  43. package/dist/commands/next.js +0 -283
  44. package/dist/commands/notion-init.js +0 -764
  45. package/dist/commands/notion-sync.js +0 -405
  46. package/dist/commands/notion-test.js +0 -595
  47. package/dist/commands/plan.js +0 -146
  48. package/dist/commands/rollback.js +0 -126
  49. package/dist/commands/start.js +0 -129
  50. package/dist/commands/task-check.js +0 -155
  51. package/dist/commands/task-generate.js +0 -206
  52. package/dist/commands/task-pull.js +0 -277
  53. package/dist/commands/task-rollback.js +0 -174
  54. package/dist/commands/task-start.js +0 -102
  55. package/dist/commands/task-status.js +0 -122
  56. package/dist/lib/brief.js +0 -356
  57. package/dist/lib/cursor-task-prompt.js +0 -60
  58. package/dist/lib/done-follow-up.js +0 -308
  59. package/dist/lib/github-cli.js +0 -247
  60. package/dist/lib/guide-state.js +0 -69
  61. package/dist/lib/mvp-artifacts.js +0 -204
  62. package/dist/lib/mvp-constants.js +0 -15
  63. package/dist/lib/mvp-diff-summary.js +0 -43
  64. package/dist/lib/mvp-done-summary.js +0 -335
  65. package/dist/lib/notion-client.js +0 -459
  66. package/dist/lib/notion-discovery.js +0 -671
  67. package/dist/lib/notion-env.js +0 -140
  68. package/dist/lib/notion-mappers.js +0 -148
  69. package/dist/lib/notion-schema.js +0 -272
  70. package/dist/lib/notion-sync.js +0 -337
  71. package/dist/lib/notion-target.js +0 -247
  72. package/dist/lib/package-json.js +0 -133
  73. package/dist/lib/plan-apply-planner.js +0 -131
  74. package/dist/lib/plan-codex-pkce.js +0 -234
  75. package/dist/lib/plan-llm-models.js +0 -237
  76. package/dist/lib/plan-llm-normalize.js +0 -146
  77. package/dist/lib/plan-llm-session.js +0 -682
  78. package/dist/lib/plan-post-apply-setup.js +0 -116
  79. package/dist/lib/project-docs.js +0 -95
  80. package/dist/lib/prompt-builder.js +0 -135
  81. package/dist/lib/resolve-command-task.js +0 -40
  82. package/dist/lib/task-add-build-prompt.js +0 -39
  83. package/dist/lib/task-add-cursor.js +0 -81
  84. package/dist/lib/task-generator.js +0 -183
  85. package/dist/lib/task-prompt.js +0 -23
  86. package/dist/lib/task-pull.js +0 -354
  87. package/dist/lib/task-status-brief.js +0 -132
  88. package/dist/lib/workflow-guide.js +0 -485
  89. package/dist/status/collector.js +0 -103
  90. package/dist/status/format.js +0 -181
  91. package/dist/types/brief.js +0 -126
  92. package/templates/.cursor/rules/00-project-governance.mdc +0 -28
  93. package/templates/.cursor/rules/01-agent-orchestration.mdc +0 -48
  94. package/templates/.cursor/rules/02-task-workflow.mdc +0 -38
  95. package/templates/.cursor/rules/03-git-safety.mdc +0 -30
  96. package/templates/.cursor/rules/04-docs-update.mdc +0 -22
  97. package/templates/.vibeops/agents/architect.md +0 -47
  98. package/templates/.vibeops/agents/builder.md +0 -38
  99. package/templates/.vibeops/agents/docs.md +0 -54
  100. package/templates/.vibeops/agents/orchestrator.md +0 -40
  101. package/templates/.vibeops/agents/planner.md +0 -60
  102. package/templates/.vibeops/agents/recovery.md +0 -49
  103. package/templates/.vibeops/agents/reviewer.md +0 -47
  104. package/templates/.vibeops/agents/tester.md +0 -43
  105. package/templates/.vibeops/prompts/create-plan.md +0 -33
  106. package/templates/.vibeops/prompts/generate-tasks.md +0 -41
  107. package/templates/.vibeops/prompts/implement-task.md +0 -39
  108. package/templates/.vibeops/prompts/review-task.md +0 -34
  109. package/templates/.vibeops/prompts/rollback.md +0 -32
  110. package/templates/.vibeops/prompts/start-project.md +0 -39
  111. package/templates/.vibeops/workflows/notion-sync.md +0 -53
  112. package/templates/.vibeops/workflows/project-start.md +0 -73
  113. package/templates/.vibeops/workflows/rollback.md +0 -45
  114. package/templates/.vibeops/workflows/task-lifecycle.md +0 -71
  115. package/templates/docs/project/00-overview.md +0 -27
  116. package/templates/docs/project/01-requirements.md +0 -30
  117. package/templates/docs/project/02-mvp-scope.md +0 -36
  118. package/templates/docs/project/04-tech-stack.md +0 -29
  119. package/templates/docs/project/07-backlog.md +0 -23
  120. package/templates/docs/project/08-env.md +0 -29
  121. package/templates/docs/project/09-deployment.md +0 -28
@@ -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
- }