gitnexushub 0.4.5 → 0.6.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/dist/api.d.ts +90 -1
- package/dist/api.js +34 -0
- package/dist/index.js +27 -0
- package/dist/install-ci-command.d.ts +176 -0
- package/dist/install-ci-command.js +680 -0
- package/dist/wiki/claude.d.ts +11 -5
- package/dist/wiki/claude.js +8 -3
- package/dist/wiki/compose-overview.d.ts +29 -0
- package/dist/wiki/compose-overview.js +48 -0
- package/dist/wiki/concurrency.d.ts +20 -0
- package/dist/wiki/concurrency.js +91 -0
- package/dist/wiki/helpers.d.ts +102 -0
- package/dist/wiki/helpers.js +308 -0
- package/dist/wiki/incremental.d.ts +72 -0
- package/dist/wiki/incremental.js +214 -0
- package/dist/wiki/index.js +37 -0
- package/dist/wiki/session.d.ts +10 -0
- package/dist/wiki/session.js +89 -9
- package/dist/wiki/upload-command.d.ts +12 -0
- package/dist/wiki/upload-command.js +384 -53
- package/hooks/gitnexus-enterprise-hook.cjs +134 -0
- package/package.json +1 -1
- package/skills/gitnexus-debugging.md +89 -89
- package/skills/gitnexus-exploring.md +78 -78
- package/skills/gitnexus-impact-analysis.md +99 -99
- package/skills/gitnexus-pr-review.md +161 -161
package/dist/api.d.ts
CHANGED
|
@@ -33,6 +33,44 @@ export interface IndexResult {
|
|
|
33
33
|
fullName: string;
|
|
34
34
|
status: string;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Response from `POST /api/repos/:id/install-ci-bundle`.
|
|
38
|
+
*
|
|
39
|
+
* Hub mints a long-lived CI token + renders the single Claude-led
|
|
40
|
+
* workflow YAML. `token` and `ciToken` are the same value emitted
|
|
41
|
+
* twice for back-compat with the v1 client (which read `ciToken`)
|
|
42
|
+
* and any future caller that reads the more conventional `token`.
|
|
43
|
+
* Use `ciToken` — the field name documents intent.
|
|
44
|
+
*
|
|
45
|
+
* `workflowYaml` is the rendered `.github/workflows/gitnexus.yml`
|
|
46
|
+
* contents. The CLI base64-encodes and PUTs it via the GitHub
|
|
47
|
+
* Contents API; no template assembly happens client-side.
|
|
48
|
+
*/
|
|
49
|
+
export interface InstallCiBundle {
|
|
50
|
+
/** CI token. Same value as `ciToken` — emitted twice for back-compat. */
|
|
51
|
+
token: string;
|
|
52
|
+
/** CI token (preferred field name; documents intent). */
|
|
53
|
+
ciToken: string;
|
|
54
|
+
/** First 12 chars of the token for display in the CLI summary. */
|
|
55
|
+
ciTokenPrefix?: string;
|
|
56
|
+
/** Repo secret name the workflow YAML references for the gnx_ token. */
|
|
57
|
+
ciTokenSecretName?: string;
|
|
58
|
+
/** Repo secret name the workflow YAML references for Claude OAuth. */
|
|
59
|
+
claudeOauthSecretName?: string;
|
|
60
|
+
/** Rendered `.github/workflows/gitnexus.yml` contents. */
|
|
61
|
+
workflowYaml: string;
|
|
62
|
+
/** Hub MCP endpoint URL embedded in the workflow YAML. */
|
|
63
|
+
hubMcpUrl?: string;
|
|
64
|
+
/** owner/repo this token + workflow target. */
|
|
65
|
+
repoFullName: string;
|
|
66
|
+
/**
|
|
67
|
+
* Map of relative-path → file-content. Currently always
|
|
68
|
+
* `{ '.github/workflows/gitnexus.yml': workflowYaml }` — emitted as
|
|
69
|
+
* an object so future installs can ship sidecar config files (e.g.
|
|
70
|
+
* `.gitnexus.toml`) without bumping the response shape.
|
|
71
|
+
*/
|
|
72
|
+
files?: Record<string, string>;
|
|
73
|
+
}
|
|
36
74
|
export interface RepoDetail {
|
|
37
75
|
id: string;
|
|
38
76
|
name: string;
|
|
@@ -121,6 +159,24 @@ export interface WikiUploadStatus {
|
|
|
121
159
|
active: WikiUploadSessionRow | null;
|
|
122
160
|
last: WikiUploadSessionRow | null;
|
|
123
161
|
}
|
|
162
|
+
export interface WikiGroupingContext {
|
|
163
|
+
communityGroups: string;
|
|
164
|
+
interCommunityEdges: string;
|
|
165
|
+
crossCommunityProcesses: string;
|
|
166
|
+
filesWithExports: string;
|
|
167
|
+
directoryTree: string;
|
|
168
|
+
communityMapping?: Array<{
|
|
169
|
+
label: string;
|
|
170
|
+
files: string[];
|
|
171
|
+
}>;
|
|
172
|
+
filesWithExportsRaw?: Array<{
|
|
173
|
+
filePath: string;
|
|
174
|
+
symbols: Array<{
|
|
175
|
+
name: string;
|
|
176
|
+
type: string;
|
|
177
|
+
}>;
|
|
178
|
+
}>;
|
|
179
|
+
}
|
|
124
180
|
export declare class HubAPI {
|
|
125
181
|
private hubUrl;
|
|
126
182
|
private token;
|
|
@@ -135,6 +191,14 @@ export declare class HubAPI {
|
|
|
135
191
|
listRepos(): Promise<HubRepo[]>;
|
|
136
192
|
getConnectContext(repoFullName: string): Promise<ConnectContext>;
|
|
137
193
|
indexRepo(fullName: string): Promise<IndexResult>;
|
|
194
|
+
/**
|
|
195
|
+
* Mint a CI token for the given repo and fetch the rendered
|
|
196
|
+
* Claude-led workflow YAML in one call. Hub atomically revokes
|
|
197
|
+
* any prior `GitNexus CI` token for this repo and mints a fresh
|
|
198
|
+
* one — calling this repeatedly is safe and idempotent (the prior
|
|
199
|
+
* token stops working, the new one starts).
|
|
200
|
+
*/
|
|
201
|
+
installCiBundle(repoId: string): Promise<InstallCiBundle>;
|
|
138
202
|
getRepo(repoId: string): Promise<RepoDetail>;
|
|
139
203
|
meta(repoId: string): Promise<RepoMeta>;
|
|
140
204
|
sync(repoId: string, params: {
|
|
@@ -158,7 +222,32 @@ export declare class HubAPI {
|
|
|
158
222
|
}>;
|
|
159
223
|
wikiUploadStatus(repoId: string): Promise<WikiUploadStatus>;
|
|
160
224
|
getWikiConfig(): Promise<WikiConfig>;
|
|
161
|
-
wikiGroupingContext(repoId: string): Promise<
|
|
225
|
+
wikiGroupingContext(repoId: string): Promise<WikiGroupingContext>;
|
|
226
|
+
wikiStatus(repoId: string): Promise<{
|
|
227
|
+
status: string;
|
|
228
|
+
fromCommit?: string | null;
|
|
229
|
+
generatedAt?: string | null;
|
|
230
|
+
pageCount?: number;
|
|
231
|
+
generating?: boolean;
|
|
232
|
+
} | null>;
|
|
233
|
+
wikiTree(repoId: string): Promise<{
|
|
234
|
+
tree: Array<{
|
|
235
|
+
name: string;
|
|
236
|
+
slug: string;
|
|
237
|
+
files: string[];
|
|
238
|
+
children?: Array<{
|
|
239
|
+
name: string;
|
|
240
|
+
slug: string;
|
|
241
|
+
files: string[];
|
|
242
|
+
}>;
|
|
243
|
+
}>;
|
|
244
|
+
} | null>;
|
|
245
|
+
wikiPage(repoId: string, slug: string): Promise<{
|
|
246
|
+
slug: string;
|
|
247
|
+
title: string;
|
|
248
|
+
content: string;
|
|
249
|
+
updatedAt: string;
|
|
250
|
+
} | null>;
|
|
162
251
|
wikiLeafContext(repoId: string, moduleName: string, filePaths: string[]): Promise<any>;
|
|
163
252
|
wikiOverviewContext(repoId: string, moduleFiles: Record<string, string[]>): Promise<any>;
|
|
164
253
|
wikiPromptTemplates(repoId: string): Promise<{
|
package/dist/api.js
CHANGED
|
@@ -77,6 +77,16 @@ export class HubAPI {
|
|
|
77
77
|
async indexRepo(fullName) {
|
|
78
78
|
return this.post('/api/repos/public', { fullName });
|
|
79
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Mint a CI token for the given repo and fetch the rendered
|
|
82
|
+
* Claude-led workflow YAML in one call. Hub atomically revokes
|
|
83
|
+
* any prior `GitNexus CI` token for this repo and mints a fresh
|
|
84
|
+
* one — calling this repeatedly is safe and idempotent (the prior
|
|
85
|
+
* token stops working, the new one starts).
|
|
86
|
+
*/
|
|
87
|
+
async installCiBundle(repoId) {
|
|
88
|
+
return this.post(`/api/repos/${repoId}/install-ci-bundle`, {});
|
|
89
|
+
}
|
|
80
90
|
async getRepo(repoId) {
|
|
81
91
|
return this.request(`/api/repos/${repoId}`);
|
|
82
92
|
}
|
|
@@ -143,6 +153,30 @@ export class HubAPI {
|
|
|
143
153
|
async wikiGroupingContext(repoId) {
|
|
144
154
|
return this.request(`/api/repos/${repoId}/wiki/context/grouping`);
|
|
145
155
|
}
|
|
156
|
+
async wikiStatus(repoId) {
|
|
157
|
+
try {
|
|
158
|
+
return await this.request(`/api/repos/${repoId}/wiki/status`);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async wikiTree(repoId) {
|
|
165
|
+
try {
|
|
166
|
+
return await this.request(`/api/repos/${repoId}/wiki/tree`);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async wikiPage(repoId, slug) {
|
|
173
|
+
try {
|
|
174
|
+
return await this.request(`/api/repos/${repoId}/wiki/page/${encodeURIComponent(slug)}`);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
146
180
|
async wikiLeafContext(repoId, moduleName, filePaths) {
|
|
147
181
|
const params = new URLSearchParams({ name: moduleName, files: filePaths.join(',') });
|
|
148
182
|
return this.request(`/api/repos/${repoId}/wiki/context/leaf?${params}`);
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import { removeProjectContext } from './context.js';
|
|
|
18
18
|
import { runSync } from './sync-command.js';
|
|
19
19
|
import { ok, info, warn, fail, resolveAuth, DEFAULT_HUB_URL, EDITORS } from './cli-helpers.js';
|
|
20
20
|
import { runConnect } from './connect-command.js';
|
|
21
|
+
import { runInstallCi } from './install-ci-command.js';
|
|
21
22
|
import { registerWikiCommand } from './wiki/index.js';
|
|
22
23
|
const BANNER = [
|
|
23
24
|
' ██████╗ ██╗████████╗███╗ ██╗███████╗██╗ ██╗██╗ ██╗███████╗',
|
|
@@ -111,6 +112,32 @@ program
|
|
|
111
112
|
process.exit(1);
|
|
112
113
|
}
|
|
113
114
|
});
|
|
115
|
+
// ─── install-ci command ───────────────────────────────────────────
|
|
116
|
+
//
|
|
117
|
+
// Opens a PR on the user's GitHub repo that adds the GitNexus PR
|
|
118
|
+
// review workflow. Mints a long-lived CI token via the Hub, sets it
|
|
119
|
+
// (and CLAUDE_CODE_OAUTH_TOKEN, when a local Claude Code login is
|
|
120
|
+
// detected) as repo Actions secrets, and base64-PUTs the rendered
|
|
121
|
+
// workflow YAML through the user's `gh` CLI. v2 ships a single
|
|
122
|
+
// workflow file; Claude is the primary review mechanism, no opt-out.
|
|
123
|
+
program
|
|
124
|
+
.command('install-ci')
|
|
125
|
+
.description('Open a PR adding the GitNexus PR review workflow + set Actions secrets')
|
|
126
|
+
.option('--hub <url>', `Hub URL (default: saved config or ${DEFAULT_HUB_URL})`)
|
|
127
|
+
.option('--claude-token <token>', 'Claude OAuth token from `claude setup-token` (leaks to argv — prefer the CLAUDE_CODE_OAUTH_TOKEN env var)')
|
|
128
|
+
.option('--claude-token-file <path>', 'Path to a file containing the Claude OAuth token (alternative to --claude-token / env var)')
|
|
129
|
+
.action(async (opts) => {
|
|
130
|
+
try {
|
|
131
|
+
printBanner();
|
|
132
|
+
await runInstallCi(opts);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
console.error('');
|
|
136
|
+
fail(err.message);
|
|
137
|
+
console.error('');
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
114
141
|
// ─── wiki commands ────────────────────────────────────────────────
|
|
115
142
|
registerWikiCommand(program);
|
|
116
143
|
program.parse();
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gnx install-ci` (v2) — opens a PR on the user's GitHub repo that
|
|
3
|
+
* adds the single Claude-led GitNexus CI workflow, using the user's
|
|
4
|
+
* local `gh` CLI rather than a Hub-side OAuth-write grant.
|
|
5
|
+
*
|
|
6
|
+
* v1 shipped three workflow files (gitnexus.yml + claude.yml +
|
|
7
|
+
* claude-code-review.yml) and a `--no-claude` opt-out. v2 collapses
|
|
8
|
+
* to one file and makes Claude mandatory: the workflow's only review
|
|
9
|
+
* mechanism is the `anthropics/claude-code-action@v1` step, primed
|
|
10
|
+
* with a pre-computed Context Pack from `Akon-Labs/gitnexus-check@v2`.
|
|
11
|
+
*
|
|
12
|
+
* Pivot rationale (unchanged from v1): we don't want the Hub to keep
|
|
13
|
+
* a write-capable GitHub token per user. Doing the GitHub write from
|
|
14
|
+
* the user's machine via `gh` keeps the Hub free of `workflow` /
|
|
15
|
+
* `repo` OAuth scopes.
|
|
16
|
+
*
|
|
17
|
+
* Flow:
|
|
18
|
+
* 1. Detect git repo + GitHub remote.
|
|
19
|
+
* 2. Resolve fullName → repoId via `GET /api/repos`.
|
|
20
|
+
* 3. POST install-ci-bundle (mints CI token, renders workflow YAML).
|
|
21
|
+
* 4. `gh` preflight (installed, authed, has `repo` scope).
|
|
22
|
+
* 5. Push branch + PUT workflow file + open PR.
|
|
23
|
+
* 6. Set GITNEXUS_TOKEN + CLAUDE_CODE_OAUTH_TOKEN secrets via stdin.
|
|
24
|
+
*
|
|
25
|
+
* The exported `runInstallCi` is the commander entry point; the
|
|
26
|
+
* GitHub-write helpers (`pushWorkflowAndOpenPr`, `setRepoSecret`)
|
|
27
|
+
* accept an injected `runGh` for unit-testability.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Adapter for `gh` invocations. Real impl shells out via execFileSync;
|
|
31
|
+
* tests replace it with a recording fake. The Promise return shape
|
|
32
|
+
* mirrors what we actually need from `gh` — stdout, stderr, exit code.
|
|
33
|
+
*
|
|
34
|
+
* `input`, when set, is piped to stdin. We use this for `gh secret
|
|
35
|
+
* set NAME --repo OWNER/REPO`, which reads the secret value from stdin
|
|
36
|
+
* when no `--body` / `--body-file` flag is set. That keeps the secret
|
|
37
|
+
* out of argv (`ps` leak) and out of any shell history.
|
|
38
|
+
*/
|
|
39
|
+
export type GhRunner = (args: string[], opts?: {
|
|
40
|
+
input?: string;
|
|
41
|
+
}) => Promise<{
|
|
42
|
+
stdout: string;
|
|
43
|
+
stderr: string;
|
|
44
|
+
code: number;
|
|
45
|
+
}>;
|
|
46
|
+
interface InstallCiOpts {
|
|
47
|
+
hub?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Raw Claude Code OAuth token (output of `claude setup-token`). Lowest
|
|
50
|
+
* priority — leaks to argv / `ps` / shell history. Prefer the env var
|
|
51
|
+
* `CLAUDE_CODE_OAUTH_TOKEN` for routine use; this flag is the escape
|
|
52
|
+
* hatch for one-off / scripted runs.
|
|
53
|
+
*/
|
|
54
|
+
claudeToken?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Path to a file whose contents are the Claude OAuth token. Mid
|
|
57
|
+
* priority — keeps the token off argv. Useful for ops that pre-stage
|
|
58
|
+
* the token in a file rather than the environment.
|
|
59
|
+
*/
|
|
60
|
+
claudeTokenFile?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Entry point for `gnx install-ci`. Imported by index.ts so commander
|
|
64
|
+
* has a stable handle.
|
|
65
|
+
*
|
|
66
|
+
* Output is structured as 6 numbered steps with a final boxed summary.
|
|
67
|
+
* The styling matches the v1 install-ci output that the user explicitly
|
|
68
|
+
* called out as "polished" — we keep the visual contract.
|
|
69
|
+
*/
|
|
70
|
+
export declare function runInstallCi(opts: InstallCiOpts): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Returns the missing scope name (e.g. "repo") if the active gh auth
|
|
73
|
+
* session lacks one we need, else null. We check `repo` only — that
|
|
74
|
+
* scope covers everything install-ci does:
|
|
75
|
+
* - push branches & file content (Contents API)
|
|
76
|
+
* - open PRs (Pulls API)
|
|
77
|
+
* - read+write Actions secrets (`repo` is sufficient for repo-level
|
|
78
|
+
* secrets; only org-level secrets need `admin:org`)
|
|
79
|
+
*
|
|
80
|
+
* Falls back to "no missing scope" if we can't parse `gh auth status`
|
|
81
|
+
* — better to let the real call surface the real error than block on
|
|
82
|
+
* a parser misfire across gh versions.
|
|
83
|
+
*/
|
|
84
|
+
export declare function ghMissingScope(): string | null;
|
|
85
|
+
/**
|
|
86
|
+
* Read the user's Claude Code OAuth token from local credentials so
|
|
87
|
+
* we can auto-set CLAUDE_CODE_OAUTH_TOKEN as a GitHub Actions secret
|
|
88
|
+
* — the same source Claude Code's own `/install-github-app` reads.
|
|
89
|
+
*
|
|
90
|
+
* Lookup order (descending preference — first hit wins):
|
|
91
|
+
*
|
|
92
|
+
* 1. `opts.claudeToken` from `--claude-token=<value>` (lowest priority,
|
|
93
|
+
* flagged for completeness — leaks to argv / `ps` / shell history).
|
|
94
|
+
* 2. `opts.claudeTokenFile` from `--claude-token-file=<path>` — reads
|
|
95
|
+
* the file's trimmed contents. Keeps the token off argv.
|
|
96
|
+
* 3. `process.env.CLAUDE_CODE_OAUTH_TOKEN` — the documented happy path.
|
|
97
|
+
* User runs `claude setup-token` once, exports the result via shell
|
|
98
|
+
* rc, then `gnx install-ci` picks it up automatically on every run.
|
|
99
|
+
*
|
|
100
|
+
* Why we no longer auto-detect from `~/.claude/.credentials.json`: that
|
|
101
|
+
* file's `claudeAiOauth.accessToken` is the user's *interactive session*
|
|
102
|
+
* token (~1h expiry, refreshed in-band). It is NOT a valid CI OAuth
|
|
103
|
+
* token — those are minted explicitly by `claude setup-token` and have
|
|
104
|
+
* the longer-lived headless scope set the GHA expects. Live testing on
|
|
105
|
+
* deer-flow PR #11 confirmed: shipping the session token to the GHA
|
|
106
|
+
* causes "Could not resolve auth credentials" inside the bundled
|
|
107
|
+
* Anthropic SDK. Better to fail loud with a clear instruction than to
|
|
108
|
+
* silently set the wrong token.
|
|
109
|
+
*
|
|
110
|
+
* Returns the trimmed token if any source yields one; null otherwise.
|
|
111
|
+
* The caller prints a clear "run `claude setup-token` first" instruction
|
|
112
|
+
* on null and exits non-zero rather than shipping a half-installed CI.
|
|
113
|
+
*/
|
|
114
|
+
export declare function readClaudeOAuthToken(opts: {
|
|
115
|
+
claudeToken?: string;
|
|
116
|
+
claudeTokenFile?: string;
|
|
117
|
+
}): string | null;
|
|
118
|
+
/**
|
|
119
|
+
* Default `gh` runner — shells out via execFileSync. Captures stdout
|
|
120
|
+
* + stderr separately so callers can inspect each. When `input` is set,
|
|
121
|
+
* pipes it to stdin (used for `gh secret set` so secret material never
|
|
122
|
+
* lands in argv or process listings). Translates non-zero exit into a
|
|
123
|
+
* thrown Error whose message is the stderr text, matching the contract
|
|
124
|
+
* tests expect.
|
|
125
|
+
*/
|
|
126
|
+
export declare const defaultGhRunner: GhRunner;
|
|
127
|
+
/**
|
|
128
|
+
* Set a repository-level Actions secret without ever putting the value
|
|
129
|
+
* in argv. `gh secret set NAME --repo OWNER/REPO` reads the secret
|
|
130
|
+
* value from stdin when neither `--body` nor `--body-file` is set —
|
|
131
|
+
* documented behavior across all supported gh versions.
|
|
132
|
+
*
|
|
133
|
+
* (We deliberately avoid `--body-file -`. That flag was added later
|
|
134
|
+
* than our minimum supported gh version; older installs reject it as
|
|
135
|
+
* "unknown flag" and crash the install-ci flow even though the
|
|
136
|
+
* underlying API supports stdin natively.)
|
|
137
|
+
*/
|
|
138
|
+
export declare function setRepoSecret(runGh: GhRunner, fullName: string, name: string, value: string): Promise<void>;
|
|
139
|
+
export interface PushResult {
|
|
140
|
+
prUrl: string;
|
|
141
|
+
/**
|
|
142
|
+
* True when we found the workflow file already present on the branch
|
|
143
|
+
* with byte-identical content AND a PR already exists. We still
|
|
144
|
+
* surface the PR URL so the user can check on it; the summary box
|
|
145
|
+
* formats this case as "already installed" rather than "completed".
|
|
146
|
+
*/
|
|
147
|
+
alreadyInstalled: boolean;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Push the workflow file to `branchName` and open a PR.
|
|
151
|
+
*
|
|
152
|
+
* Idempotency:
|
|
153
|
+
* - If `branchName` already exists we keep using it (gh's create-branch
|
|
154
|
+
* fails with "Reference already exists" — caught and ignored).
|
|
155
|
+
* - If the workflow file is already present with byte-identical
|
|
156
|
+
* content AND a matching PR is already open from this head, we
|
|
157
|
+
* mark `alreadyInstalled = true` and skip the PUT + create-PR
|
|
158
|
+
* calls. The branch name embeds a timestamp so practical retries
|
|
159
|
+
* never collide, but tests inject a fixed branch name to drive
|
|
160
|
+
* this code path.
|
|
161
|
+
* - If the file exists with different content, we PUT with the
|
|
162
|
+
* existing sha so it's an update.
|
|
163
|
+
* - If a PR creation returns 422 (duplicate), we fall back to the
|
|
164
|
+
* `pulls?head=...&state=all` lookup and surface that URL.
|
|
165
|
+
*
|
|
166
|
+
* `runGh` is injected so tests can drive the full idempotency matrix
|
|
167
|
+
* without spawning a real `gh`.
|
|
168
|
+
*/
|
|
169
|
+
export declare function pushWorkflowAndOpenPr(args: {
|
|
170
|
+
owner: string;
|
|
171
|
+
repo: string;
|
|
172
|
+
branchName: string;
|
|
173
|
+
workflowYaml: string;
|
|
174
|
+
runGh: GhRunner;
|
|
175
|
+
}): Promise<PushResult>;
|
|
176
|
+
export {};
|