@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,485 +0,0 @@
1
- import { basename, join } from "node:path";
2
- import { pathExists } from "./filesystem.js";
3
- import { readText } from "./filesystem.js";
4
- import { gitBranchExists, gitGovernanceOnlyDirty, readGitInfo } from "./git.js";
5
- import { findMvpTaskFile } from "./mvp-artifacts.js";
6
- import { MVP_BUILD_PROMPT_REL, NEXT_TASK_SUGGESTION_REL, } from "./mvp-constants.js";
7
- import { LAST_DONE_SUMMARY_REL } from "./mvp-done-summary.js";
8
- import { cursorImplementPromptRel } from "./cursor-task-prompt.js";
9
- import { branchNameForTaskFile, hasNonEmptySection, isMvpTaskId, isOnTaskBranch, loadActionableTasks, pickActiveTask, pickLatestDoneTask, readGitContext, readTaskFile, } from "./task.js";
10
- export async function resolveMvpTask(paths) {
11
- const file = await findMvpTaskFile(paths.docsTasks);
12
- if (file === null)
13
- return null;
14
- return readTaskFile(file);
15
- }
16
- export async function resolveGuideTask(paths, cwd) {
17
- const mvpFile = await findMvpTaskFile(paths.docsTasks);
18
- if (mvpFile !== null) {
19
- return {
20
- mode: "mvp",
21
- task: await readTaskFile(mvpFile),
22
- referenceTask: null,
23
- };
24
- }
25
- const actionable = await loadActionableTasks(paths.docsTasks);
26
- const next = await pickActiveTask(paths.docsTasks, cwd);
27
- if (next !== null) {
28
- return { mode: "backlog", task: next, referenceTask: null };
29
- }
30
- const referenceTask = pickLatestDoneTask(actionable);
31
- if (actionable.length > 0 && actionable.every((t) => t.status === "done")) {
32
- return { mode: "post-mvp", task: null, referenceTask };
33
- }
34
- if (referenceTask !== null || (await hasPostMvpArtifacts(cwd))) {
35
- return { mode: "post-mvp", task: null, referenceTask };
36
- }
37
- return { mode: "greenfield", task: null, referenceTask: null };
38
- }
39
- async function hasPostMvpArtifacts(cwd) {
40
- return ((await pathExists(join(cwd, LAST_DONE_SUMMARY_REL))) ||
41
- (await pathExists(join(cwd, NEXT_TASK_SUGGESTION_REL))));
42
- }
43
- export async function inferGuideStep(inputs) {
44
- const { cwd, paths, config } = inputs;
45
- const git = await readGitInfo(cwd);
46
- const resolved = await resolveGuideTask(paths, cwd);
47
- if (resolved.mode === "greenfield") {
48
- return {
49
- cwd,
50
- paths,
51
- config,
52
- mode: "greenfield",
53
- task: null,
54
- referenceTask: null,
55
- gitBranch: git.branch,
56
- gitDirty: git.dirty,
57
- step: buildStep("no-mvp", {
58
- mode: "greenfield",
59
- task: null,
60
- referenceTask: null,
61
- body: "",
62
- gitCtx: null,
63
- git,
64
- hasSuggestion: false,
65
- }),
66
- };
67
- }
68
- if (resolved.mode === "post-mvp") {
69
- const hasSuggestion = await pathExists(join(cwd, NEXT_TASK_SUGGESTION_REL));
70
- return {
71
- cwd,
72
- paths,
73
- config,
74
- mode: "post-mvp",
75
- task: null,
76
- referenceTask: resolved.referenceTask,
77
- gitBranch: git.branch,
78
- gitDirty: git.dirty,
79
- step: inputs.forceStepId !== undefined
80
- ? buildStep(inputs.forceStepId, {
81
- mode: "post-mvp",
82
- task: null,
83
- referenceTask: resolved.referenceTask,
84
- body: "",
85
- gitCtx: null,
86
- git,
87
- hasSuggestion,
88
- })
89
- : buildStep("continue-iteration", {
90
- mode: "post-mvp",
91
- task: null,
92
- referenceTask: resolved.referenceTask,
93
- body: "",
94
- gitCtx: null,
95
- git,
96
- hasSuggestion,
97
- }),
98
- };
99
- }
100
- const task = resolved.task;
101
- const body = await readText(task.filePath);
102
- const gitCtx = await readGitContext(task.filePath);
103
- const taskBranch = branchNameForTaskFile(task.filePath);
104
- const branchExists = await gitBranchExists(cwd, taskBranch);
105
- const hasSuggestion = await pathExists(join(cwd, NEXT_TASK_SUGGESTION_REL));
106
- const step = inputs.forceStepId !== undefined
107
- ? buildStep(inputs.forceStepId, {
108
- mode: resolved.mode,
109
- task,
110
- referenceTask: null,
111
- body,
112
- gitCtx,
113
- git,
114
- hasSuggestion,
115
- taskBranch,
116
- branchExists,
117
- })
118
- : await inferStep({
119
- cwd,
120
- mode: resolved.mode,
121
- task,
122
- body,
123
- gitCtx,
124
- git,
125
- hasSuggestion,
126
- taskBranch,
127
- branchExists,
128
- });
129
- return {
130
- cwd,
131
- paths,
132
- config,
133
- mode: resolved.mode,
134
- task,
135
- referenceTask: null,
136
- gitBranch: git.branch,
137
- gitDirty: git.dirty,
138
- step,
139
- };
140
- }
141
- /** Next guide step when the current one is manual (user chose Next). */
142
- export function successorStepId(current, ctx) {
143
- const resultOk = hasNonEmptySection(ctx.body, "Result");
144
- const testOk = hasNonEmptySection(ctx.body, "Test Result");
145
- switch (current) {
146
- case "implement":
147
- if (resultOk && testOk)
148
- return "finish";
149
- if (ctx.gitDirty === true)
150
- return "commit";
151
- return null;
152
- case "commit":
153
- return "finish";
154
- default:
155
- return null;
156
- }
157
- }
158
- async function inferStep(ctx) {
159
- const { mode, task, body, gitCtx, git, hasSuggestion, taskBranch, branchExists } = ctx;
160
- const resultOk = hasNonEmptySection(body, "Result");
161
- const testOk = hasNonEmptySection(body, "Test Result");
162
- const onTaskBranch = git.branch === taskBranch;
163
- if (branchExists && !onTaskBranch) {
164
- return buildStep("checkout-task", {
165
- mode,
166
- task,
167
- referenceTask: null,
168
- body,
169
- gitCtx,
170
- git,
171
- hasSuggestion,
172
- taskBranch,
173
- branchExists,
174
- });
175
- }
176
- if (task.status === "done") {
177
- if (mode === "mvp") {
178
- return buildStep("all-done", { mode, task, referenceTask: null, body, gitCtx, git, hasSuggestion });
179
- }
180
- if (isOnTaskBranch(git.branch, gitCtx)) {
181
- return buildStep("finish", { mode, task, referenceTask: null, body, gitCtx, git, hasSuggestion });
182
- }
183
- return buildStep("continue-iteration", {
184
- mode: "post-mvp",
185
- task,
186
- referenceTask: task,
187
- body,
188
- gitCtx,
189
- git,
190
- hasSuggestion,
191
- });
192
- }
193
- if ((!gitCtx || task.status === "planned") && !branchExists) {
194
- if (git.dirty === true) {
195
- const gov = await gitGovernanceOnlyDirty(ctx.cwd);
196
- if (!gov.onlyGovernance) {
197
- return buildStep("prepare-start", {
198
- mode,
199
- task,
200
- referenceTask: null,
201
- body,
202
- gitCtx,
203
- git,
204
- hasSuggestion,
205
- taskBranch,
206
- branchExists,
207
- });
208
- }
209
- }
210
- return buildStep("start", {
211
- mode,
212
- task,
213
- referenceTask: null,
214
- body,
215
- gitCtx,
216
- git,
217
- hasSuggestion,
218
- taskBranch,
219
- branchExists,
220
- });
221
- }
222
- if (!resultOk || !testOk) {
223
- return buildStep("implement", { mode, task, referenceTask: null, body, gitCtx, git, hasSuggestion });
224
- }
225
- if (git.dirty === true) {
226
- const gov = await gitGovernanceOnlyDirty(ctx.cwd);
227
- if (!gov.onlyGovernance) {
228
- return buildStep("commit", {
229
- mode,
230
- task,
231
- referenceTask: null,
232
- body,
233
- gitCtx,
234
- git,
235
- hasSuggestion,
236
- taskBranch,
237
- branchExists,
238
- });
239
- }
240
- }
241
- return buildStep("finish", { mode, task, referenceTask: null, body, gitCtx, git, hasSuggestion });
242
- }
243
- function buildStep(stepId, ctx) {
244
- const { task, referenceTask, gitCtx, git, hasSuggestion } = ctx;
245
- const id = task?.id ?? referenceTask?.id ?? null;
246
- const isMvp = id !== null && isMvpTaskId(id);
247
- switch (stepId) {
248
- case "no-mvp":
249
- return {
250
- id: "no-mvp",
251
- taskId: null,
252
- title: "No TASK yet",
253
- youDo: ["Run planning to create TASK-mvp and the build prompt."],
254
- vibeopsCommand: "vibeops plan",
255
- shellHints: ['vibeops plan --idea "Your product idea"'],
256
- runnable: false,
257
- };
258
- case "continue-iteration": {
259
- const ref = referenceTask ?? task;
260
- const lines = [];
261
- if (ref) {
262
- lines.push(`Last finished: ${ref.id}${ref.title ? ` — ${ref.title}` : ""}.`);
263
- }
264
- else {
265
- lines.push("MVP / backlog work is complete on this project.");
266
- }
267
- lines.push(`Read ${LAST_DONE_SUMMARY_REL} for what shipped (if present).`);
268
- if (hasSuggestion) {
269
- lines.push(`Review ${NEXT_TASK_SUGGESTION_REL} from the last vibeops done.`);
270
- }
271
- lines.push("Plan the next slice — prior summary is injected into TASK-mvp + mvp-build.md.");
272
- const shellHints = ['vibeops plan --idea "Next feature or slice"'];
273
- if (hasSuggestion) {
274
- shellHints.push(`cat ${NEXT_TASK_SUGGESTION_REL}`);
275
- }
276
- return {
277
- id: "continue-iteration",
278
- taskId: ref?.id ?? null,
279
- title: "Continue after MVP",
280
- youDo: lines,
281
- vibeopsCommand: "vibeops plan",
282
- shellHints,
283
- runnable: false,
284
- };
285
- }
286
- case "prepare-start":
287
- return {
288
- id: "prepare-start",
289
- taskId: id,
290
- title: `Prepare git before ${id}`,
291
- youDo: [
292
- git.branch
293
- ? `On ${git.branch} with uncommitted project files (not just .vibeops/).`
294
- : "Uncommitted project files on the working tree.",
295
- "Commit or stash them before `vibeops start` creates a task branch.",
296
- "If this is TASK-009 (Next.js scaffold), finish with `vibeops done TASK-009` or commit that work first.",
297
- `Optional: vibeops start ${id} --allow-dirty (carries current changes into the new task branch).`,
298
- ].filter((l) => l.length > 0),
299
- vibeopsCommand: null,
300
- shellHints: [
301
- "git status",
302
- 'git stash push -u -m "wip before task branch"',
303
- `vibeops start ${id} --allow-dirty`,
304
- ],
305
- runnable: false,
306
- };
307
- case "start":
308
- return {
309
- id: "start",
310
- taskId: id,
311
- title: isMvp ? "Start MVP branch" : `Start ${id}`,
312
- youDo: isMvp
313
- ? ["Creates task/mvp-* branch and Git Context."]
314
- : [`Creates task branch and Git Context for ${id}.`],
315
- vibeopsCommand: isMvp ? "vibeops start" : `vibeops start ${id}`,
316
- shellHints: [],
317
- runnable: true,
318
- };
319
- case "checkout-task": {
320
- const branch = ctx.taskBranch ?? gitCtx?.taskBranch ?? "task/…";
321
- return {
322
- id: "checkout-task",
323
- taskId: id,
324
- title: `Resume ${id} branch`,
325
- youDo: [
326
- git.branch ? `Current branch: ${git.branch}` : "",
327
- `Switch: git switch ${branch} (or: vibeops start ${id})`,
328
- "Does not recreate the branch if it already exists.",
329
- ].filter((l) => l.length > 0),
330
- vibeopsCommand: isMvp ? "vibeops start" : `vibeops start ${id}`,
331
- shellHints: [`git switch ${branch}`],
332
- runnable: true,
333
- };
334
- }
335
- case "implement": {
336
- const branchNote = gitCtx && git.branch && git.branch !== gitCtx.taskBranch
337
- ? `On ${git.branch} — switch: git switch ${gitCtx.taskBranch}`
338
- : gitCtx
339
- ? `Branch: ${gitCtx.taskBranch}`
340
- : "";
341
- const taskFile = task ? basename(task.filePath) : "TASK file";
342
- const fillResult = !hasNonEmptySection(ctx.body, "Result");
343
- const fillTestResult = !hasNonEmptySection(ctx.body, "Test Result");
344
- const needsCursorPrompt = fillResult || fillTestResult;
345
- const promptRel = task && needsCursorPrompt ? cursorImplementPromptRel(task.id) : null;
346
- const implementLines = isMvp
347
- ? [
348
- branchNote,
349
- `Drag ${MVP_BUILD_PROMPT_REL} into a new chat.`,
350
- ...(needsCursorPrompt && promptRel
351
- ? [
352
- `Result / Test Result empty — in Cursor: @${promptRel} (see ${promptRel}).`,
353
- ]
354
- : ["Meet Acceptance Criteria; fill Result and Test Result."]),
355
- ]
356
- : [
357
- branchNote,
358
- `Open docs/tasks/${taskFile} in Cursor.`,
359
- ...(needsCursorPrompt && promptRel
360
- ? [
361
- `Result / Test Result empty — in Cursor: @${promptRel} or paste the prompt from that file.`,
362
- ]
363
- : ["Meet Acceptance Criteria; fill Result and Test Result."]),
364
- ];
365
- return {
366
- id: "implement",
367
- taskId: id,
368
- title: isMvp ? "Build MVP in Cursor" : `Implement ${id}`,
369
- youDo: implementLines.filter((l) => l.length > 0),
370
- vibeopsCommand: null,
371
- shellHints: promptRel ? [`cat ${promptRel}`] : [],
372
- runnable: false,
373
- };
374
- }
375
- case "commit": {
376
- const doneCmd = isMvp ? "vibeops done" : `vibeops done ${id}`;
377
- return {
378
- id: "commit",
379
- taskId: id,
380
- title: "Commit work on task branch",
381
- youDo: [
382
- "Commit task files (docs/, src/) — not node_modules or .next.",
383
- `Or run ${doneCmd} to commit safe paths and merge.`,
384
- ],
385
- vibeopsCommand: null,
386
- shellHints: [
387
- "git status",
388
- "git add docs/ src/ …",
389
- `git commit -m "feat(${id?.toLowerCase() ?? "task"}): <summary>"`,
390
- doneCmd,
391
- ],
392
- runnable: false,
393
- };
394
- }
395
- case "finish": {
396
- const markedDone = task?.status === "done";
397
- return {
398
- id: "finish",
399
- taskId: id,
400
- title: isMvp
401
- ? "Finish MVP (merge)"
402
- : markedDone
403
- ? `Close out ${id} (merge & sync)`
404
- : `Finish ${id}`,
405
- youDo: markedDone
406
- ? [
407
- "TASK file already says Done — still on the task branch.",
408
- "Run done to commit if needed, merge to base branch, delete task branch.",
409
- "Runs Notion sync when enabled.",
410
- ]
411
- : [
412
- "Requires Result and Test Result.",
413
- isMvp
414
- ? "Marks Done, commits if needed, merges to main, deletes task branch."
415
- : "Marks Done, commits if needed, merges to main when on the task branch.",
416
- "Runs Notion sync when enabled; offers post-MVP cleanup if the tree is still dirty.",
417
- ],
418
- vibeopsCommand: isMvp ? "vibeops done" : `vibeops done ${id}`,
419
- shellHints: [],
420
- runnable: true,
421
- };
422
- }
423
- case "notion-sync":
424
- return {
425
- id: "notion-sync",
426
- taskId: id,
427
- title: "Sync Notion metadata",
428
- youDo: ["Optional — pushes project + TASK metadata to Notion."],
429
- vibeopsCommand: "vibeops notion sync",
430
- shellHints: ["vibeops notion sync --dry-run"],
431
- runnable: true,
432
- };
433
- case "all-done":
434
- return {
435
- id: "all-done",
436
- taskId: id,
437
- title: "MVP complete",
438
- youDo: [
439
- `Read ${LAST_DONE_SUMMARY_REL} for what shipped.`,
440
- "Update docs/project/05-current-state.md if the project stage changed.",
441
- "Next iteration: vibeops plan (prior summary is injected into TASK + mvp-build.md).",
442
- ],
443
- vibeopsCommand: "vibeops plan",
444
- shellHints: hasSuggestion ? [`cat ${NEXT_TASK_SUGGESTION_REL}`] : [],
445
- runnable: false,
446
- };
447
- }
448
- }
449
- /** Higher = later in the TASK workflow (used to keep manual Next advances). */
450
- export function guideStepRank(stepId) {
451
- switch (stepId) {
452
- case "no-mvp":
453
- case "continue-iteration":
454
- return 0;
455
- case "prepare-start":
456
- return 5;
457
- case "start":
458
- case "checkout-task":
459
- return 10;
460
- case "implement":
461
- return 20;
462
- case "commit":
463
- return 30;
464
- case "finish":
465
- case "notion-sync":
466
- return 40;
467
- case "all-done":
468
- return 50;
469
- default:
470
- return 0;
471
- }
472
- }
473
- export function followUpStepId(completed) {
474
- switch (completed) {
475
- case "start":
476
- case "checkout-task":
477
- return "implement";
478
- case "commit":
479
- return "implement";
480
- case "finish":
481
- return "all-done";
482
- default:
483
- return null;
484
- }
485
- }
@@ -1,103 +0,0 @@
1
- import { basename } from "node:path";
2
- import { isDirectory, isFile, pathExists } from "../lib/filesystem.js";
3
- import { readConfig } from "../lib/config.js";
4
- import { readGitInfo } from "../lib/git.js";
5
- import { getNotionTokenSource } from "../lib/notion-env.js";
6
- import { readPackageJson, } from "../lib/package-json.js";
7
- import { countTasks, pickActiveTask, scanTasks } from "../lib/task.js";
8
- import { projectPaths } from "../lib/paths.js";
9
- import { DEFAULT_GITHUB_CONFIG, } from "../types/config.js";
10
- async function presenceCheck(label, path, required, asDir) {
11
- const present = asDir ? await isDirectory(path) : await isFile(path);
12
- return { label, path, present, required };
13
- }
14
- function snapshotNotion(notion, token) {
15
- const projects = (notion?.projectsTargetId ?? "").length > 0 ||
16
- (notion?.projectsDatabaseId ?? "").length > 0;
17
- const tasks = (notion?.tasksTargetId ?? "").length > 0 ||
18
- (notion?.tasksDatabaseId ?? "").length > 0;
19
- return {
20
- enabled: notion?.enabled ?? false,
21
- hasToken: token.hasToken,
22
- tokenSource: token.source,
23
- hasProjectsTarget: projects,
24
- hasTasksTarget: tasks,
25
- };
26
- }
27
- function snapshotGithub(github) {
28
- const g = github ?? DEFAULT_GITHUB_CONFIG;
29
- return {
30
- enabled: g.enabled,
31
- mode: g.enabled ? g.mode : "",
32
- owner: g.owner,
33
- repo: g.repo,
34
- remote: g.remote,
35
- url: g.url,
36
- };
37
- }
38
- function pickBinName(pkg) {
39
- const bin = pkg.bin;
40
- if (typeof bin === "string") {
41
- return basename(bin).replace(/\.[A-Za-z0-9]+$/, "");
42
- }
43
- if (bin !== null && typeof bin === "object") {
44
- const keys = Object.keys(bin);
45
- if (keys.length > 0)
46
- return keys[0];
47
- }
48
- return "";
49
- }
50
- function snapshotPackage(pkg) {
51
- if (pkg === null) {
52
- return { exists: false, name: "", version: "", bin: "" };
53
- }
54
- const data = pkg.data;
55
- const name = typeof data.name === "string" ? data.name : "";
56
- const version = typeof data.version === "string"
57
- ? (data.version ?? "")
58
- : "";
59
- return { exists: true, name, version, bin: pickBinName(data) };
60
- }
61
- export async function collectStatus(cwd) {
62
- const paths = projectPaths(cwd);
63
- const config = await readConfig(paths.root);
64
- const checks = [
65
- await presenceCheck(".vibeops.json", paths.config, true, false),
66
- await presenceCheck("AGENTS.md", paths.agentsMd, true, false),
67
- await presenceCheck(".cursor/rules/", paths.cursorRules, true, true),
68
- await presenceCheck("docs/project/", paths.docsProject, true, true),
69
- await presenceCheck("docs/tasks/", paths.docsTasks, true, true),
70
- await presenceCheck("docs/logs/", paths.docsLogs, false, true),
71
- await presenceCheck(".vibeops/agents/", paths.vibeopsAgents, true, true),
72
- await presenceCheck(".vibeops/prompts/", paths.vibeopsPrompts, false, true),
73
- await presenceCheck(".vibeops/workflows/", paths.vibeopsWorkflows, false, true),
74
- await presenceCheck(".vibeops.env.example", paths.envExample, false, false),
75
- ];
76
- const missingRequired = checks.filter((c) => c.required && !c.present);
77
- const tasks = (await pathExists(paths.docsTasks))
78
- ? await scanTasks(paths.docsTasks)
79
- : [];
80
- const taskCounts = countTasks(tasks);
81
- const nextTask = await pickActiveTask(paths.docsTasks, paths.root);
82
- const git = await readGitInfo(paths.root);
83
- const tokenProbe = await getNotionTokenSource(paths.root);
84
- const notion = snapshotNotion(config?.notion, tokenProbe);
85
- const github = snapshotGithub(config?.github);
86
- const pkg = await readPackageJson(paths.root);
87
- const packageSnapshot = snapshotPackage(pkg);
88
- const isVibeopsProject = config !== null && missingRequired.length === 0;
89
- return {
90
- cwd: paths.root,
91
- isVibeopsProject,
92
- config,
93
- checks,
94
- missingRequired,
95
- tasks,
96
- taskCounts,
97
- nextTask,
98
- git,
99
- notion,
100
- github,
101
- package: packageSnapshot,
102
- };
103
- }