@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.
- package/CHANGELOG.md +198 -1
- package/README.md +62 -402
- package/dist/bootstrap/installer.js +4 -16
- package/dist/cli.js +52 -244
- package/dist/commands/init.js +3 -3
- package/dist/commands/status.js +145 -8
- package/dist/commands/task-add.js +100 -0
- package/dist/commands/task-done.js +117 -74
- package/dist/lib/config.js +2 -118
- package/dist/lib/git.js +190 -8
- package/dist/lib/llm-complete.js +62 -0
- package/dist/lib/paths.js +7 -3
- package/dist/lib/plan-llm-openai.js +0 -63
- package/dist/lib/resolve-task.js +34 -0
- package/dist/lib/slug.js +11 -0
- package/dist/lib/task-add-llm.js +81 -0
- package/dist/lib/task-context.js +68 -0
- package/dist/lib/task-done-llm.js +148 -0
- package/dist/lib/task-merge.js +115 -0
- package/dist/lib/task-scaffold.js +70 -92
- package/dist/lib/task-start.js +72 -0
- package/dist/lib/task-summary.js +12 -239
- package/dist/lib/task.js +80 -37
- package/dist/types/config.js +0 -16
- package/package.json +3 -4
- package/templates/.cursor/rules/01-task-source-of-truth.mdc +12 -0
- package/templates/.cursor/rules/02-git-safety.mdc +11 -0
- package/templates/.cursor/rules/03-docs-on-done.mdc +13 -0
- package/templates/.cursor/skills/implement-task/SKILL.md +22 -0
- package/templates/.cursor/skills/plan-task/SKILL.md +19 -0
- package/templates/AGENTS.md +26 -87
- package/templates/docs/logs/README.md +4 -35
- package/templates/docs/project/03-architecture.md +10 -29
- package/templates/docs/project/05-current-state.md +7 -27
- package/templates/docs/project/06-decisions.md +6 -17
- package/templates/docs/tasks/TASK-000-template.md +9 -44
- package/dist/agent/loader.js +0 -71
- package/dist/agent/prompt.js +0 -66
- package/dist/commands/agent-list.js +0 -32
- package/dist/commands/agent-prompt.js +0 -59
- package/dist/commands/agent-show.js +0 -26
- package/dist/commands/github-init.js +0 -554
- package/dist/commands/github-status.js +0 -164
- package/dist/commands/notion-init.js +0 -764
- package/dist/commands/notion-sync.js +0 -405
- package/dist/commands/notion-test.js +0 -595
- package/dist/commands/plan.js +0 -343
- package/dist/commands/task-check.js +0 -155
- package/dist/commands/task-generate.js +0 -206
- package/dist/commands/task-pull.js +0 -277
- package/dist/commands/task-rollback.js +0 -174
- package/dist/commands/task-start.js +0 -102
- package/dist/lib/brief.js +0 -356
- package/dist/lib/github-cli.js +0 -247
- package/dist/lib/notion-client.js +0 -459
- package/dist/lib/notion-discovery.js +0 -671
- package/dist/lib/notion-env.js +0 -140
- package/dist/lib/notion-mappers.js +0 -148
- package/dist/lib/notion-schema.js +0 -272
- package/dist/lib/notion-sync.js +0 -337
- package/dist/lib/notion-target.js +0 -247
- package/dist/lib/package-json.js +0 -133
- package/dist/lib/plan-apply-planner.js +0 -131
- package/dist/lib/plan-codex-pkce.js +0 -234
- package/dist/lib/plan-llm-models.js +0 -237
- package/dist/lib/plan-llm-normalize.js +0 -146
- package/dist/lib/plan-llm-session.js +0 -682
- package/dist/lib/plan-post-apply-setup.js +0 -116
- package/dist/lib/project-docs.js +0 -95
- package/dist/lib/prompt-builder.js +0 -135
- package/dist/lib/task-generator.js +0 -183
- package/dist/lib/task-prompt.js +0 -23
- package/dist/lib/task-pull.js +0 -354
- package/dist/status/collector.js +0 -103
- package/dist/status/format.js +0 -177
- package/dist/types/brief.js +0 -126
- package/templates/.cursor/rules/00-project-governance.mdc +0 -28
- package/templates/.cursor/rules/01-agent-orchestration.mdc +0 -48
- package/templates/.cursor/rules/02-task-workflow.mdc +0 -38
- package/templates/.cursor/rules/03-git-safety.mdc +0 -30
- package/templates/.cursor/rules/04-docs-update.mdc +0 -22
- package/templates/.vibeops/agents/architect.md +0 -47
- package/templates/.vibeops/agents/builder.md +0 -38
- package/templates/.vibeops/agents/docs.md +0 -54
- package/templates/.vibeops/agents/orchestrator.md +0 -40
- package/templates/.vibeops/agents/planner.md +0 -60
- package/templates/.vibeops/agents/recovery.md +0 -49
- package/templates/.vibeops/agents/reviewer.md +0 -47
- package/templates/.vibeops/agents/tester.md +0 -43
- package/templates/.vibeops/prompts/create-plan.md +0 -33
- package/templates/.vibeops/prompts/generate-tasks.md +0 -41
- package/templates/.vibeops/prompts/implement-task.md +0 -39
- package/templates/.vibeops/prompts/review-task.md +0 -34
- package/templates/.vibeops/prompts/rollback.md +0 -32
- package/templates/.vibeops/prompts/start-project.md +0 -39
- package/templates/.vibeops/workflows/notion-sync.md +0 -53
- package/templates/.vibeops/workflows/project-start.md +0 -73
- package/templates/.vibeops/workflows/rollback.md +0 -45
- package/templates/.vibeops/workflows/task-lifecycle.md +0 -71
- package/templates/docs/project/00-overview.md +0 -27
- package/templates/docs/project/01-requirements.md +0 -30
- package/templates/docs/project/02-mvp-scope.md +0 -36
- package/templates/docs/project/04-tech-stack.md +0 -29
- package/templates/docs/project/07-backlog.md +0 -23
- package/templates/docs/project/08-env.md +0 -29
- package/templates/docs/project/09-deployment.md +0 -28
package/dist/lib/github-cli.js
DELETED
|
@@ -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
|
-
}
|