git-vibe-setup 3.0.2 → 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 +31 -3
- package/dist/cli.d.ts +4 -1
- package/dist/cli.js +113 -14
- 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 +10 -1
- package/dist/install.js +117 -35
- 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 -34
- package/templates/.github/workflows/develop.yml +0 -39
- package/templates/.github/workflows/investigate.yml +0 -34
- package/templates/.github/workflows/materialize.yml +0 -34
- package/templates/.github/workflows/review.yml +0 -34
- package/templates/.github/workflows/validate.yml +0 -41
- package/templates/GITVIBE_AI_ENV_JSON.example.json +0 -8
package/README.md
CHANGED
|
@@ -6,6 +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.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx git-vibe-setup update
|
|
20
|
+
```
|
|
21
|
+
|
|
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,10 +4,13 @@ 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>;
|
|
13
|
+
export declare function runUpdate(runtime?: SetupCliRuntime): Promise<void>;
|
|
11
14
|
export declare function setupCli(runtime?: SetupCliRuntime): Promise<number>;
|
|
12
15
|
export declare function isDirectRun(moduleUrl: string, entrypoint?: string): boolean;
|
|
13
16
|
export {};
|
package/dist/cli.js
CHANGED
|
@@ -3,42 +3,79 @@ 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 {
|
|
6
|
+
import { fetchConsumerStarterFiles } from "./consumer-starter.js";
|
|
7
|
+
import { githubTokenFromEnvironment } from "./github-api.js";
|
|
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
|
|
12
|
+
git-vibe-setup setup [--release <tag>] [--include-prereleases]
|
|
13
|
+
git-vibe-setup update [--release <tag>] [--include-prereleases]
|
|
11
14
|
git-vibe-setup
|
|
12
15
|
|
|
13
16
|
Commands:
|
|
14
17
|
setup Install GitVibe starter files into the current repository.
|
|
18
|
+
update Update GitVibe workflow wrapper files in the current repository.
|
|
15
19
|
|
|
16
20
|
Options:
|
|
17
|
-
|
|
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.`;
|
|
18
24
|
export async function runSetup(runtime = {}) {
|
|
19
25
|
const cwd = runtime.cwd || process.cwd();
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
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 });
|
|
23
31
|
const blockingPaths = blockingInstallPaths(files);
|
|
24
32
|
if (blockingPaths.length > 0)
|
|
25
33
|
throw existingFilesError(blockingPaths, cwd);
|
|
26
34
|
installFiles(files);
|
|
27
35
|
(runtime.log || console.log)(renderManualSetupInstructions(releaseTag));
|
|
28
36
|
}
|
|
37
|
+
export async function runUpdate(runtime = {}) {
|
|
38
|
+
const cwd = runtime.cwd || process.cwd();
|
|
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 });
|
|
44
|
+
const unmanagedPaths = unmanagedWorkflowUpdatePaths(files);
|
|
45
|
+
if (unmanagedPaths.length > 0)
|
|
46
|
+
throw unmanagedWorkflowUpdateError(unmanagedPaths, cwd);
|
|
47
|
+
updateFiles(files);
|
|
48
|
+
(runtime.log || console.log)(`GitVibe workflow files updated with reusable workflows pinned to ${releaseTag}.`);
|
|
49
|
+
}
|
|
29
50
|
export async function setupCli(runtime = {}) {
|
|
30
51
|
const argv = runtime.argv || process.argv.slice(2);
|
|
31
|
-
|
|
32
|
-
|
|
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") {
|
|
33
62
|
(runtime.log || console.log)(usage);
|
|
34
63
|
return 0;
|
|
35
64
|
}
|
|
36
|
-
if (
|
|
37
|
-
(runtime.error || console.error)(
|
|
65
|
+
if (parsed.kind === "error") {
|
|
66
|
+
(runtime.error || console.error)(`${parsed.message}\n\n${usage}`);
|
|
38
67
|
return 1;
|
|
39
68
|
}
|
|
69
|
+
const commandRuntime = {
|
|
70
|
+
...runtime,
|
|
71
|
+
includePrereleases: runtime.includePrereleases || parsed.options.includePrereleases,
|
|
72
|
+
releaseTag: parsed.options.releaseTag || runtime.releaseTag,
|
|
73
|
+
};
|
|
40
74
|
try {
|
|
41
|
-
|
|
75
|
+
if (parsed.options.command === "update")
|
|
76
|
+
await runUpdate(commandRuntime);
|
|
77
|
+
else
|
|
78
|
+
await runSetup(commandRuntime);
|
|
42
79
|
return 0;
|
|
43
80
|
}
|
|
44
81
|
catch (error) {
|
|
@@ -47,8 +84,70 @@ export async function setupCli(runtime = {}) {
|
|
|
47
84
|
return 1;
|
|
48
85
|
}
|
|
49
86
|
}
|
|
50
|
-
function
|
|
51
|
-
|
|
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.`);
|
|
52
151
|
}
|
|
53
152
|
/* c8 ignore start */
|
|
54
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,9 +7,17 @@ export interface InstallFile {
|
|
|
6
7
|
export declare function buildInstallFiles(options: {
|
|
7
8
|
cwd: string;
|
|
8
9
|
releaseTag: string;
|
|
9
|
-
|
|
10
|
+
sourceFiles: ConsumerStarterFile[];
|
|
11
|
+
}): InstallFile[];
|
|
12
|
+
export declare function buildWorkflowUpdateFiles(options: {
|
|
13
|
+
cwd: string;
|
|
14
|
+
releaseTag: string;
|
|
15
|
+
sourceFiles: ConsumerStarterFile[];
|
|
10
16
|
}): InstallFile[];
|
|
11
17
|
export declare function blockingInstallPaths(files: InstallFile[]): string[];
|
|
18
|
+
export declare function unmanagedWorkflowUpdatePaths(files: InstallFile[]): string[];
|
|
12
19
|
export declare function installFiles(files: InstallFile[]): void;
|
|
20
|
+
export declare function updateFiles(files: InstallFile[]): void;
|
|
13
21
|
export declare function existingFilesError(paths: string[], cwd: string): Error;
|
|
22
|
+
export declare function unmanagedWorkflowUpdateError(paths: string[], cwd: string): Error;
|
|
14
23
|
export declare function pinWorkflowReleaseRefs(content: string, releaseTag: string): string;
|
package/dist/install.js
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
|
-
import { existsSync, mkdirSync,
|
|
2
|
-
import { dirname, join, relative } from "node:path";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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));
|
|
21
|
+
}
|
|
22
|
+
export function buildWorkflowUpdateFiles(options) {
|
|
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));
|
|
14
27
|
}
|
|
15
28
|
export function blockingInstallPaths(files) {
|
|
16
29
|
return files
|
|
@@ -18,6 +31,12 @@ export function blockingInstallPaths(files) {
|
|
|
18
31
|
.filter((targetPath) => existsSync(targetPath))
|
|
19
32
|
.sort();
|
|
20
33
|
}
|
|
34
|
+
export function unmanagedWorkflowUpdatePaths(files) {
|
|
35
|
+
return files
|
|
36
|
+
.filter((file) => existsSync(file.targetPath) && !isManagedWorkflowTarget(file))
|
|
37
|
+
.map((file) => file.targetPath)
|
|
38
|
+
.sort();
|
|
39
|
+
}
|
|
21
40
|
export function installFiles(files) {
|
|
22
41
|
const createdDirectories = [];
|
|
23
42
|
const createdFiles = [];
|
|
@@ -33,36 +52,55 @@ export function installFiles(files) {
|
|
|
33
52
|
throw error;
|
|
34
53
|
}
|
|
35
54
|
}
|
|
55
|
+
export function updateFiles(files) {
|
|
56
|
+
const createdDirectories = [];
|
|
57
|
+
const snapshots = [];
|
|
58
|
+
try {
|
|
59
|
+
for (const file of files) {
|
|
60
|
+
ensureDirectory(dirname(file.targetPath), createdDirectories);
|
|
61
|
+
snapshots.push(snapshotFile(file.targetPath));
|
|
62
|
+
writeFileSync(file.targetPath, file.content);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
rollbackUpdate(snapshots, createdDirectories);
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
36
70
|
export function existingFilesError(paths, cwd) {
|
|
37
71
|
const listed = paths.map((path) => `- ${relative(cwd, path) || path}`).join("\n");
|
|
38
72
|
return new Error(`git-vibe-setup found existing GitVibe files and did not overwrite them:\n${listed}\nRemove the listed files before running setup again.`);
|
|
39
73
|
}
|
|
74
|
+
export function unmanagedWorkflowUpdateError(paths, cwd) {
|
|
75
|
+
const listed = paths.map((path) => `- ${relative(cwd, path) || path}`).join("\n");
|
|
76
|
+
return new Error(`git-vibe-setup found workflow files that do not look like GitVibe wrappers and did not overwrite them:\n${listed}`);
|
|
77
|
+
}
|
|
40
78
|
export function pinWorkflowReleaseRefs(content, releaseTag) {
|
|
41
79
|
return content.replace(/(uses:\s*markhuangai\/git-vibe\/\.github\/workflows\/[^\s@]+)@[^\s]+/g, (_match, workflowReference) => `${workflowReference}@${releaseTag}`);
|
|
42
80
|
}
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
function
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
function isManagedWorkflowTarget(file) {
|
|
82
|
+
try {
|
|
83
|
+
const workflowName = basename(file.targetPath);
|
|
84
|
+
return managedWorkflowPattern(workflowName).test(readFileSync(file.targetPath, "utf8"));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function managedWorkflowPattern(workflowName) {
|
|
91
|
+
return new RegExp(`uses:\\s*markhuangai/git-vibe/\\.github/workflows/${escapeRegExp(workflowName)}@`);
|
|
92
|
+
}
|
|
93
|
+
function escapeRegExp(value) {
|
|
94
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, String.raw `\$&`);
|
|
95
|
+
}
|
|
96
|
+
function snapshotFile(targetPath) {
|
|
97
|
+
if (!existsSync(targetPath))
|
|
98
|
+
return { existed: false, targetPath };
|
|
99
|
+
return {
|
|
100
|
+
content: readFileSync(targetPath, "utf8"),
|
|
101
|
+
existed: true,
|
|
102
|
+
targetPath,
|
|
103
|
+
};
|
|
66
104
|
}
|
|
67
105
|
function ensureDirectory(directory, createdDirectories) {
|
|
68
106
|
const missing = missingDirectories(directory);
|
|
@@ -84,6 +122,19 @@ function missingDirectories(directory) {
|
|
|
84
122
|
}
|
|
85
123
|
return missing.reverse();
|
|
86
124
|
}
|
|
125
|
+
function rollbackUpdate(snapshots, createdDirectories) {
|
|
126
|
+
for (const snapshot of [...snapshots].reverse()) {
|
|
127
|
+
if (snapshot.existed) {
|
|
128
|
+
writeFileSync(snapshot.targetPath, snapshot.content || "");
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
rmSync(snapshot.targetPath, { force: true });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
for (const directory of [...createdDirectories].reverse()) {
|
|
135
|
+
rmSync(directory, { force: true, recursive: false });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
87
138
|
function rollbackInstall(createdFiles, createdDirectories) {
|
|
88
139
|
for (const file of [...createdFiles].reverse()) {
|
|
89
140
|
rmSync(file, { force: true });
|
|
@@ -92,3 +143,34 @@ function rollbackInstall(createdFiles, createdDirectories) {
|
|
|
92
143
|
rmSync(directory, { force: true, recursive: false });
|
|
93
144
|
}
|
|
94
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,34 +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
|
-
dry-run:
|
|
12
|
-
description: Validate without writing to GitHub.
|
|
13
|
-
required: false
|
|
14
|
-
type: boolean
|
|
15
|
-
default: false
|
|
16
|
-
source-comment:
|
|
17
|
-
description: JSON source comment metadata for replying to command comments.
|
|
18
|
-
required: false
|
|
19
|
-
type: string
|
|
20
|
-
default: ""
|
|
21
|
-
|
|
22
|
-
jobs:
|
|
23
|
-
address-feedback:
|
|
24
|
-
uses: markhuangai/git-vibe/.github/workflows/address-feedback.yml@v3
|
|
25
|
-
with:
|
|
26
|
-
pr-number: ${{ inputs.pr-number }}
|
|
27
|
-
runner: ubuntu-latest
|
|
28
|
-
timeout_minutes: 120
|
|
29
|
-
max_turns: 120
|
|
30
|
-
dry-run: ${{ inputs.dry-run }}
|
|
31
|
-
source-comment: ${{ inputs.source-comment }}
|
|
32
|
-
secrets:
|
|
33
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
34
|
-
GITVIBE_AI_ENV_JSON: ${{ secrets.GITVIBE_AI_ENV_JSON }}
|
|
@@ -1,39 +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
|
-
dry-run:
|
|
12
|
-
description: Validate without writing to GitHub.
|
|
13
|
-
required: false
|
|
14
|
-
type: boolean
|
|
15
|
-
default: false
|
|
16
|
-
source-comment:
|
|
17
|
-
description: JSON source comment metadata for replying to command comments.
|
|
18
|
-
required: false
|
|
19
|
-
type: string
|
|
20
|
-
default: ""
|
|
21
|
-
|
|
22
|
-
jobs:
|
|
23
|
-
develop:
|
|
24
|
-
uses: markhuangai/git-vibe/.github/workflows/develop.yml@v3
|
|
25
|
-
with:
|
|
26
|
-
issue-number: ${{ inputs.issue-number }}
|
|
27
|
-
runner: ubuntu-latest
|
|
28
|
-
implementation_timeout_minutes: 120
|
|
29
|
-
review_timeout_minutes: 60
|
|
30
|
-
create_pr_timeout_minutes: 15
|
|
31
|
-
max_turns: 90
|
|
32
|
-
implementation_max_turns: 120
|
|
33
|
-
validation_repair_attempts: 2
|
|
34
|
-
validation_repair_max_turns: 90
|
|
35
|
-
dry-run: ${{ inputs.dry-run }}
|
|
36
|
-
source-comment: ${{ inputs.source-comment }}
|
|
37
|
-
secrets:
|
|
38
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
39
|
-
GITVIBE_AI_ENV_JSON: ${{ secrets.GITVIBE_AI_ENV_JSON }}
|
|
@@ -1,34 +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
|
-
dry-run:
|
|
12
|
-
description: Validate without writing to GitHub.
|
|
13
|
-
required: false
|
|
14
|
-
type: boolean
|
|
15
|
-
default: false
|
|
16
|
-
source-comment:
|
|
17
|
-
description: JSON source comment metadata for replying to command comments.
|
|
18
|
-
required: false
|
|
19
|
-
type: string
|
|
20
|
-
default: ""
|
|
21
|
-
|
|
22
|
-
jobs:
|
|
23
|
-
investigate:
|
|
24
|
-
uses: markhuangai/git-vibe/.github/workflows/investigate.yml@v3
|
|
25
|
-
with:
|
|
26
|
-
issue-number: ${{ inputs.issue-number }}
|
|
27
|
-
runner: ubuntu-latest
|
|
28
|
-
timeout_minutes: 60
|
|
29
|
-
max_turns: 90
|
|
30
|
-
dry-run: ${{ inputs.dry-run }}
|
|
31
|
-
source-comment: ${{ inputs.source-comment }}
|
|
32
|
-
secrets:
|
|
33
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
34
|
-
GITVIBE_AI_ENV_JSON: ${{ secrets.GITVIBE_AI_ENV_JSON }}
|
|
@@ -1,34 +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
|
-
dry-run:
|
|
12
|
-
description: Validate without writing to GitHub.
|
|
13
|
-
required: false
|
|
14
|
-
type: boolean
|
|
15
|
-
default: false
|
|
16
|
-
source-comment:
|
|
17
|
-
description: JSON source comment metadata for replying to command comments.
|
|
18
|
-
required: false
|
|
19
|
-
type: string
|
|
20
|
-
default: ""
|
|
21
|
-
|
|
22
|
-
jobs:
|
|
23
|
-
materialize:
|
|
24
|
-
uses: markhuangai/git-vibe/.github/workflows/materialize.yml@v3
|
|
25
|
-
with:
|
|
26
|
-
discussion-number: ${{ inputs.discussion-number }}
|
|
27
|
-
runner: ubuntu-latest
|
|
28
|
-
timeout_minutes: 60
|
|
29
|
-
max_turns: 90
|
|
30
|
-
dry-run: ${{ inputs.dry-run }}
|
|
31
|
-
source-comment: ${{ inputs.source-comment }}
|
|
32
|
-
secrets:
|
|
33
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
34
|
-
GITVIBE_AI_ENV_JSON: ${{ secrets.GITVIBE_AI_ENV_JSON }}
|
|
@@ -1,34 +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
|
-
dry-run:
|
|
12
|
-
description: Validate without writing to GitHub.
|
|
13
|
-
required: false
|
|
14
|
-
type: boolean
|
|
15
|
-
default: false
|
|
16
|
-
source-comment:
|
|
17
|
-
description: JSON source comment metadata for replying to command comments.
|
|
18
|
-
required: false
|
|
19
|
-
type: string
|
|
20
|
-
default: ""
|
|
21
|
-
|
|
22
|
-
jobs:
|
|
23
|
-
review:
|
|
24
|
-
uses: markhuangai/git-vibe/.github/workflows/review.yml@v3
|
|
25
|
-
with:
|
|
26
|
-
pr-number: ${{ inputs.pr-number }}
|
|
27
|
-
runner: ubuntu-latest
|
|
28
|
-
timeout_minutes: 60
|
|
29
|
-
max_turns: 90
|
|
30
|
-
dry-run: ${{ inputs.dry-run }}
|
|
31
|
-
source-comment: ${{ inputs.source-comment }}
|
|
32
|
-
secrets:
|
|
33
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
34
|
-
GITVIBE_AI_ENV_JSON: ${{ secrets.GITVIBE_AI_ENV_JSON }}
|
|
@@ -1,41 +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
|
-
dry-run:
|
|
18
|
-
description: Validate without writing to GitHub.
|
|
19
|
-
required: false
|
|
20
|
-
type: boolean
|
|
21
|
-
default: false
|
|
22
|
-
source-comment:
|
|
23
|
-
description: JSON source comment metadata for replying to command comments.
|
|
24
|
-
required: false
|
|
25
|
-
type: string
|
|
26
|
-
default: ""
|
|
27
|
-
|
|
28
|
-
jobs:
|
|
29
|
-
validate:
|
|
30
|
-
uses: markhuangai/git-vibe/.github/workflows/validate.yml@v3
|
|
31
|
-
with:
|
|
32
|
-
issue-number: ${{ inputs.issue-number }}
|
|
33
|
-
discussion-number: ${{ inputs.discussion-number }}
|
|
34
|
-
runner: ubuntu-latest
|
|
35
|
-
timeout_minutes: 60
|
|
36
|
-
max_turns: 90
|
|
37
|
-
dry-run: ${{ inputs.dry-run }}
|
|
38
|
-
source-comment: ${{ inputs.source-comment }}
|
|
39
|
-
secrets:
|
|
40
|
-
GITVIBE_GITHUB_TOKEN: ${{ secrets.GITVIBE_GITHUB_TOKEN }}
|
|
41
|
-
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
|
-
}
|