@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,247 +0,0 @@
1
- import { execFile, spawn } from "node:child_process";
2
- import { promisify } from "node:util";
3
- import { runGit } from "./git.js";
4
- const exec = promisify(execFile);
5
- export async function runGh(args) {
6
- try {
7
- const { stdout, stderr } = await exec("gh", [...args], { env: process.env });
8
- return { ok: true, stdout, stderr, exitCode: 0 };
9
- }
10
- catch (err) {
11
- const e = err;
12
- return {
13
- ok: false,
14
- stdout: e.stdout ?? "",
15
- stderr: e.stderr ?? (typeof e.message === "string" ? e.message : ""),
16
- exitCode: typeof e.code === "number" ? e.code : null,
17
- };
18
- }
19
- }
20
- /** Returns true if `gh --version` exits 0. */
21
- export async function isGhInstalled() {
22
- const res = await runGh(["--version"]);
23
- return res.ok;
24
- }
25
- const USERNAME_RE = /Logged in to (?:github\.com|[\w.-]+) (?:as|account) ([^\s)]+)/i;
26
- export async function ghAuthStatus() {
27
- const installed = await isGhInstalled();
28
- if (!installed) {
29
- return {
30
- installed: false,
31
- authenticated: false,
32
- username: null,
33
- hosts: [],
34
- detail: "gh CLI not installed",
35
- };
36
- }
37
- const res = await runGh(["auth", "status"]);
38
- const text = `${res.stdout}\n${res.stderr}`;
39
- const authenticated = res.ok || /Logged in to/i.test(text);
40
- const userMatch = USERNAME_RE.exec(text);
41
- const hosts = [];
42
- for (const m of text.matchAll(/^([\w.-]+\.com)\s*$/gm)) {
43
- const host = m[1]?.trim();
44
- if (typeof host === "string" && host.length > 0 && !hosts.includes(host)) {
45
- hosts.push(host);
46
- }
47
- }
48
- if (hosts.length === 0 && /github\.com/i.test(text))
49
- hosts.push("github.com");
50
- return {
51
- installed: true,
52
- authenticated,
53
- username: userMatch !== null ? userMatch[1].trim() : null,
54
- hosts,
55
- // Strip any potential token-looking strings defensively.
56
- detail: maskAuthDetail(text),
57
- };
58
- }
59
- function maskAuthDetail(text) {
60
- return text
61
- .replace(/gh[op]_[A-Za-z0-9]{20,}/g, "gh***")
62
- .replace(/ghp_[A-Za-z0-9]{20,}/g, "gh***")
63
- .replace(/ghu_[A-Za-z0-9]{20,}/g, "gh***")
64
- .replace(/(Authorization:\s*Bearer\s+)\S+/gi, "$1***")
65
- .trim();
66
- }
67
- /** Best-effort `gh api user --jq .login`. Returns `null` when gh fails. */
68
- export async function ghCurrentUser() {
69
- const res = await runGh(["api", "user", "--jq", ".login"]);
70
- if (!res.ok)
71
- return null;
72
- const login = res.stdout.trim();
73
- return login.length > 0 ? login : null;
74
- }
75
- export async function ghRepoExists(owner, repo) {
76
- if (owner.length === 0 || repo.length === 0)
77
- return false;
78
- const res = await runGh(["repo", "view", `${owner}/${repo}`, "--json", "name"]);
79
- return res.ok;
80
- }
81
- export function buildGhCreateRepoArgs(input) {
82
- const slug = `${input.owner}/${input.repo}`;
83
- const args = ["repo", "create", slug];
84
- args.push(input.visibility === "public" ? "--public" : "--private");
85
- if (typeof input.source === "string" && input.source.length > 0) {
86
- args.push(`--source=${input.source}`);
87
- }
88
- args.push(`--remote=${input.remote ?? "origin"}`);
89
- if (typeof input.description === "string" && input.description.length > 0) {
90
- args.push("--description", input.description);
91
- }
92
- return args;
93
- }
94
- export async function ghCreateRepo(input) {
95
- const argv = buildGhCreateRepoArgs(input);
96
- const command = `gh ${argv.map(shellSafe).join(" ")}`;
97
- if (input.dryRun === true) {
98
- return { ok: true, command, argv, dryRun: true };
99
- }
100
- const res = await runGh(argv);
101
- return {
102
- ok: res.ok,
103
- command,
104
- argv,
105
- stdout: res.stdout,
106
- stderr: res.stderr,
107
- exitCode: res.exitCode,
108
- dryRun: false,
109
- };
110
- }
111
- /**
112
- * Run `gh auth login` with the user's TTY (stdio inherit). Returns a promise
113
- * resolving with the child's exit code. Caller is responsible for verifying
114
- * `--dry-run` / `--yes` / non-TTY guards before invoking — this helper does
115
- * NOT decide policy.
116
- */
117
- export function ghAuthLoginInteractive() {
118
- return new Promise((resolve) => {
119
- const child = spawn("gh", ["auth", "login"], {
120
- stdio: "inherit",
121
- env: process.env,
122
- });
123
- child.on("exit", (code) => {
124
- resolve(typeof code === "number" ? code : 1);
125
- });
126
- child.on("error", () => {
127
- resolve(127);
128
- });
129
- });
130
- }
131
- const RE_SSH = /^git@github\.com:([^/\s]+)\/([^/\s]+?)(?:\.git)?$/i;
132
- const RE_HTTPS = /^https?:\/\/github\.com\/([^/\s?#]+)\/([^/\s?#]+?)(?:\.git)?(?:[?#].*)?$/i;
133
- const RE_GIT_HTTPS = /^git\+https?:\/\/github\.com\/([^/\s?#]+)\/([^/\s?#]+?)(?:\.git)?(?:[?#].*)?$/i;
134
- const RE_SLUG = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/;
135
- /** Strip a trailing `.git` and any `?...#...` suffix from a repo name. */
136
- function cleanRepoName(name) {
137
- return name.replace(/\.git$/i, "").trim();
138
- }
139
- export function parseGitHubRemote(rawUrl) {
140
- const url = rawUrl.trim();
141
- const empty = {
142
- isGithub: false,
143
- owner: null,
144
- repo: null,
145
- url,
146
- protocol: null,
147
- httpsUrl: null,
148
- gitHttpsUrl: null,
149
- };
150
- if (url.length === 0)
151
- return empty;
152
- let owner = null;
153
- let repo = null;
154
- let protocol = null;
155
- const ssh = RE_SSH.exec(url);
156
- if (ssh !== null) {
157
- owner = ssh[1];
158
- repo = cleanRepoName(ssh[2]);
159
- protocol = "ssh";
160
- }
161
- else {
162
- const gitHttps = RE_GIT_HTTPS.exec(url);
163
- if (gitHttps !== null) {
164
- owner = gitHttps[1];
165
- repo = cleanRepoName(gitHttps[2]);
166
- protocol = "git";
167
- }
168
- else {
169
- const https = RE_HTTPS.exec(url);
170
- if (https !== null) {
171
- owner = https[1];
172
- repo = cleanRepoName(https[2]);
173
- protocol = "https";
174
- }
175
- else {
176
- const slug = RE_SLUG.exec(url);
177
- if (slug !== null) {
178
- owner = slug[1];
179
- repo = cleanRepoName(slug[2]);
180
- protocol = "slug";
181
- }
182
- }
183
- }
184
- }
185
- if (owner === null || repo === null || repo.length === 0)
186
- return empty;
187
- const httpsUrl = `https://github.com/${owner}/${repo}`;
188
- const gitHttpsUrl = `git+https://github.com/${owner}/${repo}.git`;
189
- return {
190
- isGithub: true,
191
- owner,
192
- repo,
193
- url,
194
- protocol,
195
- httpsUrl,
196
- gitHttpsUrl,
197
- };
198
- }
199
- export async function gitRemoteList(cwd) {
200
- try {
201
- const { stdout } = await exec("git", ["remote", "-v"], { cwd });
202
- const out = [];
203
- const seen = new Set();
204
- for (const line of stdout.split(/\r?\n/)) {
205
- const trimmed = line.trim();
206
- if (trimmed.length === 0)
207
- continue;
208
- const m = /^([^\s]+)\s+([^\s]+)\s+\((fetch|push)\)$/.exec(trimmed);
209
- if (m === null)
210
- continue;
211
- const name = m[1];
212
- const url = m[2];
213
- const key = `${name}\t${url}`;
214
- if (seen.has(key))
215
- continue;
216
- seen.add(key);
217
- out.push({ name, url });
218
- }
219
- // Deduplicate to one entry per remote name (prefer fetch).
220
- const byName = new Map();
221
- for (const e of out) {
222
- if (!byName.has(e.name))
223
- byName.set(e.name, e);
224
- }
225
- return Array.from(byName.values());
226
- }
227
- catch {
228
- return [];
229
- }
230
- }
231
- /** Add a new git remote. Throws if remote already exists or git fails. */
232
- export async function gitRemoteAdd(cwd, name, url) {
233
- await runGit(cwd, ["remote", "add", name, url]);
234
- }
235
- /** Update an existing git remote URL. Throws if remote does not exist. */
236
- export async function gitRemoteSetUrl(cwd, name, url) {
237
- await runGit(cwd, ["remote", "set-url", name, url]);
238
- }
239
- /**
240
- * Quote a single argv element for human-readable command preview. Not used
241
- * to actually execute commands — we always go through execFile arg arrays.
242
- */
243
- function shellSafe(s) {
244
- if (/^[\w@/:.+=-]+$/.test(s))
245
- return s;
246
- return `'${s.replace(/'/g, "'\\''")}'`;
247
- }