@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,27 +1,12 @@
1
- /**
2
- * Small text-extraction helpers used by `vibeops notion sync` and
3
- * `vibeops task pull`.
4
- *
5
- * Everything here is read-only (`extract*`) or returns new strings the caller
6
- * may write back (`upsertNotionPageSection`, `renderPulledTaskMarkdown`).
7
- *
8
- * Goal/Background extraction is deliberately heuristic:
9
- * - we strip markdown bullet/numbering prefixes,
10
- * - we collapse blank-line groups so the first non-empty paragraph wins,
11
- * - we truncate to `NOTION_TEXT_LIMIT` (default 1500 chars).
12
- *
13
- * We never include placeholder content such as `(not yet)`, legacy localized
14
- * placeholders, or `(scaffolded ...)`; those become an empty string.
15
- */
16
- import { readTextOrNull, writeText } from "./filesystem.js";
17
1
  import { isPlaceholderContent, readSection } from "./task.js";
18
- import { truncate } from "./notion-mappers.js";
19
- const PLACEHOLDER_RE = new RegExp(String.raw `^\(.*(not yet|fill in|unassigned|scaffold|\uBBF8\uC218\uD589|\uBBF8\uC815|\uCC44\uC6CC\uB77C).*\)$`, "i");
2
+ const PLACEHOLDER_RE = new RegExp(String.raw `^\(.*(not yet|fill in|unassigned|scaffold|\uBBF8\uC218\uD589).*\)$`, "i");
20
3
  function stripBullet(line) {
21
4
  return line.replace(/^\s*(?:[-*+]|\d+\.)\s+/, "").trim();
22
5
  }
23
- function isHeading(line) {
24
- return /^#{1,6}\s+/.test(line);
6
+ function truncate(text, limit) {
7
+ if (text.length <= limit)
8
+ return text;
9
+ return `${text.slice(0, limit - 1)}…`;
25
10
  }
26
11
  function compressBlank(lines) {
27
12
  const out = [];
@@ -39,19 +24,12 @@ function compressBlank(lines) {
39
24
  }
40
25
  return out.join("\n").trim();
41
26
  }
42
- /**
43
- * Pull a flattened, single-string summary from a `## Section`. Bullet points
44
- * and numbered list items are joined with " · ". Placeholder bodies become "".
45
- */
46
- export function summarizeSection(body, title, limit) {
27
+ export function summarizeSection(body, title, limit = 280) {
47
28
  const raw = readSection(body, title);
48
- if (raw.length === 0)
29
+ if (raw.length === 0 || isPlaceholderContent(raw))
49
30
  return "";
50
- if (isPlaceholderContent(raw))
51
- return "";
52
- const lines = raw.split("\n");
53
31
  const cleaned = [];
54
- for (const line of lines) {
32
+ for (const line of raw.split("\n")) {
55
33
  const t = line.trim();
56
34
  if (t.length === 0) {
57
35
  cleaned.push("");
@@ -59,218 +37,13 @@ export function summarizeSection(body, title, limit) {
59
37
  }
60
38
  if (PLACEHOLDER_RE.test(t))
61
39
  continue;
62
- if (isHeading(t))
40
+ if (/^#{1,6}\s+/.test(t))
63
41
  continue;
64
42
  cleaned.push(stripBullet(t));
65
43
  }
66
- const compact = compressBlank(cleaned);
67
- return truncate(compact, limit);
44
+ return truncate(compressBlank(cleaned), limit);
68
45
  }
69
- export function summarizeGoal(body, limit) {
46
+ export function summarizeGoal(body, limit = 280) {
70
47
  const goal = summarizeSection(body, "Goal", limit);
71
- if (goal.length > 0)
72
- return goal;
73
- return summarizeSection(body, "Background", limit);
74
- }
75
- export function summarizeResult(body, limit) {
76
- return summarizeSection(body, "Result", limit);
77
- }
78
- /**
79
- * Best-effort: pull the very first non-heading paragraph from an arbitrary
80
- * markdown body (e.g. `docs/project/00-overview.md`). Returns "" if nothing
81
- * useful is found.
82
- */
83
- export function summarizeMarkdownLead(body, limit) {
84
- const lines = body.split("\n");
85
- const collected = [];
86
- let started = false;
87
- for (const raw of lines) {
88
- const t = raw.trim();
89
- if (t.length === 0) {
90
- if (started)
91
- break;
92
- continue;
93
- }
94
- if (isHeading(t)) {
95
- if (started)
96
- break;
97
- continue;
98
- }
99
- if (t.startsWith(">"))
100
- continue;
101
- started = true;
102
- collected.push(stripBullet(t));
103
- }
104
- return truncate(collected.join(" ").trim(), limit);
105
- }
106
- /**
107
- * Heuristic: scan `docs/project/05-current-state.md` (or any markdown) for an
108
- * "MVP 1 ~ N" hint. Falls back to the first heading-derived MVP token.
109
- */
110
- export function detectCurrentPhase(body) {
111
- if (body.length === 0)
112
- return "";
113
- const m = body.match(/MVP\s*\d+(?:\s*[·•:\-]\s*[^\n]+)?/i);
114
- if (m)
115
- return m[0].trim().replace(/\s+/g, " ");
116
- return "";
117
- }
118
- export function renderNotionPageBlock(inputs) {
119
- return [
120
- `- Page ID: \`${inputs.pageId}\``,
121
- `- Docs Path: \`${inputs.docsRelativePath}\``,
122
- ].join("\n");
123
- }
124
- const NOTION_PAGE_RE = /^-\s+Page ID:\s*`([^`]+)`/m;
125
- export function readNotionPageId(body) {
126
- const section = readSection(body, "Notion Page");
127
- if (section.length === 0)
128
- return null;
129
- const m = section.match(NOTION_PAGE_RE);
130
- return m ? m[1].trim() : null;
131
- }
132
- /**
133
- * Replace the body of an existing `## Notion Page` section, or append the
134
- * section to the file if it doesn't exist yet. Returns the new file body
135
- * (caller is responsible for writing it back).
136
- */
137
- export function upsertNotionPageSection(body, inputs) {
138
- const block = renderNotionPageBlock(inputs);
139
- const HEADING_RE = /^(##+)\s+(.+?)\s*$/;
140
- const lines = body.split("\n");
141
- let startIdx = -1;
142
- let endIdx = -1;
143
- let level = 2;
144
- for (let i = 0; i < lines.length; i++) {
145
- const m = HEADING_RE.exec(lines[i]);
146
- if (!m)
147
- continue;
148
- if (m[2].trim().toLowerCase() !== "notion page")
149
- continue;
150
- startIdx = i;
151
- level = m[1].length;
152
- let j = i + 1;
153
- while (j < lines.length) {
154
- const nm = HEADING_RE.exec(lines[j]);
155
- if (nm && nm[1].length <= level)
156
- break;
157
- j++;
158
- }
159
- endIdx = j;
160
- break;
161
- }
162
- if (startIdx < 0) {
163
- const trimmed = body.replace(/\s+$/u, "");
164
- return `${trimmed}\n\n## Notion Page\n\n${block}\n`;
165
- }
166
- const before = lines.slice(0, startIdx + 1);
167
- const after = lines.slice(endIdx);
168
- return [...before, "", block, "", ...after].join("\n").replace(/\n{3,}/g, "\n\n");
169
- }
170
- export async function writeNotionPageSection(filePath, inputs) {
171
- const current = await readTextOrNull(filePath);
172
- if (current === null)
173
- return false;
174
- const next = upsertNotionPageSection(current, inputs);
175
- if (next === current)
176
- return false;
177
- await writeText(filePath, next);
178
- return true;
179
- }
180
- /**
181
- * Render the markdown body for a TASK file that `vibeops task pull` is
182
- * creating from a Notion row. Always uses the 18-section skeleton so the
183
- * downstream `vibeops task done` validation can still run.
184
- *
185
- * Only the fields Notion actually carries get prefilled; everything else
186
- * stays a placeholder.
187
- */
188
- export function renderPulledTaskMarkdown(inputs) {
189
- const title = inputs.title.length > 0 ? inputs.title : "(pulled from Notion — fill in)";
190
- const status = inputs.status.length > 0 ? inputs.status : "planned";
191
- const phase = inputs.mvpPhase.length > 0 ? inputs.mvpPhase : "(unassigned)";
192
- const summary = inputs.summary.length > 0 ? inputs.summary : "(Notion Summary is empty — fill in.)";
193
- const notionBlock = renderNotionPageBlock({
194
- pageId: inputs.pageId,
195
- docsRelativePath: inputs.docsRelativePath,
196
- });
197
- return `# ${inputs.taskId} · ${title}
198
-
199
- > This file was generated by \`vibeops task pull\` from Notion metadata. The body is empty — the Builder/Planner Agent should fill it in.
200
-
201
- ## Status
202
-
203
- ${status}
204
-
205
- ## MVP Phase
206
-
207
- ${phase}
208
-
209
- ## Goal
210
-
211
- ${summary}
212
-
213
- ## Background
214
-
215
- (pulled — fill in context beyond the Notion Summary.)
216
-
217
- ## Scope
218
-
219
- - (pulled — Scope item 1)
220
- - (pulled — Scope item 2)
221
-
222
- ## Out of Scope
223
-
224
- - (pulled)
225
-
226
- ## Acceptance Criteria
227
-
228
- 1. (pulled — verifiable statement 1)
229
- 2. (pulled — verifiable statement 2)
230
-
231
- ## Files to Inspect First
232
-
233
- - (pulled)
234
-
235
- ## Expected Files to Change
236
-
237
- - new: (pulled)
238
- - update: (pulled)
239
-
240
- ## Risks
241
-
242
- - (pulled)
243
-
244
- ## Test Plan
245
-
246
- - (pulled)
247
-
248
- ## Rollback Plan
249
-
250
- - (pulled)
251
-
252
- ## Git Context
253
-
254
- (populated by \`vibeops task start ${inputs.taskId}\`)
255
-
256
- ## Notion Page
257
-
258
- ${notionBlock}
259
-
260
- ## Implementation Plan
261
-
262
- 1. (pulled)
263
-
264
- ## Result
265
-
266
- (not yet)
267
-
268
- ## Test Result
269
-
270
- (not yet)
271
-
272
- ## Review Notes
273
-
274
- (not yet)
275
- `;
48
+ return goal.length > 0 ? goal : summarizeSection(body, "Scope", limit);
276
49
  }
package/dist/lib/task.js CHANGED
@@ -34,9 +34,6 @@ export function statusDisplay(status) {
34
34
  }
35
35
  function extractIdFromFilename(file) {
36
36
  const name = basename(file);
37
- const mvp = /^TASK-mvp/i.exec(name);
38
- if (mvp)
39
- return "TASK-mvp";
40
37
  const m = /^(TASK-\d+)/i.exec(name);
41
38
  return m ? m[1].toUpperCase() : basename(file, ".md");
42
39
  }
@@ -103,7 +100,7 @@ export async function scanTasks(tasksDir) {
103
100
  return [];
104
101
  const entries = await readdir(tasksDir, { withFileTypes: true });
105
102
  const files = entries
106
- .filter((e) => e.isFile() && e.name.endsWith(".md") && /^TASK-(?:\d+|mvp)/i.test(e.name))
103
+ .filter((e) => e.isFile() && e.name.endsWith(".md") && /^TASK-\d+/i.test(e.name))
107
104
  .map((e) => join(tasksDir, e.name))
108
105
  .sort();
109
106
  const out = [];
@@ -141,19 +138,13 @@ export function pickNextTask(tasks) {
141
138
  if (review)
142
139
  return review;
143
140
  const planned = tasks.filter((t) => t.status === "planned");
144
- const mvp = planned.find((t) => t.id.toUpperCase() === "TASK-MVP");
145
- return mvp ?? planned[0] ?? null;
141
+ return planned[0] ?? null;
146
142
  }
147
143
  const TEMPLATE_TASK_ID = "TASK-000";
148
144
  export function filterActionableTasks(tasks) {
149
145
  return tasks.filter((t) => t.id.toUpperCase() !== TEMPLATE_TASK_ID);
150
146
  }
151
- export function isMvpTaskId(id) {
152
- return id.toUpperCase() === "TASK-MVP";
153
- }
154
147
  function taskSortKey(id) {
155
- if (isMvpTaskId(id))
156
- return 1_000_000;
157
148
  const m = /^TASK-(\d+)$/i.exec(id.trim());
158
149
  return m ? Number.parseInt(m[1], 10) : 0;
159
150
  }
@@ -191,26 +182,20 @@ export async function pickActiveTask(tasksDir, cwd) {
191
182
  }
192
183
  export async function findTaskFile(tasksDir, taskId) {
193
184
  const all = await scanTasks(tasksDir);
194
- const normalized = taskId.trim();
195
- const target = /^mvp$/i.test(normalized) || /^task-mvp$/i.test(normalized)
196
- ? "TASK-MVP"
197
- : normalized.toUpperCase();
185
+ const target = normalizeTaskRef(taskId);
198
186
  for (const t of all) {
199
187
  if (t.id.toUpperCase() === target)
200
188
  return t.filePath;
201
189
  }
202
190
  return null;
203
191
  }
192
+ function normalizeTaskRef(ref) {
193
+ const t = ref.trim().toUpperCase();
194
+ return t.startsWith("TASK-") ? t : `TASK-${t}`;
195
+ }
204
196
  const TASK_FILENAME_RE = /^TASK-(\d+)(?:-(.+))?$/i;
205
- const TASK_MVP_FILENAME_RE = /^TASK-mvp(?:-(.+))?$/i;
206
197
  export function parseTaskFilename(filePath) {
207
198
  const stem = basename(filePath, extname(filePath));
208
- const mvp = TASK_MVP_FILENAME_RE.exec(stem);
209
- if (mvp) {
210
- const tail = (mvp[1] ?? "").trim().toLowerCase();
211
- const slug = tail.length > 0 ? `mvp-${tail}` : "mvp";
212
- return { id: "TASK-mvp", number: "mvp", slug };
213
- }
214
199
  const m = TASK_FILENAME_RE.exec(stem);
215
200
  if (!m) {
216
201
  return { id: stem.toUpperCase(), number: "000", slug: stem.toLowerCase() };
@@ -377,8 +362,8 @@ export async function upsertGitContext(filePath, ctx) {
377
362
  if (locateSection(raw, "Git Context")) {
378
363
  updated = replaceSectionContent(raw, "Git Context", block);
379
364
  }
380
- else if (locateSection(raw, "MVP Phase")) {
381
- updated = insertSectionAfter(raw, "MVP Phase", "Git Context", block);
365
+ else if (locateSection(raw, "Test Plan")) {
366
+ updated = insertSectionAfter(raw, "Test Plan", "Git Context", block);
382
367
  }
383
368
  else if (locateSection(raw, "Status")) {
384
369
  updated = insertSectionAfter(raw, "Status", "Git Context", block);
@@ -397,6 +382,12 @@ const GIT_CTX_RE = {
397
382
  startedAt: /^-\s+Started At:\s*`([^`]+)`/m,
398
383
  doneAt: /^-\s+Done At:\s*`([^`]+)`/m,
399
384
  };
385
+ export async function markGitContextDone(filePath) {
386
+ const ctx = await readGitContext(filePath);
387
+ if (ctx === null)
388
+ return;
389
+ await upsertGitContext(filePath, { ...ctx, doneAt: new Date().toISOString() });
390
+ }
400
391
  export async function readGitContext(filePath) {
401
392
  const raw = await readText(filePath);
402
393
  const content = readSection(raw, "Git Context");
@@ -1,17 +1 @@
1
1
  export const VIBEOPS_CONFIG_SCHEMA_VERSION = 1;
2
- export const DEFAULT_NOTION_CONFIG = {
3
- enabled: false,
4
- projectsTargetId: "",
5
- tasksTargetId: "",
6
- projectsDatabaseId: "",
7
- tasksDatabaseId: "",
8
- };
9
- export const DEFAULT_GITHUB_CONFIG = {
10
- enabled: false,
11
- mode: "gh-cli",
12
- owner: "",
13
- repo: "",
14
- remote: "origin",
15
- visibility: "",
16
- url: "",
17
- };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@goodtek/vibeops",
3
- "version": "0.7.22",
4
- "description": "Workflow rails for Cursor-based vibe coding projects.",
3
+ "version": "1.0.0",
4
+ "description": "Minimal CLI rails for Cursor-based TASK-driven vibe coding.",
5
5
  "type": "module",
6
6
  "author": "VibeOps contributors",
7
7
  "license": "MIT",
@@ -35,14 +35,11 @@
35
35
  "coding",
36
36
  "cli",
37
37
  "task",
38
- "notion",
39
38
  "vibeops",
40
39
  "workflow"
41
40
  ],
42
41
  "dependencies": {
43
- "@goodtek/vibeops": "^0.3.0",
44
42
  "@inquirer/prompts": "^8.4.3",
45
- "@notionhq/client": "^5.20.0",
46
43
  "commander": "^12.1.0",
47
44
  "gray-matter": "^4.0.3"
48
45
  },
@@ -57,6 +54,7 @@
57
54
  "typecheck": "tsc -p tsconfig.json --noEmit",
58
55
  "start": "node dist/cli.js",
59
56
  "smoke": "pnpm typecheck && pnpm build && node scripts/smoke.mjs",
57
+ "clean": "rm -rf dist",
60
58
  "publish:dry": "pnpm publish --dry-run --no-git-checks"
61
59
  }
62
60
  }
@@ -0,0 +1,12 @@
1
+ ---
2
+ description: TASK markdown is the execution source of truth; one TASK at a time
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # TASK-driven work
7
+
8
+ - Implement **one TASK** only (`docs/tasks/TASK-NNN-*.md`).
9
+ - Read the **entire** current TASK file before changing code or docs.
10
+ - Work only inside **Scope** and **Acceptance Criteria**.
11
+ - Do not start another TASK id unless the human asks.
12
+ - If chat and TASK docs disagree, **fix the docs first**, then implement.
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Git safety for task branches and commits
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Git safety
7
+
8
+ - Use branch pattern `task/*` created by `vibeops task add`.
9
+ - Include the TASK id in commit messages, e.g. `feat(task-015): short title`.
10
+ - **No force-push** to shared branches (`main`, `master`).
11
+ - Do not merge or push unless the human runs `vibeops task done` or asks explicitly.
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: Update TASK Result and project memory when finishing work
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Docs when finishing a TASK
7
+
8
+ Before `vibeops task done`:
9
+
10
+ 1. Fill **Result** and **Test Result** in the TASK file (paths, commands, outcomes — facts only).
11
+ 2. If architecture or decisions changed, note them in the TASK; `task done` updates `docs/project/*`.
12
+
13
+ Do not mark a TASK complete with placeholder `(not yet)` sections.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: implement-task
3
+ description: Implements the current VibeOps TASK per Scope and Acceptance Criteria. Use when building features for docs/tasks/TASK-NNN.md.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Implement a TASK
8
+
9
+ ## Read first
10
+
11
+ 1. `AGENTS.md` and `.cursor/rules/`
12
+ 2. `docs/project/05-current-state.md`, `03-architecture.md`, `06-decisions.md`
13
+ 3. The TASK file referenced by the human
14
+
15
+ ## Instructions
16
+
17
+ 1. Work **only** within Scope and Acceptance Criteria.
18
+ 2. Search the repo before adding files; match existing patterns.
19
+ 3. Fill **Result** and **Test Result** with facts when done.
20
+ 4. Do not run `vibeops task done` or merge — the human closes the TASK.
21
+
22
+ Tell the human to run `vibeops task done TASK-NNN` when ready.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: plan-task
3
+ description: Refines a VibeOps TASK file (Goal, Scope, AC, Test Plan) via dialogue. Use when planning or editing docs/tasks/TASK-NNN.md before implementation.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Plan a TASK
8
+
9
+ The human opened a TASK file (e.g. `@docs/tasks/TASK-015-….md`).
10
+
11
+ ## Instructions
12
+
13
+ 1. Read the TASK file and `docs/project/05-current-state.md`.
14
+ 2. Ask only what is missing to make **Scope**, **Acceptance Criteria**, and **Test Plan** verifiable.
15
+ 3. Edit the TASK file in place — do not create a second TASK id.
16
+ 4. Keep **Out of Scope** explicit.
17
+ 5. Do not write application code in this step unless the human asks.
18
+
19
+ When planning is enough, tell the human to switch to Agent mode and implement per the TASK.
@@ -1,98 +1,37 @@
1
- # {{PROJECT_NAME}} — AI agent operating guide
1
+ # {{PROJECT_NAME}} — AI agent guide
2
2
 
3
- > This file was installed by **VibeOps**. Every AI agent on this project (including Cursor) reads it before touching code.
4
-
5
- ## Purpose
6
-
7
- This project runs **Cursor-based vibe coding** on rails. The execution input is **`docs/tasks/TASK-*.md`**, not chat history.
8
-
9
- ## Required reading before you code
10
-
11
- Read the docs below **before you start implementing**. Order top to bottom.
12
-
13
- | Document | Why |
14
- | ------------------------------------------ | --------------------------------------------------------- |
15
- | `docs/project/05-current-state.md` | Where the project is today and what comes next |
16
- | `docs/project/00-overview.md` | Vision, terminology, MVP boundaries |
17
- | `docs/project/02-mvp-scope.md` | What is in / out of MVP |
18
- | `docs/project/03-architecture.md` | System, folders, data flow |
19
- | `docs/project/04-tech-stack.md` | Tools used to build the project |
20
- | `docs/project/06-decisions.md` | Decisions already made (avoid conflicts) |
21
- | `docs/project/07-backlog.md` | TASK order and definition of done |
22
- | **Current TASK file** `docs/tasks/TASK-NNN-*` | Scope and Acceptance Criteria for the current TASK |
23
-
24
- `docs/project/01-requirements.md`, `08-env.md`, `09-deployment.md` are referenced as needed. **Always read the entire current TASK file.**
3
+ > Installed by **VibeOps**. Cursor reads this repo, not chat history.
25
4
 
26
5
  ## Source of truth
27
6
 
28
- | What | Where |
29
- | -------------------------- | ---------------------------------- |
30
- | AI execution input | Git `docs/tasks/*.md` |
31
- | Project design and status | Git `docs/project/*.md` |
32
- | Change history and rollback | Git commits / branches |
33
- | Human dashboard | Notion (metadata only, never body) |
34
- | **Not** a source of truth | Chat (Cursor history, Slack) |
35
-
36
- ## TASK-driven development rules
37
-
38
- 1. Implement **one TASK at a time**.
39
- 2. Stay inside the TASK's **Scope / Acceptance Criteria**.
40
- 3. Before adding new code, **search** for existing implementations or patterns and avoid duplication.
41
- 4. **Every mutating command** should support **`--dry-run`** (or an equivalent) so the plan can be previewed without side effects.
42
- 5. **Large refactors** happen only when a dedicated TASK calls for them.
43
- 6. **Notion / Git integrations** are only added when an explicit TASK assigns that responsibility.
44
- 7. When the work is finished, update docs per [Completion report](#completion-report) and `.cursor/rules/04-docs-update.mdc`.
45
-
46
- Details live in `.cursor/rules/`.
47
-
48
- ## Agent roles
49
-
50
- `.vibeops/agents/*.md` defines 8 agents for this project.
51
-
52
- - `orchestrator` — picks the next TASK and delegates to other agents.
53
- - `planner` — expands an idea into `docs/project/{00,01,02,07}`.
54
- - `architect` — fills in `docs/project/{03,04}` (architecture, tech stack).
55
- - `builder` — takes one TASK and edits code.
56
- - `reviewer` — compares the diff against the Acceptance Criteria.
57
- - `tester` — runs the Test Plan and fills in Test Result.
58
- - `docs` — updates `05-current-state.md`, the TASK Result, and `docs/logs/`.
59
- - `recovery` — diagnoses rollback options (destructive actions require `--confirm`).
60
-
61
- ## Forbidden
62
-
63
- - Changing requirements or implementing things "loosely" based on chat alone.
64
- - Editing code or config **without reading the current TASK file**.
65
- - Mixing **multiple TASKs** in one session, or adding a large structure (e.g. `src/`) without a TASK.
66
- - Features **outside the TASK Scope** or **outside the MVP**.
67
- - Creating similar modules **without searching first**.
68
- - Performing **large refactors** without a dedicated TASK.
69
- - Adding **Notion / Git integration** that the TASK does not authorize.
70
- - Calling the TASK done without updating **`05-current-state.md` / the TASK file / `docs/logs/YYYY-MM-DD.md`**.
71
-
72
- ## Cursor rule files
7
+ | What | Where |
8
+ |------|--------|
9
+ | Current work | `docs/tasks/TASK-NNN-*.md` |
10
+ | Project memory | `docs/project/05-current-state.md`, `03-architecture.md`, `06-decisions.md` |
11
+ | Change history | Git commits on task branches |
12
+ | **Not** trusted | Cursor chat, Slack |
73
13
 
74
- | File | Contents |
75
- | ----------------------------------------------------- | --------------------------------------------------------- |
76
- | `.cursor/rules/00-project-governance.mdc` | Source of truth, one-TASK rule, MVP scope |
77
- | `.cursor/rules/01-agent-orchestration.mdc` | Roles of the 8 agents and how they collaborate |
78
- | `.cursor/rules/02-task-workflow.mdc` | How to start / run / finish a TASK, dry-run first |
79
- | `.cursor/rules/03-git-safety.mdc` | Branch / rollback safety, no force-push |
80
- | `.cursor/rules/04-docs-update.mdc` | Mandatory doc updates after implementation |
14
+ ## Before you code
81
15
 
82
- ## Completion report
16
+ 1. `docs/project/05-current-state.md`
17
+ 2. `docs/project/03-architecture.md` and `06-decisions.md` when relevant
18
+ 3. **The full current TASK file** under `docs/tasks/`
83
19
 
84
- When you finish a TASK, include at least the following in your chat response:
20
+ ## Workflow (human + CLI)
85
21
 
86
- 1. **TASK ID** (for example `TASK-001`).
87
- 2. **Summary** — 2 to 4 sentences on what was achieved.
88
- 3. **Changed files** — list of the main paths.
89
- 4. **Verification** — the commands you ran and their results.
90
- 5. **Doc updates** — confirm you updated `05-current-state.md`, the TASK's Result / Test Result, and `docs/logs/YYYY-MM-DD.md`.
22
+ ```bash
23
+ vibeops init # once
24
+ vibeops task add # new TASK + branch
25
+ # Plan in Cursor Ask: @docs/tasks/TASK-NNN-….md
26
+ # Build in Cursor Agent: same file + Scope / Acceptance Criteria
27
+ vibeops task done # close TASK, update project docs, merge
28
+ vibeops status # briefing
29
+ ```
91
30
 
92
- Do not end the report with just "done".
31
+ ## Rules
93
32
 
94
- ## VibeOps metadata
33
+ - **One TASK at a time** — stay inside Scope and Acceptance Criteria.
34
+ - **Search** before adding files; reuse existing patterns.
35
+ - When the TASK is finished, **Result** and **Test Result** must be filled before `vibeops task done`.
95
36
 
96
- - Project name: `{{PROJECT_NAME}}`
97
- - Bootstrapped by: VibeOps `{{VIBEOPS_VERSION}}`
98
- - Created at: `{{CREATED_AT}}`
37
+ Details: `.cursor/rules/`.
@@ -1,38 +1,7 @@
1
- # Daily Logs
1
+ # Daily logs
2
2
 
3
- This directory follows a **one-file-per-day** policy.
3
+ Append-only files: `docs/logs/YYYY-MM-DD.md`.
4
4
 
5
- - Filename: `YYYY-MM-DD.md` (local date).
6
- - Create the file on the day's first change. Additional changes the same day append entries to the same file.
7
- - Logs are **fact-first**: decision summary, changed files, verification result, next work.
5
+ `vibeops task done` may append a short entry when a TASK closes.
8
6
 
9
- ## Entry template
10
-
11
- ```
12
- ## YYYY-MM-DD · TASK-NNN <short title>
13
-
14
- ### Decision summary
15
-
16
- - ...
17
-
18
- ### Changed files
19
-
20
- - new: ...
21
- - update: ...
22
-
23
- ### Verification
24
-
25
- | Command | Result |
26
- | --- | --- |
27
- | `...` | ... |
28
-
29
- ### Next work
30
-
31
- - TASK-NNN — ...
32
- ```
33
-
34
- ## Non-goals
35
-
36
- - Chitchat, opinions, self-praise.
37
- - Copy-pasted TASK bodies.
38
- - Secret values that should not be committed.
7
+ Keep entries factual: TASK id, paths touched, commands run.