@goodtek/vibeops 0.4.0 → 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 (106) hide show
  1. package/CHANGELOG.md +198 -1
  2. package/README.md +62 -402
  3. package/dist/bootstrap/installer.js +4 -16
  4. package/dist/cli.js +52 -244
  5. package/dist/commands/init.js +3 -3
  6. package/dist/commands/status.js +145 -8
  7. package/dist/commands/task-add.js +100 -0
  8. package/dist/commands/task-done.js +117 -74
  9. package/dist/lib/config.js +2 -118
  10. package/dist/lib/git.js +190 -8
  11. package/dist/lib/llm-complete.js +62 -0
  12. package/dist/lib/paths.js +7 -3
  13. package/dist/lib/plan-llm-openai.js +0 -63
  14. package/dist/lib/resolve-task.js +34 -0
  15. package/dist/lib/slug.js +11 -0
  16. package/dist/lib/task-add-llm.js +81 -0
  17. package/dist/lib/task-context.js +68 -0
  18. package/dist/lib/task-done-llm.js +148 -0
  19. package/dist/lib/task-merge.js +115 -0
  20. package/dist/lib/task-scaffold.js +70 -92
  21. package/dist/lib/task-start.js +72 -0
  22. package/dist/lib/task-summary.js +12 -239
  23. package/dist/lib/task.js +80 -37
  24. package/dist/types/config.js +0 -16
  25. package/package.json +3 -4
  26. package/templates/.cursor/rules/01-task-source-of-truth.mdc +12 -0
  27. package/templates/.cursor/rules/02-git-safety.mdc +11 -0
  28. package/templates/.cursor/rules/03-docs-on-done.mdc +13 -0
  29. package/templates/.cursor/skills/implement-task/SKILL.md +22 -0
  30. package/templates/.cursor/skills/plan-task/SKILL.md +19 -0
  31. package/templates/AGENTS.md +26 -87
  32. package/templates/docs/logs/README.md +4 -35
  33. package/templates/docs/project/03-architecture.md +10 -29
  34. package/templates/docs/project/05-current-state.md +7 -27
  35. package/templates/docs/project/06-decisions.md +6 -17
  36. package/templates/docs/tasks/TASK-000-template.md +9 -44
  37. package/dist/agent/loader.js +0 -71
  38. package/dist/agent/prompt.js +0 -66
  39. package/dist/commands/agent-list.js +0 -32
  40. package/dist/commands/agent-prompt.js +0 -59
  41. package/dist/commands/agent-show.js +0 -26
  42. package/dist/commands/github-init.js +0 -554
  43. package/dist/commands/github-status.js +0 -164
  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 -343
  48. package/dist/commands/task-check.js +0 -155
  49. package/dist/commands/task-generate.js +0 -206
  50. package/dist/commands/task-pull.js +0 -277
  51. package/dist/commands/task-rollback.js +0 -174
  52. package/dist/commands/task-start.js +0 -102
  53. package/dist/lib/brief.js +0 -356
  54. package/dist/lib/github-cli.js +0 -247
  55. package/dist/lib/notion-client.js +0 -459
  56. package/dist/lib/notion-discovery.js +0 -671
  57. package/dist/lib/notion-env.js +0 -140
  58. package/dist/lib/notion-mappers.js +0 -148
  59. package/dist/lib/notion-schema.js +0 -272
  60. package/dist/lib/notion-sync.js +0 -337
  61. package/dist/lib/notion-target.js +0 -247
  62. package/dist/lib/package-json.js +0 -133
  63. package/dist/lib/plan-apply-planner.js +0 -131
  64. package/dist/lib/plan-codex-pkce.js +0 -234
  65. package/dist/lib/plan-llm-models.js +0 -237
  66. package/dist/lib/plan-llm-normalize.js +0 -146
  67. package/dist/lib/plan-llm-session.js +0 -682
  68. package/dist/lib/plan-post-apply-setup.js +0 -116
  69. package/dist/lib/project-docs.js +0 -95
  70. package/dist/lib/prompt-builder.js +0 -135
  71. package/dist/lib/task-generator.js +0 -183
  72. package/dist/lib/task-prompt.js +0 -23
  73. package/dist/lib/task-pull.js +0 -354
  74. package/dist/status/collector.js +0 -103
  75. package/dist/status/format.js +0 -177
  76. package/dist/types/brief.js +0 -126
  77. package/templates/.cursor/rules/00-project-governance.mdc +0 -28
  78. package/templates/.cursor/rules/01-agent-orchestration.mdc +0 -48
  79. package/templates/.cursor/rules/02-task-workflow.mdc +0 -38
  80. package/templates/.cursor/rules/03-git-safety.mdc +0 -30
  81. package/templates/.cursor/rules/04-docs-update.mdc +0 -22
  82. package/templates/.vibeops/agents/architect.md +0 -47
  83. package/templates/.vibeops/agents/builder.md +0 -38
  84. package/templates/.vibeops/agents/docs.md +0 -54
  85. package/templates/.vibeops/agents/orchestrator.md +0 -40
  86. package/templates/.vibeops/agents/planner.md +0 -60
  87. package/templates/.vibeops/agents/recovery.md +0 -49
  88. package/templates/.vibeops/agents/reviewer.md +0 -47
  89. package/templates/.vibeops/agents/tester.md +0 -43
  90. package/templates/.vibeops/prompts/create-plan.md +0 -33
  91. package/templates/.vibeops/prompts/generate-tasks.md +0 -41
  92. package/templates/.vibeops/prompts/implement-task.md +0 -39
  93. package/templates/.vibeops/prompts/review-task.md +0 -34
  94. package/templates/.vibeops/prompts/rollback.md +0 -32
  95. package/templates/.vibeops/prompts/start-project.md +0 -39
  96. package/templates/.vibeops/workflows/notion-sync.md +0 -53
  97. package/templates/.vibeops/workflows/project-start.md +0 -73
  98. package/templates/.vibeops/workflows/rollback.md +0 -45
  99. package/templates/.vibeops/workflows/task-lifecycle.md +0 -71
  100. package/templates/docs/project/00-overview.md +0 -27
  101. package/templates/docs/project/01-requirements.md +0 -30
  102. package/templates/docs/project/02-mvp-scope.md +0 -36
  103. package/templates/docs/project/04-tech-stack.md +0 -29
  104. package/templates/docs/project/07-backlog.md +0 -23
  105. package/templates/docs/project/08-env.md +0 -29
  106. package/templates/docs/project/09-deployment.md +0 -28
@@ -1,67 +1,32 @@
1
- # TASK-NNN · <one-line title>
2
-
3
- > This file is an example produced from VibeOps' `TASK-000-template.md`.
4
- > Real TASKs are generated by `vibeops task generate`. Filenames follow the `TASK-NNN-<slug>.md` form.
1
+ # TASK-NNN · title
5
2
 
6
3
  ## Status
7
4
 
8
- planned
9
-
10
- ## MVP Phase
11
-
12
- MVP 1 · <phase name>
5
+ Planned
13
6
 
14
7
  ## Goal
15
8
 
16
- <2 to 4 sentences. What becomes possible when this TASK ships.>
17
-
18
- ## Background
19
-
20
- <Why this is needed now. Which prior decisions / TASKs this builds on.>
9
+ (what becomes possible when this TASK ships)
21
10
 
22
11
  ## Scope
23
12
 
24
- - <bullet 1>
25
- - <bullet 2>
13
+ -
26
14
 
27
15
  ## Out of Scope
28
16
 
29
- - <explicit exclusion 1>
30
- - <explicit exclusion 2>
17
+ -
31
18
 
32
19
  ## Acceptance Criteria
33
20
 
34
- 1. <verifiable statement>
35
- 2. <verifiable statement>
36
-
37
- ## Files to Inspect First
38
-
39
- - <path 1>
40
- - <path 2>
41
-
42
- ## Expected Files to Change
43
-
44
- - new: <path>
45
- - update: <path>
46
-
47
- ## Risks
48
-
49
- - <risk 1 and mitigation>
50
- - <risk 2 and mitigation>
21
+ 1.
51
22
 
52
23
  ## Test Plan
53
24
 
54
- - <command / case 1>
55
- - <command / case 2>
56
-
57
- ## Rollback Plan
58
-
59
- - <how to roll back>
25
+ -
60
26
 
61
- ## Implementation Plan
27
+ ## Git Context
62
28
 
63
- 1. <step 1>
64
- 2. <step 2>
29
+ (populated by `vibeops task add`)
65
30
 
66
31
  ## Result
67
32
 
@@ -1,71 +0,0 @@
1
- import { readdir } from "node:fs/promises";
2
- import { basename, extname, join } from "node:path";
3
- import matter from "gray-matter";
4
- import { isDirectory, readText } from "../lib/filesystem.js";
5
- function metaFromFile(filePath, data, body) {
6
- const fallbackName = basename(filePath, extname(filePath));
7
- const name = typeof data["name"] === "string" ? data["name"] : fallbackName;
8
- const role = typeof data["role"] === "string" ? data["role"] : extractFirstHeading(body);
9
- const description = typeof data["description"] === "string" ? data["description"] : undefined;
10
- return { name, role, description, filePath };
11
- }
12
- function extractFirstHeading(body) {
13
- for (const line of body.split("\n")) {
14
- const m = /^#\s+(.*)$/.exec(line.trim());
15
- if (m)
16
- return m[1].trim();
17
- }
18
- return "(no role)";
19
- }
20
- function frontmatterSlice(raw) {
21
- if (!raw.startsWith("---"))
22
- return "";
23
- const end = raw.indexOf("\n---", 3);
24
- if (end < 0)
25
- return "";
26
- return raw.slice(0, end + 4);
27
- }
28
- export async function loadAgent(filePath) {
29
- const raw = await readText(filePath);
30
- const parsed = matter(raw);
31
- const meta = metaFromFile(filePath, parsed.data, parsed.content);
32
- return {
33
- meta,
34
- body: parsed.content.trimStart(),
35
- rawFrontmatter: frontmatterSlice(raw),
36
- raw,
37
- };
38
- }
39
- export async function listAgents(agentsDir) {
40
- if (!(await isDirectory(agentsDir)))
41
- return [];
42
- const entries = await readdir(agentsDir, { withFileTypes: true });
43
- const files = entries
44
- .filter((e) => e.isFile() && e.name.endsWith(".md"))
45
- .map((e) => join(agentsDir, e.name))
46
- .sort();
47
- const records = [];
48
- for (const f of files) {
49
- try {
50
- records.push(await loadAgent(f));
51
- }
52
- catch {
53
- // skip malformed agent files
54
- }
55
- }
56
- return records;
57
- }
58
- export async function findAgent(agentsDir, name) {
59
- const all = await listAgents(agentsDir);
60
- const target = name.toLowerCase();
61
- for (const a of all) {
62
- if (a.meta.name.toLowerCase() === target)
63
- return a;
64
- }
65
- for (const a of all) {
66
- const filenameKey = basename(a.meta.filePath, extname(a.meta.filePath)).toLowerCase();
67
- if (filenameKey === target)
68
- return a;
69
- }
70
- return null;
71
- }
@@ -1,66 +0,0 @@
1
- import { relative } from "node:path";
2
- import { readText } from "../lib/filesystem.js";
3
- function relOrAbs(root, p) {
4
- const r = relative(root, p);
5
- return r === "" ? "." : r.startsWith("..") ? p : r;
6
- }
7
- function header(inputs) {
8
- const lines = [];
9
- lines.push(`# Cursor prompt — agent: ${inputs.agent.meta.name}`);
10
- lines.push("");
11
- if (inputs.config) {
12
- lines.push(`Project: \`${inputs.config.name}\``);
13
- lines.push(`VibeOps: \`${inputs.config.vibeopsVersion}\``);
14
- }
15
- else {
16
- lines.push("Project: (no .vibeops.json found — running outside a VibeOps project)");
17
- }
18
- if (inputs.task) {
19
- lines.push(`TASK: \`${inputs.task.meta.id}\``);
20
- lines.push(`TASK file: \`${relOrAbs(inputs.projectRoot, inputs.task.meta.filePath)}\``);
21
- }
22
- lines.push("");
23
- return lines.join("\n");
24
- }
25
- function agentSection(agent) {
26
- return `## Agent definition (${agent.meta.name})\n\n${agent.body.trim()}\n`;
27
- }
28
- function taskSection(task) {
29
- return `## TASK file content\n\n${task.body.trim()}\n`;
30
- }
31
- async function contextSection(paths, projectRoot) {
32
- if (paths.length === 0)
33
- return "";
34
- const parts = ["## Extra context\n"];
35
- for (const p of paths) {
36
- try {
37
- const text = await readText(p);
38
- parts.push(`### ${relOrAbs(projectRoot, p)}\n\n\`\`\`\n${text}\n\`\`\`\n`);
39
- }
40
- catch {
41
- parts.push(`### ${relOrAbs(projectRoot, p)}\n\n_(could not read file)_\n`);
42
- }
43
- }
44
- return parts.join("\n");
45
- }
46
- const FOOTER = `---
47
-
48
- Apply the **Role / Inputs / Output Format / Rules / Forbidden** sections of the agent definition above as-is.
49
- Your output must be a single markdown blob that pastes cleanly into the Cursor chat.
50
-
51
- When done, follow the "Completion report" format in \`AGENTS.md\`:
52
- TASK ID · summary · changed files · verification · doc updates (05-current-state / TASK / docs/logs).
53
- `;
54
- export async function buildPrompt(inputs) {
55
- const parts = [
56
- header(inputs),
57
- agentSection(inputs.agent),
58
- ];
59
- if (inputs.task)
60
- parts.push(taskSection(inputs.task));
61
- const extra = await contextSection(inputs.contextPaths ?? [], inputs.projectRoot);
62
- if (extra.length > 0)
63
- parts.push(extra);
64
- parts.push(FOOTER);
65
- return parts.join("\n");
66
- }
@@ -1,32 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { listAgents } from "../agent/loader.js";
3
- import { bold, dim, log } from "../lib/logger.js";
4
- import { projectPaths } from "../lib/paths.js";
5
- export async function agentListCommand(options = {}) {
6
- const cwd = resolve(options.cwd ?? process.cwd());
7
- const paths = projectPaths(cwd);
8
- const agents = await listAgents(paths.vibeopsAgents);
9
- if (options.json) {
10
- const payload = agents.map((a) => ({
11
- name: a.meta.name,
12
- role: a.meta.role,
13
- description: a.meta.description ?? null,
14
- filePath: a.meta.filePath,
15
- }));
16
- log.raw(`${JSON.stringify(payload, null, 2)}\n`);
17
- return;
18
- }
19
- if (agents.length === 0) {
20
- log.warn(`No agents found in ${paths.vibeopsAgents}`);
21
- log.info(dim("Run `vibeops init` first."));
22
- process.exitCode = 1;
23
- return;
24
- }
25
- log.info(bold("Agents"));
26
- const nameWidth = Math.max(...agents.map((a) => a.meta.name.length));
27
- for (const a of agents) {
28
- const padded = a.meta.name.padEnd(nameWidth);
29
- const summary = a.meta.description ?? a.meta.role;
30
- log.info(` ${padded} ${dim(summary)}`);
31
- }
32
- }
@@ -1,59 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { findAgent, listAgents } from "../agent/loader.js";
3
- import { buildPrompt } from "../agent/prompt.js";
4
- import { readConfig } from "../lib/config.js";
5
- import { readText } from "../lib/filesystem.js";
6
- import { log } from "../lib/logger.js";
7
- import { projectPaths } from "../lib/paths.js";
8
- import { readTaskFile, scanTasks } from "../lib/task.js";
9
- async function locateTaskFile(tasksDir, taskId) {
10
- const all = await scanTasks(tasksDir);
11
- const target = taskId.toUpperCase();
12
- for (const t of all) {
13
- if (t.id.toUpperCase() === target)
14
- return t.filePath;
15
- }
16
- return null;
17
- }
18
- export async function agentPromptCommand(name, taskId, options = {}) {
19
- const cwd = resolve(options.cwd ?? process.cwd());
20
- const paths = projectPaths(cwd);
21
- const agent = await findAgent(paths.vibeopsAgents, name);
22
- if (!agent) {
23
- const available = (await listAgents(paths.vibeopsAgents)).map((a) => a.meta.name);
24
- log.error(`Unknown agent: "${name}".`);
25
- if (available.length > 0) {
26
- log.info(`Available: ${available.join(", ")}`);
27
- }
28
- else {
29
- log.info(`No agents installed in ${paths.vibeopsAgents}. Run \`vibeops init\` first.`);
30
- }
31
- process.exitCode = 1;
32
- return;
33
- }
34
- const config = await readConfig(paths.root);
35
- let task;
36
- const looksLikeTaskId = /^TASK-\d+/i.test(taskId);
37
- if (looksLikeTaskId) {
38
- const filePath = await locateTaskFile(paths.docsTasks, taskId);
39
- if (!filePath) {
40
- log.error(`TASK not found: ${taskId} (looked in ${paths.docsTasks})`);
41
- process.exitCode = 1;
42
- return;
43
- }
44
- const meta = await readTaskFile(filePath);
45
- const body = await readText(filePath);
46
- task = { meta, body };
47
- }
48
- else if (taskId !== "(unspecified)" && taskId.length > 0) {
49
- log.warn(`"${taskId}" does not look like a TASK id (expected TASK-NNN). Continuing without TASK context.`);
50
- }
51
- const prompt = await buildPrompt({
52
- agent,
53
- config,
54
- task,
55
- projectRoot: paths.root,
56
- contextPaths: options.context ?? [],
57
- });
58
- log.raw(prompt.endsWith("\n") ? prompt : `${prompt}\n`);
59
- }
@@ -1,26 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { findAgent, listAgents } from "../agent/loader.js";
3
- import { log } from "../lib/logger.js";
4
- import { projectPaths } from "../lib/paths.js";
5
- export async function agentShowCommand(name, options = {}) {
6
- const cwd = resolve(options.cwd ?? process.cwd());
7
- const paths = projectPaths(cwd);
8
- const agent = await findAgent(paths.vibeopsAgents, name);
9
- if (!agent) {
10
- const available = (await listAgents(paths.vibeopsAgents)).map((a) => a.meta.name);
11
- log.error(`Unknown agent: "${name}".`);
12
- if (available.length > 0) {
13
- log.info(`Available: ${available.join(", ")}`);
14
- }
15
- else {
16
- log.info(`No agents installed in ${paths.vibeopsAgents}. Run \`vibeops init\` first.`);
17
- }
18
- process.exitCode = 1;
19
- return;
20
- }
21
- if (options.raw) {
22
- log.raw(agent.raw.endsWith("\n") ? agent.raw : `${agent.raw}\n`);
23
- return;
24
- }
25
- log.raw(agent.body.endsWith("\n") ? agent.body : `${agent.body}\n`);
26
- }