git-vibe-setup 3.0.3 → 3.1.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/README.md +26 -8
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +102 -19
- package/dist/consumer-starter.d.ts +10 -0
- package/dist/consumer-starter.js +116 -0
- package/dist/github-api.d.ts +2 -0
- package/dist/github-api.js +24 -0
- package/dist/install.d.ts +3 -2
- package/dist/install.js +53 -46
- package/dist/releases.d.ts +10 -0
- package/dist/releases.js +21 -12
- package/package.json +1 -2
- package/templates/.git-vibe/role-group/correctness.md +0 -7
- package/templates/.git-vibe/role-group/maintainability.md +0 -7
- package/templates/.git-vibe/role-group/security.md +0 -7
- package/templates/.github/git-vibe.yml +0 -150
- package/templates/.github/workflows/address-feedback.yml +0 -44
- package/templates/.github/workflows/develop.yml +0 -74
- package/templates/.github/workflows/investigate.yml +0 -44
- package/templates/.github/workflows/materialize.yml +0 -44
- package/templates/.github/workflows/review.yml +0 -44
- package/templates/.github/workflows/validate.yml +0 -51
- package/templates/GITVIBE_AI_ENV_JSON.example.json +0 -8
package/README.md
CHANGED
|
@@ -6,16 +6,34 @@ Local initializer for GitVibe consumer repositories.
|
|
|
6
6
|
npx git-vibe-setup setup
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
The `setup` command
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
The `setup` command fetches `examples/consumer` from the latest stable
|
|
10
|
+
`markhuangai/git-vibe` release, writes `.github` and `.git-vibe` starter files,
|
|
11
|
+
pins reusable workflow refs to that release, and fails before writing if release
|
|
12
|
+
lookup, starter fetch, or target-file validation fails.
|
|
13
|
+
|
|
14
|
+
When `GITHUB_TOKEN` or `GH_TOKEN` is set, `git-vibe-setup` uses it only to
|
|
15
|
+
authenticate GitHub release and starter-file reads. This avoids anonymous API
|
|
16
|
+
throttling in CI and shared-network environments.
|
|
12
17
|
|
|
13
18
|
```bash
|
|
14
19
|
npx git-vibe-setup update
|
|
15
20
|
```
|
|
16
21
|
|
|
17
|
-
The `update` command
|
|
18
|
-
|
|
19
|
-
release. It does not update
|
|
20
|
-
variables, and it refuses to
|
|
21
|
-
GitVibe wrappers.
|
|
22
|
+
The `update` command fetches `examples/consumer` from the latest stable
|
|
23
|
+
`markhuangai/git-vibe` release, rewrites only `.github/workflows/*.yml` GitVibe
|
|
24
|
+
wrapper files, and pins them to that release. It does not update
|
|
25
|
+
`.github/git-vibe.yml`, `.git-vibe`, secrets, or variables, and it refuses to
|
|
26
|
+
overwrite workflow files that do not look like GitVibe wrappers.
|
|
27
|
+
|
|
28
|
+
To test a specific release or prerelease from a consumer repository, pass the
|
|
29
|
+
release tag explicitly:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx git-vibe-setup update --release v3.0.4-rc.1
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To let automatic latest-release lookup choose prereleases, opt in explicitly:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx git-vibe-setup update --include-prereleases
|
|
39
|
+
```
|
package/dist/cli.d.ts
CHANGED
|
@@ -4,8 +4,10 @@ interface SetupCliRuntime {
|
|
|
4
4
|
cwd?: string;
|
|
5
5
|
error?: (message: string) => void;
|
|
6
6
|
fetchImpl?: typeof fetch;
|
|
7
|
+
githubToken?: string;
|
|
8
|
+
includePrereleases?: boolean;
|
|
7
9
|
log?: (message: string) => void;
|
|
8
|
-
|
|
10
|
+
releaseTag?: string;
|
|
9
11
|
}
|
|
10
12
|
export declare function runSetup(runtime?: SetupCliRuntime): Promise<void>;
|
|
11
13
|
export declare function runUpdate(runtime?: SetupCliRuntime): Promise<void>;
|
package/dist/cli.js
CHANGED
|
@@ -3,12 +3,14 @@ import { realpathSync } from "node:fs";
|
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { fetchConsumerStarterFiles } from "./consumer-starter.js";
|
|
7
|
+
import { githubTokenFromEnvironment } from "./github-api.js";
|
|
6
8
|
import { blockingInstallPaths, buildInstallFiles, buildWorkflowUpdateFiles, existingFilesError, installFiles, unmanagedWorkflowUpdateError, unmanagedWorkflowUpdatePaths, updateFiles, } from "./install.js";
|
|
7
9
|
import { renderManualSetupInstructions } from "./instructions.js";
|
|
8
|
-
import {
|
|
10
|
+
import { latestReleaseTag } from "./releases.js";
|
|
9
11
|
const usage = `Usage:
|
|
10
|
-
git-vibe-setup setup
|
|
11
|
-
git-vibe-setup update
|
|
12
|
+
git-vibe-setup setup [--release <tag>] [--include-prereleases]
|
|
13
|
+
git-vibe-setup update [--release <tag>] [--include-prereleases]
|
|
12
14
|
git-vibe-setup
|
|
13
15
|
|
|
14
16
|
Commands:
|
|
@@ -16,12 +18,16 @@ Commands:
|
|
|
16
18
|
update Update GitVibe workflow wrapper files in the current repository.
|
|
17
19
|
|
|
18
20
|
Options:
|
|
19
|
-
|
|
21
|
+
--release <tag> Use a specific GitVibe release tag, including prereleases.
|
|
22
|
+
--include-prereleases Allow latest-release lookup to select prereleases.
|
|
23
|
+
-h, --help Show this help message.`;
|
|
20
24
|
export async function runSetup(runtime = {}) {
|
|
21
25
|
const cwd = runtime.cwd || process.cwd();
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
26
|
+
const fetchImpl = runtime.fetchImpl || fetch;
|
|
27
|
+
const githubToken = runtime.githubToken || githubTokenFromEnvironment();
|
|
28
|
+
const releaseTag = await resolveReleaseTag(runtime, fetchImpl, githubToken);
|
|
29
|
+
const sourceFiles = await fetchConsumerStarterFiles({ fetchImpl, githubToken, releaseTag });
|
|
30
|
+
const files = buildInstallFiles({ cwd, releaseTag, sourceFiles });
|
|
25
31
|
const blockingPaths = blockingInstallPaths(files);
|
|
26
32
|
if (blockingPaths.length > 0)
|
|
27
33
|
throw existingFilesError(blockingPaths, cwd);
|
|
@@ -30,9 +36,11 @@ export async function runSetup(runtime = {}) {
|
|
|
30
36
|
}
|
|
31
37
|
export async function runUpdate(runtime = {}) {
|
|
32
38
|
const cwd = runtime.cwd || process.cwd();
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
39
|
+
const fetchImpl = runtime.fetchImpl || fetch;
|
|
40
|
+
const githubToken = runtime.githubToken || githubTokenFromEnvironment();
|
|
41
|
+
const releaseTag = await resolveReleaseTag(runtime, fetchImpl, githubToken);
|
|
42
|
+
const sourceFiles = await fetchConsumerStarterFiles({ fetchImpl, githubToken, releaseTag });
|
|
43
|
+
const files = buildWorkflowUpdateFiles({ cwd, releaseTag, sourceFiles });
|
|
36
44
|
const unmanagedPaths = unmanagedWorkflowUpdatePaths(files);
|
|
37
45
|
if (unmanagedPaths.length > 0)
|
|
38
46
|
throw unmanagedWorkflowUpdateError(unmanagedPaths, cwd);
|
|
@@ -41,20 +49,33 @@ export async function runUpdate(runtime = {}) {
|
|
|
41
49
|
}
|
|
42
50
|
export async function setupCli(runtime = {}) {
|
|
43
51
|
const argv = runtime.argv || process.argv.slice(2);
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = parseCliOptions(argv);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
+
(runtime.error || console.error)(`${message}\n\n${usage}`);
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
if (parsed.kind === "help") {
|
|
46
62
|
(runtime.log || console.log)(usage);
|
|
47
63
|
return 0;
|
|
48
64
|
}
|
|
49
|
-
if (
|
|
50
|
-
(runtime.error || console.error)(
|
|
65
|
+
if (parsed.kind === "error") {
|
|
66
|
+
(runtime.error || console.error)(`${parsed.message}\n\n${usage}`);
|
|
51
67
|
return 1;
|
|
52
68
|
}
|
|
69
|
+
const commandRuntime = {
|
|
70
|
+
...runtime,
|
|
71
|
+
includePrereleases: runtime.includePrereleases || parsed.options.includePrereleases,
|
|
72
|
+
releaseTag: parsed.options.releaseTag || runtime.releaseTag,
|
|
73
|
+
};
|
|
53
74
|
try {
|
|
54
|
-
if (command === "update")
|
|
55
|
-
await runUpdate(
|
|
75
|
+
if (parsed.options.command === "update")
|
|
76
|
+
await runUpdate(commandRuntime);
|
|
56
77
|
else
|
|
57
|
-
await runSetup(
|
|
78
|
+
await runSetup(commandRuntime);
|
|
58
79
|
return 0;
|
|
59
80
|
}
|
|
60
81
|
catch (error) {
|
|
@@ -63,8 +84,70 @@ export async function setupCli(runtime = {}) {
|
|
|
63
84
|
return 1;
|
|
64
85
|
}
|
|
65
86
|
}
|
|
66
|
-
function
|
|
67
|
-
|
|
87
|
+
async function resolveReleaseTag(runtime, fetchImpl, githubToken) {
|
|
88
|
+
if (runtime.releaseTag)
|
|
89
|
+
return validateReleaseTag(runtime.releaseTag);
|
|
90
|
+
const releaseTag = await latestReleaseTag({
|
|
91
|
+
fetchImpl,
|
|
92
|
+
githubToken,
|
|
93
|
+
includePrereleases: runtime.includePrereleases,
|
|
94
|
+
});
|
|
95
|
+
return validateReleaseTag(releaseTag);
|
|
96
|
+
}
|
|
97
|
+
function parseCliOptions(argv) {
|
|
98
|
+
if (argv.includes("--help") || argv.includes("-h") || argv[0] === "help") {
|
|
99
|
+
return { kind: "help" };
|
|
100
|
+
}
|
|
101
|
+
const command = commandName(argv[0]);
|
|
102
|
+
if (argv[0] && !command && !argv[0].startsWith("-")) {
|
|
103
|
+
return { kind: "error", message: `Unknown command: ${argv[0]}` };
|
|
104
|
+
}
|
|
105
|
+
const options = parseOptionArgs(command ? argv.slice(1) : argv);
|
|
106
|
+
if (options.kind !== "command")
|
|
107
|
+
return options;
|
|
108
|
+
return {
|
|
109
|
+
kind: "command",
|
|
110
|
+
options: {
|
|
111
|
+
...options.options,
|
|
112
|
+
command: command || "setup",
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function parseOptionArgs(args) {
|
|
117
|
+
const options = { includePrereleases: false };
|
|
118
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
119
|
+
const arg = args[index] || "";
|
|
120
|
+
if (arg === "--include-prereleases") {
|
|
121
|
+
options.includePrereleases = true;
|
|
122
|
+
}
|
|
123
|
+
else if (arg === "--release") {
|
|
124
|
+
index += 1;
|
|
125
|
+
const releaseTag = args[index];
|
|
126
|
+
if (!releaseTag || releaseTag.startsWith("-")) {
|
|
127
|
+
return { kind: "error", message: "--release requires a release tag" };
|
|
128
|
+
}
|
|
129
|
+
options.releaseTag = validateReleaseTag(releaseTag);
|
|
130
|
+
}
|
|
131
|
+
else if (arg.startsWith("--release=")) {
|
|
132
|
+
options.releaseTag = validateReleaseTag(arg.slice("--release=".length));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
return { kind: "error", message: `Unknown option: ${arg}` };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { kind: "command", options };
|
|
139
|
+
}
|
|
140
|
+
function commandName(value) {
|
|
141
|
+
if (value === "setup" || value === "update")
|
|
142
|
+
return value;
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
function validateReleaseTag(releaseTag) {
|
|
146
|
+
const trimmed = releaseTag.trim();
|
|
147
|
+
if (/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(trimmed)) {
|
|
148
|
+
return trimmed;
|
|
149
|
+
}
|
|
150
|
+
throw new Error(`Invalid release tag: ${releaseTag}. Release tags must look like v3.0.4 or v3.0.4-rc.1.`);
|
|
68
151
|
}
|
|
69
152
|
/* c8 ignore start */
|
|
70
153
|
if (isDirectRun(import.meta.url)) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ConsumerStarterFile {
|
|
2
|
+
content: string;
|
|
3
|
+
relativePath: string;
|
|
4
|
+
sourcePath: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function fetchConsumerStarterFiles(options: {
|
|
7
|
+
fetchImpl?: typeof fetch;
|
|
8
|
+
githubToken?: string;
|
|
9
|
+
releaseTag: string;
|
|
10
|
+
}): Promise<ConsumerStarterFile[]>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { githubApiHeaders } from "./github-api.js";
|
|
2
|
+
const consumerStarterRoot = "examples/consumer";
|
|
3
|
+
const repositoryContentsUrl = "https://api.github.com/repos/markhuangai/git-vibe/contents";
|
|
4
|
+
const maxStarterFileBytes = 128 * 1024;
|
|
5
|
+
export async function fetchConsumerStarterFiles(options) {
|
|
6
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
7
|
+
const files = await fetchDirectoryFiles(fetchImpl, options.releaseTag, consumerStarterRoot, options.githubToken);
|
|
8
|
+
if (files.length === 0) {
|
|
9
|
+
throw invalidStarterBundleError(options.releaseTag, `${consumerStarterRoot} contains no files`);
|
|
10
|
+
}
|
|
11
|
+
return files.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
12
|
+
}
|
|
13
|
+
async function fetchDirectoryFiles(fetchImpl, releaseTag, path, githubToken) {
|
|
14
|
+
const data = await fetchGitHubJson(fetchImpl, contentsUrl(path, releaseTag), releaseTag, githubToken);
|
|
15
|
+
if (!Array.isArray(data)) {
|
|
16
|
+
throw invalidStarterBundleError(releaseTag, `${path} is not a directory`);
|
|
17
|
+
}
|
|
18
|
+
const fileGroups = await Promise.all(data.map((entry) => fetchEntryFiles(fetchImpl, releaseTag, asContentEntry(entry), githubToken)));
|
|
19
|
+
return fileGroups.flat();
|
|
20
|
+
}
|
|
21
|
+
async function fetchEntryFiles(fetchImpl, releaseTag, entry, githubToken) {
|
|
22
|
+
const path = entryPath(entry, releaseTag);
|
|
23
|
+
relativeConsumerPath(path, releaseTag);
|
|
24
|
+
const type = entryType(entry, releaseTag, path);
|
|
25
|
+
if (type === "dir")
|
|
26
|
+
return fetchDirectoryFiles(fetchImpl, releaseTag, path, githubToken);
|
|
27
|
+
if (type === "file")
|
|
28
|
+
return [await fetchFile(fetchImpl, releaseTag, path, githubToken)];
|
|
29
|
+
throw invalidStarterBundleError(releaseTag, `${path} has unsupported type ${type}`);
|
|
30
|
+
}
|
|
31
|
+
async function fetchFile(fetchImpl, releaseTag, path, githubToken) {
|
|
32
|
+
const data = asContentEntry(await fetchGitHubJson(fetchImpl, contentsUrl(path, releaseTag), releaseTag, githubToken));
|
|
33
|
+
if (data.type !== "file" || typeof data.content !== "string" || data.encoding !== "base64") {
|
|
34
|
+
throw invalidStarterBundleError(releaseTag, `${path} is not a base64 file`);
|
|
35
|
+
}
|
|
36
|
+
const encodedContent = data.content.replace(/\s/g, "");
|
|
37
|
+
if (!/^[A-Za-z0-9+/=]*$/.test(encodedContent)) {
|
|
38
|
+
throw invalidStarterBundleError(releaseTag, `${path} has invalid base64 content`);
|
|
39
|
+
}
|
|
40
|
+
const content = Buffer.from(encodedContent, "base64");
|
|
41
|
+
if (content.byteLength > maxStarterFileBytes) {
|
|
42
|
+
throw invalidStarterBundleError(releaseTag, `${path} is larger than 128KiB`);
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
content: content.toString("utf8"),
|
|
46
|
+
relativePath: relativeConsumerPath(path, releaseTag),
|
|
47
|
+
sourcePath: path,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function fetchGitHubJson(fetchImpl, url, releaseTag, githubToken) {
|
|
51
|
+
let response;
|
|
52
|
+
const headers = githubApiHeaders(githubToken);
|
|
53
|
+
try {
|
|
54
|
+
response = await fetchImpl(url, {
|
|
55
|
+
headers,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw unavailableStarterBundleError(releaseTag);
|
|
60
|
+
}
|
|
61
|
+
if (!response.ok)
|
|
62
|
+
throw unavailableStarterBundleError(releaseTag);
|
|
63
|
+
try {
|
|
64
|
+
return await response.json();
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
throw unavailableStarterBundleError(releaseTag);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function contentsUrl(path, releaseTag) {
|
|
71
|
+
const url = new URL(`${repositoryContentsUrl}/${encodePath(path)}`);
|
|
72
|
+
url.searchParams.set("ref", releaseTag);
|
|
73
|
+
return url;
|
|
74
|
+
}
|
|
75
|
+
function encodePath(path) {
|
|
76
|
+
return path.split("/").map(encodeURIComponent).join("/");
|
|
77
|
+
}
|
|
78
|
+
function asContentEntry(value) {
|
|
79
|
+
return typeof value === "object" && value !== null ? value : {};
|
|
80
|
+
}
|
|
81
|
+
function entryPath(entry, releaseTag) {
|
|
82
|
+
if (typeof entry.path !== "string") {
|
|
83
|
+
throw invalidStarterBundleError(releaseTag, "a content entry is missing path");
|
|
84
|
+
}
|
|
85
|
+
return entry.path;
|
|
86
|
+
}
|
|
87
|
+
function entryType(entry, releaseTag, path) {
|
|
88
|
+
if (typeof entry.type !== "string") {
|
|
89
|
+
throw invalidStarterBundleError(releaseTag, `${path} is missing type`);
|
|
90
|
+
}
|
|
91
|
+
return entry.type;
|
|
92
|
+
}
|
|
93
|
+
function relativeConsumerPath(path, releaseTag) {
|
|
94
|
+
const prefix = `${consumerStarterRoot}/`;
|
|
95
|
+
if (!path.startsWith(prefix)) {
|
|
96
|
+
throw invalidStarterBundleError(releaseTag, `${path} is outside ${consumerStarterRoot}`);
|
|
97
|
+
}
|
|
98
|
+
const relativePath = path.slice(prefix.length);
|
|
99
|
+
if (!isSafeRelativePath(relativePath)) {
|
|
100
|
+
throw invalidStarterBundleError(releaseTag, `${path} has an unsafe relative path`);
|
|
101
|
+
}
|
|
102
|
+
return relativePath;
|
|
103
|
+
}
|
|
104
|
+
function isSafeRelativePath(relativePath) {
|
|
105
|
+
const segments = relativePath.split("/");
|
|
106
|
+
return (relativePath.length > 0 &&
|
|
107
|
+
!relativePath.startsWith("/") &&
|
|
108
|
+
!relativePath.includes("\\") &&
|
|
109
|
+
segments.every((segment) => segment.length > 0 && segment !== "." && segment !== ".."));
|
|
110
|
+
}
|
|
111
|
+
function unavailableStarterBundleError(releaseTag) {
|
|
112
|
+
return new Error(`git-vibe-setup could not fetch the GitVibe consumer starter from markhuangai/git-vibe@${releaseTag} because the GitHub content service is unavailable. No files were written.`);
|
|
113
|
+
}
|
|
114
|
+
function invalidStarterBundleError(releaseTag, detail) {
|
|
115
|
+
return new Error(`git-vibe-setup found an invalid GitVibe consumer starter at markhuangai/git-vibe@${releaseTag}: ${detail}. No files were written.`);
|
|
116
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const defaultGitHubApiHeaders = {
|
|
2
|
+
accept: "application/vnd.github+json",
|
|
3
|
+
"user-agent": "git-vibe-setup",
|
|
4
|
+
"x-github-api-version": "2022-11-28",
|
|
5
|
+
};
|
|
6
|
+
export function githubApiHeaders(githubToken) {
|
|
7
|
+
const headers = { ...defaultGitHubApiHeaders };
|
|
8
|
+
const token = normalizeGitHubToken(githubToken);
|
|
9
|
+
if (token)
|
|
10
|
+
headers.authorization = `Bearer ${token}`;
|
|
11
|
+
return headers;
|
|
12
|
+
}
|
|
13
|
+
export function githubTokenFromEnvironment(env = process.env) {
|
|
14
|
+
return normalizeGitHubToken(env.GITHUB_TOKEN) || normalizeGitHubToken(env.GH_TOKEN);
|
|
15
|
+
}
|
|
16
|
+
function normalizeGitHubToken(githubToken) {
|
|
17
|
+
const token = githubToken?.trim();
|
|
18
|
+
if (!token)
|
|
19
|
+
return undefined;
|
|
20
|
+
if (/[\r\n]/.test(token)) {
|
|
21
|
+
throw new Error("git-vibe-setup found an invalid GitHub token value. No files were written.");
|
|
22
|
+
}
|
|
23
|
+
return token;
|
|
24
|
+
}
|
package/dist/install.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ConsumerStarterFile } from "./consumer-starter.js";
|
|
1
2
|
export interface InstallFile {
|
|
2
3
|
content: string;
|
|
3
4
|
sourcePath: string;
|
|
@@ -6,12 +7,12 @@ export interface InstallFile {
|
|
|
6
7
|
export declare function buildInstallFiles(options: {
|
|
7
8
|
cwd: string;
|
|
8
9
|
releaseTag: string;
|
|
9
|
-
|
|
10
|
+
sourceFiles: ConsumerStarterFile[];
|
|
10
11
|
}): InstallFile[];
|
|
11
12
|
export declare function buildWorkflowUpdateFiles(options: {
|
|
12
13
|
cwd: string;
|
|
13
14
|
releaseTag: string;
|
|
14
|
-
|
|
15
|
+
sourceFiles: ConsumerStarterFile[];
|
|
15
16
|
}): InstallFile[];
|
|
16
17
|
export declare function blockingInstallPaths(files: InstallFile[]): string[];
|
|
17
18
|
export declare function unmanagedWorkflowUpdatePaths(files: InstallFile[]): string[];
|
package/dist/install.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import { existsSync, mkdirSync,
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { basename, dirname, join, relative } from "node:path";
|
|
3
|
+
const requiredInstallSourcePaths = [
|
|
4
|
+
".github/git-vibe.yml",
|
|
5
|
+
".github/workflows/address-feedback.yml",
|
|
6
|
+
".github/workflows/develop.yml",
|
|
7
|
+
".github/workflows/investigate.yml",
|
|
8
|
+
".github/workflows/materialize.yml",
|
|
9
|
+
".github/workflows/review.yml",
|
|
10
|
+
".github/workflows/validate.yml",
|
|
11
|
+
".git-vibe/role-group/correctness.md",
|
|
12
|
+
".git-vibe/role-group/maintainability.md",
|
|
13
|
+
".git-vibe/role-group/security.md",
|
|
14
|
+
];
|
|
15
|
+
const requiredWorkflowSourcePaths = requiredInstallSourcePaths.filter(isWorkflowSourcePath);
|
|
3
16
|
export function buildInstallFiles(options) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
return {
|
|
9
|
-
content: pinWorkflowReleaseRefs(content, options.releaseTag),
|
|
10
|
-
sourcePath,
|
|
11
|
-
targetPath,
|
|
12
|
-
};
|
|
13
|
-
}));
|
|
17
|
+
requireSourcePaths(options.sourceFiles, requiredInstallSourcePaths, options.releaseTag);
|
|
18
|
+
return options.sourceFiles
|
|
19
|
+
.filter((file) => isInstallSourcePath(file.relativePath))
|
|
20
|
+
.map((file) => buildInstallFile(file, options.cwd, options.releaseTag));
|
|
14
21
|
}
|
|
15
22
|
export function buildWorkflowUpdateFiles(options) {
|
|
16
|
-
|
|
17
|
-
return
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const content = readFileSync(sourcePath, "utf8");
|
|
21
|
-
return {
|
|
22
|
-
content: pinWorkflowReleaseRefs(content, options.releaseTag),
|
|
23
|
-
sourcePath,
|
|
24
|
-
targetPath,
|
|
25
|
-
};
|
|
26
|
-
});
|
|
23
|
+
requireSourcePaths(options.sourceFiles, requiredWorkflowSourcePaths, options.releaseTag);
|
|
24
|
+
return options.sourceFiles
|
|
25
|
+
.filter((file) => isWorkflowSourcePath(file.relativePath))
|
|
26
|
+
.map((file) => buildInstallFile(file, options.cwd, options.releaseTag));
|
|
27
27
|
}
|
|
28
28
|
export function blockingInstallPaths(files) {
|
|
29
29
|
return files
|
|
@@ -78,18 +78,6 @@ export function unmanagedWorkflowUpdateError(paths, cwd) {
|
|
|
78
78
|
export function pinWorkflowReleaseRefs(content, releaseTag) {
|
|
79
79
|
return content.replace(/(uses:\s*markhuangai\/git-vibe\/\.github\/workflows\/[^\s@]+)@[^\s]+/g, (_match, workflowReference) => `${workflowReference}@${releaseTag}`);
|
|
80
80
|
}
|
|
81
|
-
function installSources(repositoryRoot) {
|
|
82
|
-
return [
|
|
83
|
-
{
|
|
84
|
-
sourceDirectory: join(repositoryRoot, "templates", ".github"),
|
|
85
|
-
targetDirectory: ".github",
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
sourceDirectory: join(repositoryRoot, "templates", ".git-vibe"),
|
|
89
|
-
targetDirectory: ".git-vibe",
|
|
90
|
-
},
|
|
91
|
-
];
|
|
92
|
-
}
|
|
93
81
|
function isManagedWorkflowTarget(file) {
|
|
94
82
|
try {
|
|
95
83
|
const workflowName = basename(file.targetPath);
|
|
@@ -105,18 +93,6 @@ function managedWorkflowPattern(workflowName) {
|
|
|
105
93
|
function escapeRegExp(value) {
|
|
106
94
|
return value.replace(/[.*+?^${}()|[\]\\]/g, String.raw `\$&`);
|
|
107
95
|
}
|
|
108
|
-
function listRelativeFiles(directory) {
|
|
109
|
-
return readdirSync(directory, { withFileTypes: true })
|
|
110
|
-
.flatMap((entry) => {
|
|
111
|
-
const entryPath = join(directory, entry.name);
|
|
112
|
-
if (entry.isDirectory()) {
|
|
113
|
-
return listRelativeFiles(entryPath).map((path) => join(entry.name, path));
|
|
114
|
-
}
|
|
115
|
-
/* c8 ignore next */
|
|
116
|
-
return entry.isFile() ? [entry.name] : [];
|
|
117
|
-
})
|
|
118
|
-
.sort();
|
|
119
|
-
}
|
|
120
96
|
function snapshotFile(targetPath) {
|
|
121
97
|
if (!existsSync(targetPath))
|
|
122
98
|
return { existed: false, targetPath };
|
|
@@ -167,3 +143,34 @@ function rollbackInstall(createdFiles, createdDirectories) {
|
|
|
167
143
|
rmSync(directory, { force: true, recursive: false });
|
|
168
144
|
}
|
|
169
145
|
}
|
|
146
|
+
function buildInstallFile(sourceFile, cwd, releaseTag) {
|
|
147
|
+
return {
|
|
148
|
+
content: pinWorkflowReleaseRefs(sourceFile.content, releaseTag),
|
|
149
|
+
sourcePath: sourceFile.sourcePath,
|
|
150
|
+
targetPath: join(cwd, safeRelativePath(sourceFile.relativePath)),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function requireSourcePaths(sourceFiles, requiredPaths, releaseTag) {
|
|
154
|
+
const sourcePaths = new Set(sourceFiles.map((file) => file.relativePath));
|
|
155
|
+
const missingPaths = requiredPaths.filter((path) => !sourcePaths.has(path));
|
|
156
|
+
if (missingPaths.length === 0)
|
|
157
|
+
return;
|
|
158
|
+
const listed = missingPaths.map((path) => `- examples/consumer/${path}`).join("\n");
|
|
159
|
+
throw new Error(`git-vibe-setup found an incomplete GitVibe consumer starter at markhuangai/git-vibe@${releaseTag}. Missing files:\n${listed}\nNo files were written.`);
|
|
160
|
+
}
|
|
161
|
+
function isInstallSourcePath(relativePath) {
|
|
162
|
+
return relativePath.startsWith(".github/") || relativePath.startsWith(".git-vibe/");
|
|
163
|
+
}
|
|
164
|
+
function isWorkflowSourcePath(relativePath) {
|
|
165
|
+
return relativePath.startsWith(".github/workflows/") && relativePath.endsWith(".yml");
|
|
166
|
+
}
|
|
167
|
+
function safeRelativePath(relativePath) {
|
|
168
|
+
const segments = relativePath.split("/");
|
|
169
|
+
const isSafe = relativePath.length > 0 &&
|
|
170
|
+
!relativePath.startsWith("/") &&
|
|
171
|
+
!relativePath.includes("\\") &&
|
|
172
|
+
segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
173
|
+
if (isSafe)
|
|
174
|
+
return relativePath;
|
|
175
|
+
throw new Error(`git-vibe-setup found an unsafe starter file path: ${relativePath}`);
|
|
176
|
+
}
|
package/dist/releases.d.ts
CHANGED
|
@@ -7,5 +7,15 @@ export interface GitHubRelease {
|
|
|
7
7
|
}
|
|
8
8
|
export declare class ReleaseLookupError extends Error {
|
|
9
9
|
}
|
|
10
|
+
export interface ReleaseLookupOptions {
|
|
11
|
+
fetchImpl?: typeof fetch;
|
|
12
|
+
githubToken?: string;
|
|
13
|
+
includePrereleases?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface ReleaseSelectionOptions {
|
|
16
|
+
includePrereleases?: boolean;
|
|
17
|
+
}
|
|
10
18
|
export declare function latestStableReleaseTag(fetchImpl?: typeof fetch): Promise<string>;
|
|
19
|
+
export declare function latestReleaseTag(options?: ReleaseLookupOptions): Promise<string>;
|
|
11
20
|
export declare function selectLatestStableRelease(releases: GitHubRelease[]): GitHubRelease | undefined;
|
|
21
|
+
export declare function selectLatestRelease(releases: GitHubRelease[], options?: ReleaseSelectionOptions): GitHubRelease | undefined;
|
package/dist/releases.js
CHANGED
|
@@ -1,38 +1,42 @@
|
|
|
1
|
+
import { githubApiHeaders } from "./github-api.js";
|
|
1
2
|
export class ReleaseLookupError extends Error {
|
|
2
3
|
}
|
|
3
4
|
const releasesUrl = "https://api.github.com/repos/markhuangai/git-vibe/releases";
|
|
4
5
|
const releasesPerPage = 100;
|
|
5
6
|
export async function latestStableReleaseTag(fetchImpl = fetch) {
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
return latestReleaseTag({ fetchImpl });
|
|
8
|
+
}
|
|
9
|
+
export async function latestReleaseTag(options = {}) {
|
|
10
|
+
const releases = await fetchReleases(options.fetchImpl || fetch, options.githubToken);
|
|
11
|
+
const release = selectLatestRelease(releases, options);
|
|
8
12
|
if (!release?.tag_name) {
|
|
9
|
-
throw new ReleaseLookupError(
|
|
13
|
+
throw new ReleaseLookupError(`git-vibe-setup could not check the latest GitVibe update because ${missingReleaseReason(options)}. No files were written.`);
|
|
10
14
|
}
|
|
11
15
|
return release.tag_name;
|
|
12
16
|
}
|
|
13
17
|
export function selectLatestStableRelease(releases) {
|
|
18
|
+
return selectLatestRelease(releases);
|
|
19
|
+
}
|
|
20
|
+
export function selectLatestRelease(releases, options = {}) {
|
|
14
21
|
return releases
|
|
15
|
-
.filter((release) => !release.draft && !release.prerelease && release.tag_name)
|
|
22
|
+
.filter((release) => !release.draft && (options.includePrereleases || !release.prerelease) && release.tag_name)
|
|
16
23
|
.sort(compareReleaseFreshness)[0];
|
|
17
24
|
}
|
|
18
|
-
async function fetchReleases(fetchImpl) {
|
|
25
|
+
async function fetchReleases(fetchImpl, githubToken) {
|
|
19
26
|
const releases = [];
|
|
20
27
|
for (let page = 1;; page += 1) {
|
|
21
|
-
const data = await fetchReleasePage(fetchImpl, page);
|
|
28
|
+
const data = await fetchReleasePage(fetchImpl, page, githubToken);
|
|
22
29
|
releases.push(...data);
|
|
23
30
|
if (data.length < releasesPerPage)
|
|
24
31
|
return releases;
|
|
25
32
|
}
|
|
26
33
|
}
|
|
27
|
-
async function fetchReleasePage(fetchImpl, page) {
|
|
34
|
+
async function fetchReleasePage(fetchImpl, page, githubToken) {
|
|
28
35
|
let response;
|
|
36
|
+
const headers = githubApiHeaders(githubToken);
|
|
29
37
|
try {
|
|
30
38
|
response = await fetchImpl(releasePageUrl(page), {
|
|
31
|
-
headers
|
|
32
|
-
accept: "application/vnd.github+json",
|
|
33
|
-
"user-agent": "git-vibe-setup",
|
|
34
|
-
"x-github-api-version": "2022-11-28",
|
|
35
|
-
},
|
|
39
|
+
headers,
|
|
36
40
|
});
|
|
37
41
|
}
|
|
38
42
|
catch {
|
|
@@ -63,3 +67,8 @@ function releaseTime(release) {
|
|
|
63
67
|
function unavailableReleaseError() {
|
|
64
68
|
return new ReleaseLookupError("git-vibe-setup could not check the latest GitVibe update because the GitHub release service is unavailable. No files were written.");
|
|
65
69
|
}
|
|
70
|
+
function missingReleaseReason(options) {
|
|
71
|
+
return options.includePrereleases
|
|
72
|
+
? "no stable or prerelease release is available"
|
|
73
|
+
: "no stable release is available";
|
|
74
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
# Correctness Reviewer
|
|
2
|
-
|
|
3
|
-
Review the stage result for behavioral correctness. Check that conclusions are
|
|
4
|
-
grounded in the GitHub context and repository evidence, and that any required
|
|
5
|
-
next action is specific enough to execute.
|
|
6
|
-
|
|
7
|
-
Return only the current stage schema.
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
# Copy this file to .github/git-vibe.yml in the consumer repository.
|
|
2
|
-
|
|
3
|
-
version: 1
|
|
4
|
-
|
|
5
|
-
permissions:
|
|
6
|
-
# Users with write, maintain, or admin can approve automation.
|
|
7
|
-
approver_roles:
|
|
8
|
-
- write
|
|
9
|
-
- maintain
|
|
10
|
-
- admin
|
|
11
|
-
|
|
12
|
-
labels:
|
|
13
|
-
story: gvi:story
|
|
14
|
-
needs_discussion: gvi:needs-discussion
|
|
15
|
-
investigate: git-vibe:investigate
|
|
16
|
-
investigating: gvi:investigating
|
|
17
|
-
investigated: gvi:investigated
|
|
18
|
-
ready_for_approval: gvi:ready-for-approval
|
|
19
|
-
approved: git-vibe:approved
|
|
20
|
-
review: git-vibe:review
|
|
21
|
-
reviewing: gvi:reviewing
|
|
22
|
-
in_progress: gvi:in-progress
|
|
23
|
-
blocked: gvi:blocked
|
|
24
|
-
pr_opened: gvi:pr-opened
|
|
25
|
-
pr_approved: gvi:pr-approved
|
|
26
|
-
pr_merged: gvi:pr-merged
|
|
27
|
-
validate: git-vibe:validate
|
|
28
|
-
validating: gvi:validating
|
|
29
|
-
validated: gvi:validated
|
|
30
|
-
|
|
31
|
-
commands:
|
|
32
|
-
prefix: /git-vibe
|
|
33
|
-
allow_external_agent_mentions: false
|
|
34
|
-
|
|
35
|
-
event_delivery:
|
|
36
|
-
# webhook: repository webhook points at the self-hosted GitVibe server.
|
|
37
|
-
# relay: webhook proxy/tunnel such as Smee, Hookdeck, Cloudflare Tunnel, or ngrok.
|
|
38
|
-
# actions: no-server receiver workflows in the consumer repository.
|
|
39
|
-
# polling: local/scheduled worker polls GitHub APIs with cursors/ETags.
|
|
40
|
-
mode: webhook
|
|
41
|
-
relay:
|
|
42
|
-
provider: smee
|
|
43
|
-
# GitHub Actions Secret name. Relay URLs may contain delivery tokens; do not commit the real URL.
|
|
44
|
-
url_secret: GITVIBE_RELAY_URL
|
|
45
|
-
actions_receiver:
|
|
46
|
-
enabled: false
|
|
47
|
-
scheduled_scan: "*/15 * * * *"
|
|
48
|
-
polling:
|
|
49
|
-
enabled: false
|
|
50
|
-
interval_seconds: 300
|
|
51
|
-
|
|
52
|
-
github_auth:
|
|
53
|
-
# Self-hosted default: the GitVibe server uses a fine-grained PAT scoped to this repository.
|
|
54
|
-
mode: webhook-pat
|
|
55
|
-
# GitHub Actions Secret name. Stores the GitHub write token.
|
|
56
|
-
token_secret: GITVIBE_GITHUB_TOKEN
|
|
57
|
-
|
|
58
|
-
tests:
|
|
59
|
-
commands: []
|
|
60
|
-
|
|
61
|
-
ai:
|
|
62
|
-
security:
|
|
63
|
-
web:
|
|
64
|
-
# Default for public repositories: let AI search current GitHub project material only.
|
|
65
|
-
# Add domains only for websites the repository owner trusts AI to search and fetch.
|
|
66
|
-
allowed_domains: []
|
|
67
|
-
# - github.com
|
|
68
|
-
# - "*.github.com"
|
|
69
|
-
profiles:
|
|
70
|
-
local_proxy:
|
|
71
|
-
adapter: ai-sdk-agentool
|
|
72
|
-
# Optional: set to the selected model's context window.
|
|
73
|
-
# context_window_tokens: 128000
|
|
74
|
-
provider:
|
|
75
|
-
# openai, anthropic, or openai-compatible.
|
|
76
|
-
type: openai-compatible
|
|
77
|
-
# Model names are configuration, not credentials.
|
|
78
|
-
model: glm-5
|
|
79
|
-
# Keys inside GITVIBE_AI_ENV_JSON. Store provider credentials and endpoints there.
|
|
80
|
-
base_url:
|
|
81
|
-
from_bundle: GITVIBE_AI_BASE_URL
|
|
82
|
-
api_key:
|
|
83
|
-
from_bundle: GITVIBE_AI_API_KEY
|
|
84
|
-
reasoning:
|
|
85
|
-
effort: high
|
|
86
|
-
provider_options:
|
|
87
|
-
openai:
|
|
88
|
-
reasoningEffort: high
|
|
89
|
-
reasoningSummary: concise
|
|
90
|
-
anthropic:
|
|
91
|
-
effort: high
|
|
92
|
-
codex_cli:
|
|
93
|
-
adapter: cli-codex
|
|
94
|
-
# Key inside GITVIBE_AI_ENV_JSON. Stores escaped auth.json text from jq -Rs .
|
|
95
|
-
# Requires GITVIBE_GITHUB_TOKEN repository Secrets read/write permission for refresh write-back.
|
|
96
|
-
auth_json:
|
|
97
|
-
from_bundle: CODEX_AUTH_JSON
|
|
98
|
-
model: gpt-5.3-codex
|
|
99
|
-
reasoning:
|
|
100
|
-
effort: high
|
|
101
|
-
summary: concise
|
|
102
|
-
claude_code:
|
|
103
|
-
adapter: cli-claude-code
|
|
104
|
-
# Key inside GITVIBE_AI_ENV_JSON. Stores the Claude Code OAuth access token.
|
|
105
|
-
env:
|
|
106
|
-
CLAUDE_CODE_OAUTH_TOKEN:
|
|
107
|
-
from_bundle: CLAUDE_OAUTH_TOKEN
|
|
108
|
-
model: opus
|
|
109
|
-
reasoning:
|
|
110
|
-
effort: xhigh
|
|
111
|
-
role_groups:
|
|
112
|
-
review_gate:
|
|
113
|
-
synthesizer: local_proxy
|
|
114
|
-
parallel: 2
|
|
115
|
-
roles:
|
|
116
|
-
- role: correctness.md
|
|
117
|
-
profile: local_proxy
|
|
118
|
-
- role: security.md
|
|
119
|
-
profile: local_proxy
|
|
120
|
-
- role: maintainability.md
|
|
121
|
-
profile: local_proxy
|
|
122
|
-
budgets:
|
|
123
|
-
default_timeout_minutes: 60
|
|
124
|
-
review_timeout_minutes: 60
|
|
125
|
-
implementation_timeout_minutes: 120
|
|
126
|
-
feedback_timeout_minutes: 120
|
|
127
|
-
create_pr_timeout_minutes: 15
|
|
128
|
-
default_max_turns: 90
|
|
129
|
-
implementation_max_turns: 200
|
|
130
|
-
feedback_max_turns: 120
|
|
131
|
-
validation_repair_attempts: 3
|
|
132
|
-
validation_repair_max_turns: 45
|
|
133
|
-
pr_feedback_max_iterations: 3
|
|
134
|
-
request_retry_attempts: 3
|
|
135
|
-
request_retry_delay_seconds: 60
|
|
136
|
-
stages:
|
|
137
|
-
investigate:
|
|
138
|
-
role_group: review_gate
|
|
139
|
-
validate:
|
|
140
|
-
role_group: review_gate
|
|
141
|
-
materialize:
|
|
142
|
-
profile: local_proxy
|
|
143
|
-
implement:
|
|
144
|
-
profile: local_proxy
|
|
145
|
-
review-matrix:
|
|
146
|
-
role_group: review_gate
|
|
147
|
-
create-pr:
|
|
148
|
-
profile: local_proxy
|
|
149
|
-
address-pr-feedback:
|
|
150
|
-
profile: local_proxy
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
name: GitVibe address feedback
|
|
2
|
-
run-name: "[git-vibe][address-feedback]: PR #${{ inputs.pr-number }}"
|
|
3
|
-
|
|
4
|
-
on:
|
|
5
|
-
workflow_dispatch:
|
|
6
|
-
inputs:
|
|
7
|
-
pr-number:
|
|
8
|
-
description: Pull request number with feedback to address.
|
|
9
|
-
required: true
|
|
10
|
-
type: string
|
|
11
|
-
timeout_minutes:
|
|
12
|
-
description: Maximum minutes for the feedback remediation job.
|
|
13
|
-
required: false
|
|
14
|
-
type: number
|
|
15
|
-
default: 120
|
|
16
|
-
max_turns:
|
|
17
|
-
description: Maximum AI turns for feedback remediation.
|
|
18
|
-
required: false
|
|
19
|
-
type: number
|
|
20
|
-
default: 120
|
|
21
|
-
dry-run:
|
|
22
|
-
description: Validate without writing to GitHub.
|
|
23
|
-
required: false
|
|
24
|
-
type: boolean
|
|
25
|
-
default: false
|
|
26
|
-
source-comment:
|
|
27
|
-
description: JSON source comment metadata for replying to command comments.
|
|
28
|
-
required: false
|
|
29
|
-
type: string
|
|
30
|
-
default: ""
|
|
31
|
-
|
|
32
|
-
jobs:
|
|
33
|
-
address-feedback:
|
|
34
|
-
uses: markhuangai/git-vibe/.github/workflows/address-feedback.yml@v3
|
|
35
|
-
with:
|
|
36
|
-
pr-number: ${{ inputs.pr-number }}
|
|
37
|
-
runner: ubuntu-latest
|
|
38
|
-
timeout_minutes: ${{ inputs.timeout_minutes }}
|
|
39
|
-
max_turns: ${{ inputs.max_turns }}
|
|
40
|
-
dry-run: ${{ inputs.dry-run }}
|
|
41
|
-
source-comment: ${{ inputs.source-comment }}
|
|
42
|
-
secrets:
|
|
43
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
44
|
-
GITVIBE_AI_ENV_JSON: ${{ secrets.GITVIBE_AI_ENV_JSON }}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
name: GitVibe develop
|
|
2
|
-
run-name: "[git-vibe][develop]: Issue #${{ inputs.issue-number }}"
|
|
3
|
-
|
|
4
|
-
on:
|
|
5
|
-
workflow_dispatch:
|
|
6
|
-
inputs:
|
|
7
|
-
issue-number:
|
|
8
|
-
description: Approved implementation issue number.
|
|
9
|
-
required: true
|
|
10
|
-
type: string
|
|
11
|
-
implementation_timeout_minutes:
|
|
12
|
-
description: Maximum minutes for the implementation job.
|
|
13
|
-
required: false
|
|
14
|
-
type: number
|
|
15
|
-
default: 120
|
|
16
|
-
review_timeout_minutes:
|
|
17
|
-
description: Maximum minutes for the review matrix job.
|
|
18
|
-
required: false
|
|
19
|
-
type: number
|
|
20
|
-
default: 60
|
|
21
|
-
create_pr_timeout_minutes:
|
|
22
|
-
description: Maximum minutes for the PR creation job.
|
|
23
|
-
required: false
|
|
24
|
-
type: number
|
|
25
|
-
default: 15
|
|
26
|
-
max_turns:
|
|
27
|
-
description: Default maximum AI turns for stages in this workflow.
|
|
28
|
-
required: false
|
|
29
|
-
type: number
|
|
30
|
-
default: 90
|
|
31
|
-
implementation_max_turns:
|
|
32
|
-
description: Maximum AI turns for implementation and feedback-style coding work.
|
|
33
|
-
required: false
|
|
34
|
-
type: number
|
|
35
|
-
default: 200
|
|
36
|
-
validation_repair_attempts:
|
|
37
|
-
description: Maximum validation repair attempts inside each implementation run.
|
|
38
|
-
required: false
|
|
39
|
-
type: number
|
|
40
|
-
default: 3
|
|
41
|
-
validation_repair_max_turns:
|
|
42
|
-
description: Maximum AI turns for each validation repair attempt.
|
|
43
|
-
required: false
|
|
44
|
-
type: number
|
|
45
|
-
default: 45
|
|
46
|
-
dry-run:
|
|
47
|
-
description: Validate without writing to GitHub.
|
|
48
|
-
required: false
|
|
49
|
-
type: boolean
|
|
50
|
-
default: false
|
|
51
|
-
source-comment:
|
|
52
|
-
description: JSON source comment metadata for replying to command comments.
|
|
53
|
-
required: false
|
|
54
|
-
type: string
|
|
55
|
-
default: ""
|
|
56
|
-
|
|
57
|
-
jobs:
|
|
58
|
-
develop:
|
|
59
|
-
uses: markhuangai/git-vibe/.github/workflows/develop.yml@v3
|
|
60
|
-
with:
|
|
61
|
-
issue-number: ${{ inputs.issue-number }}
|
|
62
|
-
runner: ubuntu-latest
|
|
63
|
-
implementation_timeout_minutes: ${{ inputs.implementation_timeout_minutes }}
|
|
64
|
-
review_timeout_minutes: ${{ inputs.review_timeout_minutes }}
|
|
65
|
-
create_pr_timeout_minutes: ${{ inputs.create_pr_timeout_minutes }}
|
|
66
|
-
max_turns: ${{ inputs.max_turns }}
|
|
67
|
-
implementation_max_turns: ${{ inputs.implementation_max_turns }}
|
|
68
|
-
validation_repair_attempts: ${{ inputs.validation_repair_attempts }}
|
|
69
|
-
validation_repair_max_turns: ${{ inputs.validation_repair_max_turns }}
|
|
70
|
-
dry-run: ${{ inputs.dry-run }}
|
|
71
|
-
source-comment: ${{ inputs.source-comment }}
|
|
72
|
-
secrets:
|
|
73
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
74
|
-
GITVIBE_AI_ENV_JSON: ${{ secrets.GITVIBE_AI_ENV_JSON }}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
name: GitVibe investigate
|
|
2
|
-
run-name: "[git-vibe][investigate]: Issue #${{ inputs.issue-number }}"
|
|
3
|
-
|
|
4
|
-
on:
|
|
5
|
-
workflow_dispatch:
|
|
6
|
-
inputs:
|
|
7
|
-
issue-number:
|
|
8
|
-
description: Issue number to investigate.
|
|
9
|
-
required: true
|
|
10
|
-
type: string
|
|
11
|
-
timeout_minutes:
|
|
12
|
-
description: Maximum minutes for the investigation job.
|
|
13
|
-
required: false
|
|
14
|
-
type: number
|
|
15
|
-
default: 60
|
|
16
|
-
max_turns:
|
|
17
|
-
description: Maximum AI turns for this stage.
|
|
18
|
-
required: false
|
|
19
|
-
type: number
|
|
20
|
-
default: 90
|
|
21
|
-
dry-run:
|
|
22
|
-
description: Validate without writing to GitHub.
|
|
23
|
-
required: false
|
|
24
|
-
type: boolean
|
|
25
|
-
default: false
|
|
26
|
-
source-comment:
|
|
27
|
-
description: JSON source comment metadata for replying to command comments.
|
|
28
|
-
required: false
|
|
29
|
-
type: string
|
|
30
|
-
default: ""
|
|
31
|
-
|
|
32
|
-
jobs:
|
|
33
|
-
investigate:
|
|
34
|
-
uses: markhuangai/git-vibe/.github/workflows/investigate.yml@v3
|
|
35
|
-
with:
|
|
36
|
-
issue-number: ${{ inputs.issue-number }}
|
|
37
|
-
runner: ubuntu-latest
|
|
38
|
-
timeout_minutes: ${{ inputs.timeout_minutes }}
|
|
39
|
-
max_turns: ${{ inputs.max_turns }}
|
|
40
|
-
dry-run: ${{ inputs.dry-run }}
|
|
41
|
-
source-comment: ${{ inputs.source-comment }}
|
|
42
|
-
secrets:
|
|
43
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
44
|
-
GITVIBE_AI_ENV_JSON: ${{ secrets.GITVIBE_AI_ENV_JSON }}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
name: GitVibe materialize
|
|
2
|
-
run-name: "[git-vibe][materialize]: Discussion #${{ inputs.discussion-number }}"
|
|
3
|
-
|
|
4
|
-
on:
|
|
5
|
-
workflow_dispatch:
|
|
6
|
-
inputs:
|
|
7
|
-
discussion-number:
|
|
8
|
-
description: Discussion number to materialize.
|
|
9
|
-
required: true
|
|
10
|
-
type: string
|
|
11
|
-
timeout_minutes:
|
|
12
|
-
description: Maximum minutes for this job.
|
|
13
|
-
required: false
|
|
14
|
-
type: number
|
|
15
|
-
default: 60
|
|
16
|
-
max_turns:
|
|
17
|
-
description: Maximum AI turns for this stage.
|
|
18
|
-
required: false
|
|
19
|
-
type: number
|
|
20
|
-
default: 90
|
|
21
|
-
dry-run:
|
|
22
|
-
description: Validate without writing to GitHub.
|
|
23
|
-
required: false
|
|
24
|
-
type: boolean
|
|
25
|
-
default: false
|
|
26
|
-
source-comment:
|
|
27
|
-
description: JSON source comment metadata for replying to command comments.
|
|
28
|
-
required: false
|
|
29
|
-
type: string
|
|
30
|
-
default: ""
|
|
31
|
-
|
|
32
|
-
jobs:
|
|
33
|
-
materialize:
|
|
34
|
-
uses: markhuangai/git-vibe/.github/workflows/materialize.yml@v3
|
|
35
|
-
with:
|
|
36
|
-
discussion-number: ${{ inputs.discussion-number }}
|
|
37
|
-
runner: ubuntu-latest
|
|
38
|
-
timeout_minutes: ${{ inputs.timeout_minutes }}
|
|
39
|
-
max_turns: ${{ inputs.max_turns }}
|
|
40
|
-
dry-run: ${{ inputs.dry-run }}
|
|
41
|
-
source-comment: ${{ inputs.source-comment }}
|
|
42
|
-
secrets:
|
|
43
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
44
|
-
GITVIBE_AI_ENV_JSON: ${{ secrets.GITVIBE_AI_ENV_JSON }}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
name: GitVibe review
|
|
2
|
-
run-name: "[git-vibe][review]: PR #${{ inputs.pr-number }}"
|
|
3
|
-
|
|
4
|
-
on:
|
|
5
|
-
workflow_dispatch:
|
|
6
|
-
inputs:
|
|
7
|
-
pr-number:
|
|
8
|
-
description: Pull request number to review.
|
|
9
|
-
required: true
|
|
10
|
-
type: string
|
|
11
|
-
timeout_minutes:
|
|
12
|
-
description: Maximum minutes for the review matrix job.
|
|
13
|
-
required: false
|
|
14
|
-
type: number
|
|
15
|
-
default: 60
|
|
16
|
-
max_turns:
|
|
17
|
-
description: Maximum AI turns for the review stage.
|
|
18
|
-
required: false
|
|
19
|
-
type: number
|
|
20
|
-
default: 90
|
|
21
|
-
dry-run:
|
|
22
|
-
description: Validate without writing to GitHub.
|
|
23
|
-
required: false
|
|
24
|
-
type: boolean
|
|
25
|
-
default: false
|
|
26
|
-
source-comment:
|
|
27
|
-
description: JSON source comment metadata for replying to command comments.
|
|
28
|
-
required: false
|
|
29
|
-
type: string
|
|
30
|
-
default: ""
|
|
31
|
-
|
|
32
|
-
jobs:
|
|
33
|
-
review:
|
|
34
|
-
uses: markhuangai/git-vibe/.github/workflows/review.yml@v3
|
|
35
|
-
with:
|
|
36
|
-
pr-number: ${{ inputs.pr-number }}
|
|
37
|
-
runner: ubuntu-latest
|
|
38
|
-
timeout_minutes: ${{ inputs.timeout_minutes }}
|
|
39
|
-
max_turns: ${{ inputs.max_turns }}
|
|
40
|
-
dry-run: ${{ inputs.dry-run }}
|
|
41
|
-
source-comment: ${{ inputs.source-comment }}
|
|
42
|
-
secrets:
|
|
43
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
44
|
-
GITVIBE_AI_ENV_JSON: ${{ secrets.GITVIBE_AI_ENV_JSON }}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
name: GitVibe validate
|
|
2
|
-
run-name: "[git-vibe][validate]: ${{ inputs.discussion-number != '' && format('Discussion #{0}', inputs.discussion-number) || inputs.issue-number != '' && format('Issue #{0}', inputs.issue-number) || 'Artifact' }}"
|
|
3
|
-
|
|
4
|
-
on:
|
|
5
|
-
workflow_dispatch:
|
|
6
|
-
inputs:
|
|
7
|
-
issue-number:
|
|
8
|
-
description: Issue number to validate.
|
|
9
|
-
required: false
|
|
10
|
-
type: string
|
|
11
|
-
default: ""
|
|
12
|
-
discussion-number:
|
|
13
|
-
description: Discussion number to validate.
|
|
14
|
-
required: false
|
|
15
|
-
type: string
|
|
16
|
-
default: ""
|
|
17
|
-
timeout_minutes:
|
|
18
|
-
description: Maximum minutes for this job.
|
|
19
|
-
required: false
|
|
20
|
-
type: number
|
|
21
|
-
default: 60
|
|
22
|
-
max_turns:
|
|
23
|
-
description: Maximum AI turns for this stage.
|
|
24
|
-
required: false
|
|
25
|
-
type: number
|
|
26
|
-
default: 90
|
|
27
|
-
dry-run:
|
|
28
|
-
description: Validate without writing to GitHub.
|
|
29
|
-
required: false
|
|
30
|
-
type: boolean
|
|
31
|
-
default: false
|
|
32
|
-
source-comment:
|
|
33
|
-
description: JSON source comment metadata for replying to command comments.
|
|
34
|
-
required: false
|
|
35
|
-
type: string
|
|
36
|
-
default: ""
|
|
37
|
-
|
|
38
|
-
jobs:
|
|
39
|
-
validate:
|
|
40
|
-
uses: markhuangai/git-vibe/.github/workflows/validate.yml@v3
|
|
41
|
-
with:
|
|
42
|
-
issue-number: ${{ inputs.issue-number }}
|
|
43
|
-
discussion-number: ${{ inputs.discussion-number }}
|
|
44
|
-
runner: ubuntu-latest
|
|
45
|
-
timeout_minutes: ${{ inputs.timeout_minutes }}
|
|
46
|
-
max_turns: ${{ inputs.max_turns }}
|
|
47
|
-
dry-run: ${{ inputs.dry-run }}
|
|
48
|
-
source-comment: ${{ inputs.source-comment }}
|
|
49
|
-
secrets:
|
|
50
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
51
|
-
GITVIBE_AI_ENV_JSON: ${{ secrets.GITVIBE_AI_ENV_JSON }}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"CLAUDE_OAUTH_TOKEN": "replace-with-claude-oauth-token",
|
|
3
|
-
"CODEX_AUTH_JSON": "{\"tokens\":[]}",
|
|
4
|
-
"GITVIBE_AI_API_KEY": "replace-with-ai-provider-api-key",
|
|
5
|
-
"GITVIBE_AI_BASE_URL": "https://api.provider.example/v1",
|
|
6
|
-
"MINIMAX_API_KEY": "replace-with-minimax-api-key",
|
|
7
|
-
"MINIMAX_ANTHROPIC_BASE_URL": "https://api.minimax.example/anthropic"
|
|
8
|
-
}
|