pm-github 2026.8.7 → 2026.8.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/index.d.ts +415 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +533 -110
- package/dist/index.js.map +1 -1
- package/dist/projects.d.ts +105 -0
- package/dist/projects.d.ts.map +1 -1
- package/dist/projects.js +67 -19
- package/dist/projects.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.8.10 - 2026-08-10
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Converge changelog generation and verification on replace mode ([pm-github-8f60](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-8f60.toon))
|
|
8
|
+
- The mandatory docstring gate could skip its own scan and still exit zero ([pm-github-wxob](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-wxob.toon))
|
|
9
|
+
|
|
10
|
+
### Other
|
|
11
|
+
|
|
12
|
+
- Adopt the canonical pm-ops docstring gate ([pm-github-pbxd](https://github.com/unbraind/pm-github/blob/main/.agents/pm/tasks/pm-github-pbxd.toon))
|
|
13
|
+
|
|
3
14
|
## 2026.8.7 - 2026-08-07
|
|
4
15
|
|
|
5
16
|
### Other
|
package/dist/index.d.ts
CHANGED
|
@@ -1,31 +1,55 @@
|
|
|
1
1
|
import type { ExtensionApi } from "@unbrained/pm-cli/sdk/authoring";
|
|
2
2
|
import { type BulkItemMutation, type CommitItemMutationsOptions, type CommitItemMutationsResult, type ItemDocument } from "@unbrained/pm-cli/sdk";
|
|
3
|
+
/**
|
|
4
|
+
* One GitHub issue or pull request as returned by the REST issues endpoint.
|
|
5
|
+
*
|
|
6
|
+
* The REST issues endpoint lists PRs alongside issues (a PR is an issue that
|
|
7
|
+
* also carries a `pull_request` object); {@link isDraftPr} and the
|
|
8
|
+
* `includePrs` / `skipDrafts` filters decide which of those survive into an
|
|
9
|
+
* import.
|
|
10
|
+
*/
|
|
3
11
|
export interface GhIssue {
|
|
12
|
+
/** Issue/PR number within its repo (the `N` in `gh:owner/repo#N` provenance). */
|
|
4
13
|
number: number;
|
|
14
|
+
/** One-line issue title, imported as the pm item title. */
|
|
5
15
|
title: string;
|
|
16
|
+
/** Markdown body, imported as the pm item body (`null` when GitHub stored none). */
|
|
6
17
|
body: string | null;
|
|
18
|
+
/** GitHub lifecycle state; mapped to a pm status by {@link mapState}. */
|
|
7
19
|
state: "open" | "closed";
|
|
20
|
+
/** Why a closed issue was closed; `not_planned` maps to `canceled`, not `closed`. */
|
|
8
21
|
state_reason?: "completed" | "not_planned" | "reopened" | null;
|
|
22
|
+
/** Labels on the issue, imported verbatim as pm tags (before any `--label-map`). */
|
|
9
23
|
labels: Array<{
|
|
10
24
|
name: string;
|
|
11
25
|
}>;
|
|
26
|
+
/** Author login, surfaced as a `github_author:` tag when present. */
|
|
12
27
|
user?: {
|
|
13
28
|
login: string;
|
|
14
29
|
} | null;
|
|
30
|
+
/** Assigned user, or `null` (imported as the item assignee when set). */
|
|
15
31
|
assignee: {
|
|
16
32
|
login: string;
|
|
17
33
|
} | null;
|
|
34
|
+
/** Milestone title, or `null` (filtered client-side by `--milestone`). */
|
|
18
35
|
milestone: {
|
|
19
36
|
title: string;
|
|
20
37
|
} | null;
|
|
38
|
+
/** ISO 8601 creation timestamp. */
|
|
21
39
|
created_at: string;
|
|
40
|
+
/** ISO 8601 last-update timestamp, used by the `--since` incremental filter. */
|
|
22
41
|
updated_at: string;
|
|
23
42
|
/** GitHub completion timestamp (`null` while the issue is open); carried as `--completed-at` on close. */
|
|
24
43
|
closed_at?: string | null;
|
|
44
|
+
/** Browser URL of the issue, embedded in the imported description. */
|
|
25
45
|
html_url: string;
|
|
46
|
+
/** Comment count GitHub reports; drives whether comments are fetched at all. */
|
|
26
47
|
comments?: number;
|
|
48
|
+
/** REST URL of the issue's comments collection (paginated when present). */
|
|
27
49
|
comments_url?: string;
|
|
50
|
+
/** Present (opaque) when the issue is actually a pull request; its presence is the PR signal. */
|
|
28
51
|
pull_request?: unknown;
|
|
52
|
+
/** `true` when the issue is a draft pull request (GitHub sets this only on draft PRs). */
|
|
29
53
|
draft?: boolean;
|
|
30
54
|
}
|
|
31
55
|
export interface GhComment {
|
|
@@ -37,19 +61,38 @@ export interface GhComment {
|
|
|
37
61
|
body: string | null;
|
|
38
62
|
}
|
|
39
63
|
type CommentsMode = "body" | "annotations" | "both";
|
|
64
|
+
/**
|
|
65
|
+
* Normalized options governing one GitHub issue import.
|
|
66
|
+
*
|
|
67
|
+
* Produced by {@link parseImportOptions} from the raw CLI flag bag so the rest
|
|
68
|
+
* of the import path consumes a typed shape rather than re-parsing strings.
|
|
69
|
+
*/
|
|
40
70
|
export interface ImportOptions {
|
|
71
|
+
/** GitHub issue state to fetch: `open`, `closed`, or `all`. */
|
|
41
72
|
state: "open" | "closed" | "all";
|
|
73
|
+
/** Comma-separated label filter applied server-side. */
|
|
42
74
|
labels?: string;
|
|
75
|
+
/** ISO timestamp; only issues updated after it are fetched (server-side). */
|
|
43
76
|
since?: string;
|
|
77
|
+
/** Assignee login filter applied server-side. */
|
|
44
78
|
assignee?: string;
|
|
79
|
+
/** Milestone title filter applied client-side (the API keys milestones by number, not title). */
|
|
45
80
|
milestone?: string;
|
|
81
|
+
/** Whether pull requests are included (filtered out by default). */
|
|
46
82
|
includePrs: boolean;
|
|
83
|
+
/** Whether draft pull requests are excluded (only meaningful with {@link includePrs}). */
|
|
47
84
|
skipDrafts: boolean;
|
|
85
|
+
/** Whether fetched comments are also embedded in the item body (legacy behavior). */
|
|
48
86
|
withComments: boolean;
|
|
87
|
+
/** How fetched comments are persisted: body, native annotations, or both. */
|
|
49
88
|
commentsMode: CommentsMode;
|
|
89
|
+
/** pm item type assigned to every created item (default `Issue`). */
|
|
50
90
|
itemType: string;
|
|
91
|
+
/** When true, preview the plan without writing to the tracker or GitHub. */
|
|
51
92
|
dryRun: boolean;
|
|
93
|
+
/** When true, commit the batch as one crash-resumable SDK transaction. */
|
|
52
94
|
atomic: boolean;
|
|
95
|
+
/** When true, run the `--link-deps` dependency-edge second pass after import. */
|
|
53
96
|
linkDeps: boolean;
|
|
54
97
|
}
|
|
55
98
|
type CommitItemMutations = (options: CommitItemMutationsOptions) => Promise<CommitItemMutationsResult>;
|
|
@@ -57,10 +100,21 @@ type NormalizeItemId = (input: string, prefix: string) => string;
|
|
|
57
100
|
type ReadSettings = (pmRoot: string) => Promise<{
|
|
58
101
|
id_prefix?: string;
|
|
59
102
|
}>;
|
|
103
|
+
/**
|
|
104
|
+
* Injectable collaborators for the atomic import path.
|
|
105
|
+
*
|
|
106
|
+
* Lets the crash-resumable transaction be exercised hermetically against fake
|
|
107
|
+
* SDK functions. Every member is optional because production wires the real SDK
|
|
108
|
+
* defaults; a test overrides only the ones it needs.
|
|
109
|
+
*/
|
|
60
110
|
export interface AtomicImportOptions {
|
|
111
|
+
/** Author identity stamped onto the transaction's mutations (default `pm-github`). */
|
|
61
112
|
atomicAuthor?: string;
|
|
113
|
+
/** SDK bulk-mutation primitive that applies and journals the transaction. */
|
|
62
114
|
commitItemMutations?: CommitItemMutations;
|
|
115
|
+
/** SDK id normalizer that derives each item's stable external-key id. */
|
|
63
116
|
normalizeItemId?: NormalizeItemId;
|
|
117
|
+
/** SDK settings reader used to resolve the workspace's id prefix. */
|
|
64
118
|
readSettings?: ReadSettings;
|
|
65
119
|
}
|
|
66
120
|
export interface ImportRunDependencies {
|
|
@@ -80,11 +134,45 @@ export interface ImportRunDependencies {
|
|
|
80
134
|
};
|
|
81
135
|
}
|
|
82
136
|
export declare function resolveGitHubToken(): string | undefined;
|
|
137
|
+
/**
|
|
138
|
+
* Decide whether the Authorization token may be forwarded across a redirect.
|
|
139
|
+
*
|
|
140
|
+
* Only same-origin (scheme + host + port) redirects keep the credential; any
|
|
141
|
+
* origin change drops it so the bearer token can never leak to a third party.
|
|
142
|
+
*
|
|
143
|
+
* @param fromUrl - The URL the redirect originated from.
|
|
144
|
+
* @param toUrl - The redirect target URL.
|
|
145
|
+
* @returns True when both URLs share an origin.
|
|
146
|
+
*/
|
|
83
147
|
export declare function sameOrigin(fromUrl: string, toUrl: string): boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Compute how long (ms) to wait before retrying a rate-limited or transient
|
|
150
|
+
* response.
|
|
151
|
+
*
|
|
152
|
+
* Honors `Retry-After` (seconds) and the primary rate-limit reset window
|
|
153
|
+
* (`X-RateLimit-Remaining: 0` + `X-RateLimit-Reset` epoch), then falls back to
|
|
154
|
+
* exponential backoff. The result is capped so a CLI run is never hung
|
|
155
|
+
* indefinitely.
|
|
156
|
+
*
|
|
157
|
+
* @param headers - Response headers to read the retry hints from.
|
|
158
|
+
* @param attempt - Zero-based retry index feeding the exponential backoff floor.
|
|
159
|
+
* @param nowMs - Current epoch ms, used to measure the reset window's remaining time.
|
|
160
|
+
* @returns The capped wait in milliseconds.
|
|
161
|
+
*/
|
|
84
162
|
export declare function computeBackoffMs(headers: Record<string, string | string[] | undefined>, attempt: number, nowMs?: number): number;
|
|
163
|
+
/**
|
|
164
|
+
* Structured view of GitHub's rate-limit headers off one response.
|
|
165
|
+
*
|
|
166
|
+
* Fields are `undefined` when the corresponding header is absent or non-numeric
|
|
167
|
+
* so callers can degrade gracefully; `reset` is the epoch-second timestamp at
|
|
168
|
+
* which the current window reopens.
|
|
169
|
+
*/
|
|
85
170
|
export interface RateLimitInfo {
|
|
171
|
+
/** Remaining requests in the current window. */
|
|
86
172
|
remaining?: number;
|
|
173
|
+
/** Total requests permitted per window. */
|
|
87
174
|
limit?: number;
|
|
175
|
+
/** Epoch seconds at which the window resets. */
|
|
88
176
|
reset?: number;
|
|
89
177
|
/** True when the remaining quota is at/under the low-water mark. */
|
|
90
178
|
low: boolean;
|
|
@@ -105,22 +193,76 @@ export declare const EXIT_CODE: {
|
|
|
105
193
|
readonly USAGE: 2;
|
|
106
194
|
readonly NOT_FOUND: 3;
|
|
107
195
|
};
|
|
196
|
+
/**
|
|
197
|
+
* Error that carries a semantic process exit code.
|
|
198
|
+
*
|
|
199
|
+
* pm's command runtime treats a thrown error as a cleanly handled non-zero exit
|
|
200
|
+
* only when it exposes a numeric `exitCode`; a plain `Error` instead falls
|
|
201
|
+
* through to the "unhandled" path, which re-invokes the handler (doubling side
|
|
202
|
+
* effects such as a second GitHub fetch) and exits with a generic code. Throwing
|
|
203
|
+
* this routes a failure to a clean, single exit at the chosen code.
|
|
204
|
+
*/
|
|
108
205
|
export declare class CommandError extends Error {
|
|
206
|
+
/** Numeric exit code the runtime propagates to the shell (one of {@link EXIT_CODE}). */
|
|
109
207
|
exitCode: number;
|
|
110
208
|
constructor(message: string, exitCode?: number);
|
|
111
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Build the `gh:owner/repo#N` provenance tag linking a pm item to a GitHub issue.
|
|
212
|
+
*
|
|
213
|
+
* The tag is the idempotency key: it round-trips losslessly through
|
|
214
|
+
* `pm create --tags` / `pm list --json`, so a re-import can find the existing
|
|
215
|
+
* item and UPDATE it instead of duplicating. Provenance also rides on declared
|
|
216
|
+
* schema fields and the description, but the tag is what matching keys on.
|
|
217
|
+
*
|
|
218
|
+
* @param repo - `owner/repo`, lowercased into the tag.
|
|
219
|
+
* @param issueNumber - The issue/PR number.
|
|
220
|
+
* @returns The provenance tag string.
|
|
221
|
+
*/
|
|
112
222
|
export declare function provenanceTag(repo: string, issueNumber: number): string;
|
|
223
|
+
/**
|
|
224
|
+
* Parse a `gh:owner/repo#N` provenance tag back into its repo and issue number.
|
|
225
|
+
*
|
|
226
|
+
* Returns `undefined` for anything that is not a provenance tag, so a caller
|
|
227
|
+
* scanning a tag list can skip foreign tags without a try/catch.
|
|
228
|
+
*
|
|
229
|
+
* @param tag - The candidate tag string.
|
|
230
|
+
* @returns The parsed repo (lowercased) and number, or `undefined`.
|
|
231
|
+
*/
|
|
113
232
|
export declare function parseProvenanceTag(tag: string): {
|
|
114
233
|
repo: string;
|
|
115
234
|
number: number;
|
|
116
235
|
} | undefined;
|
|
236
|
+
/**
|
|
237
|
+
* Build the `github_author:login` tag recording who opened a GitHub issue.
|
|
238
|
+
*
|
|
239
|
+
* Mirrors how provenance rides on tags so the author survives a round-trip.
|
|
240
|
+
* Returns `undefined` when the API supplied no usable login, so an empty tag is
|
|
241
|
+
* never emitted.
|
|
242
|
+
*
|
|
243
|
+
* @param issue - The issue whose author to tag.
|
|
244
|
+
* @returns The author tag, or `undefined`.
|
|
245
|
+
*/
|
|
117
246
|
export declare function authorTag(issue: GhIssue): string | undefined;
|
|
247
|
+
/**
|
|
248
|
+
* The slice of a pm item this package reads and writes.
|
|
249
|
+
*
|
|
250
|
+
* A deliberately loose projection of the full tracker item: only the fields the
|
|
251
|
+
* import / export / sync paths touch, so JSON from `pm list-all` parses without
|
|
252
|
+
* depending on every SDK field.
|
|
253
|
+
*/
|
|
118
254
|
export interface PmItem {
|
|
255
|
+
/** Stable item id (absent for items not yet created). */
|
|
119
256
|
id?: string;
|
|
257
|
+
/** One-line title. */
|
|
120
258
|
title?: string;
|
|
259
|
+
/** Lifecycle status (`open`, `in_progress`, `closed`, `canceled`, …). */
|
|
121
260
|
status?: string;
|
|
261
|
+
/** Long-form markdown body. */
|
|
122
262
|
body?: string;
|
|
263
|
+
/** Short summary shown in listings. */
|
|
123
264
|
description?: string;
|
|
265
|
+
/** Tag set, carrying provenance and labels. */
|
|
124
266
|
tags?: string[];
|
|
125
267
|
}
|
|
126
268
|
export interface ItemScopeResult<TItem> {
|
|
@@ -206,14 +348,26 @@ export declare function importGithubAtomic(pmRoot: string, repo: string, entries
|
|
|
206
348
|
export declare function buildIssuesUrl(repo: string, opts: ImportOptions): string;
|
|
207
349
|
export declare function composeBody(issue: GhIssue, comments: GhComment[]): string;
|
|
208
350
|
export declare const IMPORT_LOCK_TTL_MS_DEFAULT: number;
|
|
351
|
+
/** Default total budget a caller waits to acquire a contended comment-sync lock before giving up and skipping that item's sync. */
|
|
209
352
|
export declare const IMPORT_LOCK_WAIT_MS_DEFAULT = 30000;
|
|
353
|
+
/**
|
|
354
|
+
* Payload JSON written into a comment-sync lock file.
|
|
355
|
+
*
|
|
356
|
+
* Mirrors the pm CLI's own lock payload so the CLI's `pm gc` lock sweep (which
|
|
357
|
+
* reads `ttl_seconds`) treats these locks exactly like its own.
|
|
358
|
+
*/
|
|
210
359
|
export interface ImportLockPayload {
|
|
360
|
+
/** Lock identity (the lock file's basename without `.lock`). */
|
|
211
361
|
id: string;
|
|
362
|
+
/** OS process id of the holder, used to detect a dead owner. */
|
|
212
363
|
pid: number;
|
|
364
|
+
/** Human-readable holder label (always `pm-github`). */
|
|
213
365
|
owner: string;
|
|
214
366
|
/** Unique per acquisition; release() only unlinks a file carrying it. */
|
|
215
367
|
token: string;
|
|
368
|
+
/** ISO timestamp recorded at acquisition, the staleness age base. */
|
|
216
369
|
created_at: string;
|
|
370
|
+
/** Lock lifetime in seconds, read by `pm gc` to sweep abandoned locks. */
|
|
217
371
|
ttl_seconds: number;
|
|
218
372
|
}
|
|
219
373
|
export interface ImportLock {
|
|
@@ -230,25 +384,148 @@ export type ImportLockAcquisition = {
|
|
|
230
384
|
} | {
|
|
231
385
|
status: "degraded";
|
|
232
386
|
};
|
|
387
|
+
/**
|
|
388
|
+
* Resolve the pm data dir from the `pmRoot` a command handler receives.
|
|
389
|
+
*
|
|
390
|
+
* The host may hand either the workspace root (the dir containing `.agents/pm`)
|
|
391
|
+
* or the data dir itself — the pm CLI accepts both for `--path`. This returns
|
|
392
|
+
* the dir that actually holds `settings.json` and `locks/`, defaulting to
|
|
393
|
+
* `pmRoot` unchanged when no nested `.agents/pm` exists.
|
|
394
|
+
*
|
|
395
|
+
* @param pmRoot - The path supplied by the extension host.
|
|
396
|
+
* @returns The resolved pm data directory.
|
|
397
|
+
*/
|
|
233
398
|
export declare function resolvePmDataDir(pmRoot: string): string;
|
|
399
|
+
/**
|
|
400
|
+
* Absolute lock file path for one item's comment-sync critical section.
|
|
401
|
+
*
|
|
402
|
+
* The item id is sanitized defensively: pm ids are already filename-safe, but
|
|
403
|
+
* the lock path must never become a directory-traversal vector if that ever
|
|
404
|
+
* changes.
|
|
405
|
+
*
|
|
406
|
+
* @param pmRoot - Workspace root or pm data dir.
|
|
407
|
+
* @param itemId - The pm item id to serialize.
|
|
408
|
+
* @returns Absolute path under the data dir's `locks/` directory.
|
|
409
|
+
*/
|
|
234
410
|
export declare function importCommentSyncLockPath(pmRoot: string, itemId: string): string;
|
|
411
|
+
/**
|
|
412
|
+
* Staleness TTL for a breaker election sidecar, in ms.
|
|
413
|
+
*
|
|
414
|
+
* A breaker's critical section is a handful of syscalls (re-stat, re-read,
|
|
415
|
+
* unlink), so a crashed breaker's sidecar goes stale in seconds, not minutes;
|
|
416
|
+
* this short window lets a contender clear and re-run a dead election.
|
|
417
|
+
*/
|
|
235
418
|
export declare const IMPORT_LOCK_BREAKER_TTL_MS = 10000;
|
|
419
|
+
/**
|
|
420
|
+
* Acquire the cross-process comment-sync lock for one item.
|
|
421
|
+
*
|
|
422
|
+
* Never throws: every failure mode maps to one of the three acquisition
|
|
423
|
+
* statuses (`acquired`, `contended`, `degraded`) so the caller decides how to
|
|
424
|
+
* degrade. `ttlMs`/`waitMs` are injectable so the wait and staleness windows
|
|
425
|
+
* can be exercised without real timing in tests.
|
|
426
|
+
*
|
|
427
|
+
* @param pmRoot - Workspace root or pm data dir passed to {@link resolvePmDataDir}.
|
|
428
|
+
* @param itemId - The pm item whose comment critical section is serialized.
|
|
429
|
+
* @param opts - Optional overrides for the staleness TTL and wait budget.
|
|
430
|
+
* @returns The acquisition outcome.
|
|
431
|
+
*/
|
|
236
432
|
export declare function acquireImportLock(pmRoot: string, itemId: string, opts?: {
|
|
237
433
|
ttlMs?: number;
|
|
238
434
|
waitMs?: number;
|
|
239
435
|
}): Promise<ImportLockAcquisition>;
|
|
436
|
+
/**
|
|
437
|
+
* Matches the hidden HTML-comment marker carrying a synced GitHub comment id.
|
|
438
|
+
*
|
|
439
|
+
* HTML comments are invisible in rendered markdown but survive `pm comments`
|
|
440
|
+
* storage verbatim, so the id embedded in the marker is the stable dedupe key.
|
|
441
|
+
*/
|
|
240
442
|
export declare const COMMENT_MARKER_REGEX: RegExp;
|
|
443
|
+
/**
|
|
444
|
+
* Build the text for one native pm comment from a GitHub comment.
|
|
445
|
+
*
|
|
446
|
+
* Appends the marker so a later re-sync can de-duplicate on the GitHub comment
|
|
447
|
+
* id (see {@link COMMENT_MARKER_REGEX}).
|
|
448
|
+
*
|
|
449
|
+
* @param comment - The GitHub comment to render.
|
|
450
|
+
* @returns The comment text with the trailing id marker.
|
|
451
|
+
*/
|
|
241
452
|
export declare function buildCommentText(comment: GhComment): string;
|
|
453
|
+
/**
|
|
454
|
+
* Collect the GitHub comment ids already synced into an item's native comments.
|
|
455
|
+
*
|
|
456
|
+
* Scans each stored comment's text for the stable marker. Returns the set of
|
|
457
|
+
* synced ids (empty when none match, e.g. for hand-written pm comments).
|
|
458
|
+
*
|
|
459
|
+
* @param stored - The item's existing native comments (each may carry marker text).
|
|
460
|
+
* @returns The set of already-synced GitHub comment ids.
|
|
461
|
+
*/
|
|
242
462
|
export declare function extractSyncedCommentIds(stored: {
|
|
243
463
|
text?: string;
|
|
244
464
|
}[]): Set<number>;
|
|
465
|
+
/**
|
|
466
|
+
* Extract the newly-created item id from `pm create --json` stdout.
|
|
467
|
+
*
|
|
468
|
+
* The CLI emits a FLAT envelope (`{ "id": "pm-xxxx", … }`) with no `item`
|
|
469
|
+
* wrapper, so this reads `parsed.id` directly. Returns `undefined` when the
|
|
470
|
+
* output genuinely cannot be parsed, so callers skip-with-warning instead of
|
|
471
|
+
* crashing the whole import.
|
|
472
|
+
*
|
|
473
|
+
* @param stdout - Raw `pm create --json` output.
|
|
474
|
+
* @returns The created item id, or `undefined`.
|
|
475
|
+
*/
|
|
245
476
|
export declare function parseCreatedItemId(stdout: string): string | undefined;
|
|
477
|
+
/**
|
|
478
|
+
* Sync GitHub issue comments into a pm item's native comments collection.
|
|
479
|
+
*
|
|
480
|
+
* Idempotent: comments already present (matched by their marker id) are skipped,
|
|
481
|
+
* so re-running import never duplicates. Each GitHub comment becomes one pm
|
|
482
|
+
* comment authored by the GitHub login. Failures are logged and never abort the
|
|
483
|
+
* import; a contended or unavailable lock degrades gracefully (see
|
|
484
|
+
* {@link acquireImportLock}).
|
|
485
|
+
*
|
|
486
|
+
* @param itemId - The pm item to append comments to.
|
|
487
|
+
* @param comments - The GitHub comments to sync.
|
|
488
|
+
* @param pmRoot - Workspace root or pm data dir.
|
|
489
|
+
* @param issueNumber - The source issue number (for log prefixes).
|
|
490
|
+
* @returns How many comments were added and how many skipped as duplicates.
|
|
491
|
+
*/
|
|
246
492
|
export declare function syncGithubCommentsToAnnotations(itemId: string, comments: GhComment[], pmRoot: string, issueNumber: number): Promise<{
|
|
247
493
|
added: number;
|
|
248
494
|
skipped: number;
|
|
249
495
|
}>;
|
|
496
|
+
/**
|
|
497
|
+
* Whether a GitHub issue node is a draft pull request.
|
|
498
|
+
*
|
|
499
|
+
* A draft PR is an issue that is BOTH a pull request and flagged `draft: true`;
|
|
500
|
+
* plain issues are never drafts.
|
|
501
|
+
*
|
|
502
|
+
* @param issue - The issue node to test.
|
|
503
|
+
* @returns True only for draft pull requests.
|
|
504
|
+
*/
|
|
250
505
|
export declare function isDraftPr(issue: GhIssue): boolean;
|
|
506
|
+
/**
|
|
507
|
+
* Apply the client-side import filters the REST API cannot express.
|
|
508
|
+
*
|
|
509
|
+
* Drops pull requests (unless `includePrs`), draft PRs (when `skipDrafts`), and
|
|
510
|
+
* items whose milestone title does not match (the API keys milestones by number,
|
|
511
|
+
* not title). Returns the surviving issues in input order.
|
|
512
|
+
*
|
|
513
|
+
* @param issues - Fetched issues to narrow.
|
|
514
|
+
* @param opts - Import options carrying the filter flags.
|
|
515
|
+
* @returns The filtered issue list.
|
|
516
|
+
*/
|
|
251
517
|
export declare function applyClientFilters(issues: GhIssue[], opts: ImportOptions): GhIssue[];
|
|
518
|
+
/**
|
|
519
|
+
* Normalize the raw CLI flag bag into a typed {@link ImportOptions}.
|
|
520
|
+
*
|
|
521
|
+
* Resolves the state filter, labels, assignee, milestone, the `--since` window,
|
|
522
|
+
* PR inclusion, draft skipping, and the comment-fetching mode, throwing a
|
|
523
|
+
* {@link CommandError} with a usage code on a malformed `--since` or
|
|
524
|
+
* `--comments-mode`.
|
|
525
|
+
*
|
|
526
|
+
* @param options - The raw option object from the command handler.
|
|
527
|
+
* @returns The normalized import options.
|
|
528
|
+
*/
|
|
252
529
|
export declare function parseImportOptions(options: Record<string, unknown>): ImportOptions;
|
|
253
530
|
/** pm dependency kinds a GitHub body reference can map to. */
|
|
254
531
|
export type DepRefKind = "blocked_by" | "blocks";
|
|
@@ -318,6 +595,20 @@ export declare function countDependencyRefCandidates(repo: string, issues: reado
|
|
|
318
595
|
* `failures`/warnings so the import result stays truthful.
|
|
319
596
|
*/
|
|
320
597
|
export declare function linkImportedDependencies(repo: string, issues: readonly GhIssue[], pmRoot: string, deps?: ImportRunDependencies): Promise<DepLinkResult>;
|
|
598
|
+
/**
|
|
599
|
+
* Run the full GitHub issue import flow.
|
|
600
|
+
*
|
|
601
|
+
* Idempotent: items already linked (by provenance tag) to a fetched issue are
|
|
602
|
+
* UPDATEd; new issues are created. Honors `--atomic` (one crash-resumable
|
|
603
|
+
* transaction) versus the per-item `pm` mutation path, optional `--link-deps`,
|
|
604
|
+
* and `--dry-run`. Returns a structured result and throws {@link CommandError}
|
|
605
|
+
* (with a semantic exit code) on failure.
|
|
606
|
+
*
|
|
607
|
+
* @param repoArg - The `owner/repo` to import from.
|
|
608
|
+
* @param pmRoot - Workspace root or pm data dir.
|
|
609
|
+
* @param opts - Normalized import options.
|
|
610
|
+
* @param deps - Injectable collaborators (token resolver, fetchers, etc.) for tests.
|
|
611
|
+
*/
|
|
321
612
|
export declare function runImport(repoArg: string | undefined, pmRoot: string, opts: ImportOptions, dependencies?: ImportRunDependencies): Promise<{
|
|
322
613
|
imported: number;
|
|
323
614
|
updated: number;
|
|
@@ -341,11 +632,22 @@ export declare function runImport(repoArg: string | undefined, pmRoot: string, o
|
|
|
341
632
|
atomic?: boolean | undefined;
|
|
342
633
|
wouldLinkDependencyCandidates?: number | undefined;
|
|
343
634
|
}>;
|
|
635
|
+
/**
|
|
636
|
+
* One pm → GitHub issue state-change proposed by {@link planSync}.
|
|
637
|
+
*
|
|
638
|
+
* `from`/`to` are GitHub issue states; the executor only PATCHes when GitHub's
|
|
639
|
+
* live state disagrees with `to`.
|
|
640
|
+
*/
|
|
344
641
|
export interface SyncPlanEntry {
|
|
642
|
+
/** pm item id driving the proposed change. */
|
|
345
643
|
id: string;
|
|
644
|
+
/** GitHub issue number to PATCH. */
|
|
346
645
|
number: number;
|
|
646
|
+
/** Item title, surfaced in progress output. */
|
|
347
647
|
title: string;
|
|
648
|
+
/** GitHub state assumed before the write (the inverse of `to`; re-checked live). */
|
|
348
649
|
from: "open" | "closed";
|
|
650
|
+
/** Desired GitHub state derived from the pm status. */
|
|
349
651
|
to: "open" | "closed";
|
|
350
652
|
}
|
|
351
653
|
export declare function planSync(items: PmItem[], repo: string): SyncPlanEntry[];
|
|
@@ -377,27 +679,99 @@ export interface ExportApplyResult {
|
|
|
377
679
|
failures: ExportApplyFailure[];
|
|
378
680
|
}
|
|
379
681
|
export type ExportRequestFn = (method: string, url: string, token: string | undefined, payload?: string) => Promise<unknown>;
|
|
682
|
+
/**
|
|
683
|
+
* Apply an already-built export plan to GitHub, one issue at a time.
|
|
684
|
+
*
|
|
685
|
+
* Each create/update is isolated: a single failed write (e.g. a 422 for a label
|
|
686
|
+
* that does not exist on the repo) is recorded and the loop CONTINUES with the
|
|
687
|
+
* remaining items — it never abandons the rest of the batch. Pure aside from the
|
|
688
|
+
* injected `requestFn`, so the per-item isolation is directly unit-testable
|
|
689
|
+
* without real network I/O.
|
|
690
|
+
*/
|
|
380
691
|
export declare function applyExportPlan(plan: ExportPlanEntry[], repo: string, token: string | undefined, requestFn: ExportRequestFn): Promise<ExportApplyResult>;
|
|
692
|
+
/**
|
|
693
|
+
* Decide the exit status of a completed `export --apply` batch.
|
|
694
|
+
*
|
|
695
|
+
* Per-item continuation is intentional (one bad item never aborts the batch),
|
|
696
|
+
* but the batch as a whole must still report honestly: a non-empty plan that
|
|
697
|
+
* wrote NOTHING while recording at least one failure is a total failure, so it
|
|
698
|
+
* returns a {@link CommandError} to throw; otherwise `undefined` (exit 0).
|
|
699
|
+
*
|
|
700
|
+
* @param plan - The plan that was applied.
|
|
701
|
+
* @param result - What the apply loop actually wrote.
|
|
702
|
+
* @param repo - Target repo, included in the failure message.
|
|
703
|
+
* @returns A `CommandError` to throw on total failure, else `undefined`.
|
|
704
|
+
*/
|
|
381
705
|
export declare function applyOutcomeError(plan: ExportPlanEntry[], result: ExportApplyResult, repo: string): CommandError | undefined;
|
|
706
|
+
/**
|
|
707
|
+
* Build the GitHub Search-API URL for issues in one repo matching a free-text query.
|
|
708
|
+
*
|
|
709
|
+
* Restricted to `type:issue repo:<repo>` so a search never leaks across repos.
|
|
710
|
+
*
|
|
711
|
+
* @param repo - The `owner/repo` to search within.
|
|
712
|
+
* @param query - Free-text query string.
|
|
713
|
+
* @returns The encoded search URL.
|
|
714
|
+
*/
|
|
382
715
|
export declare function buildSearchUrl(repo: string, query: string): string;
|
|
716
|
+
/**
|
|
717
|
+
* Map GitHub search result numbers to local pm-item hits.
|
|
718
|
+
*
|
|
719
|
+
* For every returned issue number, looks up the pm item carrying the matching
|
|
720
|
+
* `gh:repo#N` provenance tag; only locally-present items become hits (the
|
|
721
|
+
* runtime would drop the rest anyway). Score preserves GitHub's ranking,
|
|
722
|
+
* normalized into `(0, 1]` so hits clear pm's default threshold.
|
|
723
|
+
*
|
|
724
|
+
* @param matchedNumbers - Issue numbers GitHub returned, best-first.
|
|
725
|
+
* @param repo - The repo the search ran against.
|
|
726
|
+
* @param itemsByProvenance - Local items keyed `owner/repo#N`.
|
|
727
|
+
* @returns One hit per locally-present matching item.
|
|
728
|
+
*/
|
|
383
729
|
export declare function mapSearchHits(matchedNumbers: number[], repo: string, itemsByProvenance: Map<string, PmItem>): Array<{
|
|
384
730
|
id: string;
|
|
385
731
|
score: number;
|
|
386
732
|
matched_fields: string[];
|
|
387
733
|
}>;
|
|
734
|
+
/**
|
|
735
|
+
* Resolve the search target repo for the GitHub search provider.
|
|
736
|
+
*
|
|
737
|
+
* An explicit option wins, then the `PM_GITHUB_REPO` env var so a workspace can
|
|
738
|
+
* pin its upstream. Returns `undefined` when neither yields an `owner/repo`.
|
|
739
|
+
*
|
|
740
|
+
* @param options - The raw option object from the search context.
|
|
741
|
+
* @returns The resolved `owner/repo`, or `undefined`.
|
|
742
|
+
*/
|
|
388
743
|
export declare function resolveSearchRepo(options: Record<string, unknown>): string | undefined;
|
|
744
|
+
/**
|
|
745
|
+
* Structured result of `pm github validate`.
|
|
746
|
+
*
|
|
747
|
+
* Reports the resolved token source, whether the `gh` CLI is installed, the
|
|
748
|
+
* current rate-limit snapshot, and (when a repo is given) its reachability.
|
|
749
|
+
* `ok` is the overall pass/fail; `messages` carries the human-readable detail.
|
|
750
|
+
*/
|
|
389
751
|
export interface ValidateReport {
|
|
752
|
+
/** Overall pass/fail for the validation. */
|
|
390
753
|
ok: boolean;
|
|
754
|
+
/** Whether the `gh` CLI was found on PATH. */
|
|
391
755
|
gh_cli: boolean;
|
|
756
|
+
/** Whether any GitHub token was resolvable. */
|
|
392
757
|
token: boolean;
|
|
758
|
+
/** Where the token came from: `env`, `gh`, or `none`. */
|
|
393
759
|
token_source: "env" | "gh" | "none";
|
|
760
|
+
/** The repo examined, when one was supplied. */
|
|
394
761
|
repo?: string;
|
|
762
|
+
/** Whether the repo responded 2xx. */
|
|
395
763
|
repo_accessible?: boolean;
|
|
764
|
+
/** Raw HTTP status the repo check returned. */
|
|
396
765
|
repo_status?: number;
|
|
766
|
+
/** Remaining requests in the current rate-limit window. */
|
|
397
767
|
rate_limit_remaining?: number;
|
|
768
|
+
/** Total requests permitted per window. */
|
|
398
769
|
rate_limit_limit?: number;
|
|
770
|
+
/** Epoch seconds at which the window resets. */
|
|
399
771
|
rate_limit_reset?: number;
|
|
772
|
+
/** Whether the remaining quota is at/under the low-water mark. */
|
|
400
773
|
rate_limit_low?: boolean;
|
|
774
|
+
/** Human-readable diagnostic lines. */
|
|
401
775
|
messages: string[];
|
|
402
776
|
}
|
|
403
777
|
/** One project-summary node in a `projectsV2` connection. */
|
|
@@ -427,8 +801,15 @@ interface GraphqlListOwnerProjectsData {
|
|
|
427
801
|
projectsV2?: GraphqlProjectsV2Connection | null;
|
|
428
802
|
} | null;
|
|
429
803
|
}
|
|
804
|
+
/**
|
|
805
|
+
* One page of a `projectsV2` connection as returned by GitHub GraphQL.
|
|
806
|
+
*
|
|
807
|
+
* Carries the page's nodes and the cursor threading needed to fetch the rest.
|
|
808
|
+
*/
|
|
430
809
|
export interface ProjectsV2Page {
|
|
810
|
+
/** Project-summary nodes on this page (null entries are redacted/inaccessible). */
|
|
431
811
|
nodes?: Array<GraphqlProjectsV2Node | null>;
|
|
812
|
+
/** Relay pagination info for the next page. */
|
|
432
813
|
pageInfo?: {
|
|
433
814
|
hasNextPage?: boolean;
|
|
434
815
|
endCursor?: string;
|
|
@@ -436,7 +817,31 @@ export interface ProjectsV2Page {
|
|
|
436
817
|
}
|
|
437
818
|
export declare function collectProjectsV2Pages(fetchPage: (cursor: string | undefined) => Promise<ProjectsV2Page | null | undefined>): Promise<GraphqlProjectsV2Node[]>;
|
|
438
819
|
export type GraphQLTransport = (query: string, variables: Record<string, unknown>) => Promise<GraphqlListOwnerProjectsData>;
|
|
820
|
+
/**
|
|
821
|
+
* Resolve and paginate the `projectsV2` connection for a user-or-org owner.
|
|
822
|
+
*
|
|
823
|
+
* A login is either a user or an organization, never both; querying both in one
|
|
824
|
+
* request resolves the owner type without an extra round-trip, then this keeps
|
|
825
|
+
* paginating the connection that actually exists so owners with more than 100
|
|
826
|
+
* projects are fully listed instead of silently truncated.
|
|
827
|
+
*
|
|
828
|
+
* @param owner - The user or org login.
|
|
829
|
+
* @param graphQL - Injectable transport (query + variables → data), for tests.
|
|
830
|
+
* @returns Every project-summary node for the owner.
|
|
831
|
+
*/
|
|
439
832
|
export declare function listOwnerProjectsV2Nodes(owner: string, graphQL: GraphQLTransport): Promise<GraphqlProjectsV2Node[]>;
|
|
833
|
+
/**
|
|
834
|
+
* Build the `pm` CLI argv that applies one pull (project → pm) status transition.
|
|
835
|
+
*
|
|
836
|
+
* Pure so the pm lifecycle contract is unit-testable without spawning `pm`:
|
|
837
|
+
* `closed` goes through `pm close`; `canceled` keeps its distinct terminal state
|
|
838
|
+
* via `pm update --status canceled` with `--close-reason`; everything else is a
|
|
839
|
+
* plain `pm update --status`. See the long note above for the pm CLI contracts.
|
|
840
|
+
*
|
|
841
|
+
* @param entry - The pull-plan entry to render argv for.
|
|
842
|
+
* @param pmRoot - Workspace root for the `pm` invocation.
|
|
843
|
+
* @returns The argv array.
|
|
844
|
+
*/
|
|
440
845
|
export declare function buildPullEntryArgs(entry: PullPlanEntryLike, pmRoot: string): string[];
|
|
441
846
|
interface PullPlanEntryLike {
|
|
442
847
|
itemId: string;
|
|
@@ -445,6 +850,16 @@ interface PullPlanEntryLike {
|
|
|
445
850
|
fromStatus: string;
|
|
446
851
|
toStatus: string;
|
|
447
852
|
}
|
|
853
|
+
/**
|
|
854
|
+
* Report whether a github subcommand will mutate GitHub.
|
|
855
|
+
*
|
|
856
|
+
* Feeds the host CLI's "mutating command" guard, accounting for the `--dry-run`
|
|
857
|
+
* and `--apply` overrides that turn an otherwise-mutating command into a preview.
|
|
858
|
+
*
|
|
859
|
+
* @param command - The subcommand string (e.g. `github sync`).
|
|
860
|
+
* @param options - The raw option object from the command handler.
|
|
861
|
+
* @returns True when the command will perform real GitHub writes.
|
|
862
|
+
*/
|
|
448
863
|
export declare function isMutatingGithubCommand(command: string, options: Record<string, unknown>): boolean;
|
|
449
864
|
declare const _default: {
|
|
450
865
|
name: string;
|