@buildinternet/uploads 0.42.2 → 0.44.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/cli-help.js +1 -1
- package/dist/client.d.ts +36 -1
- package/dist/client.js +15 -1
- package/dist/commands/config.js +1 -1
- package/dist/commands/hook.d.ts +6 -2
- package/dist/commands/hook.js +31 -9
- package/dist/commands/screenshot.js +96 -20
- package/dist/commands.d.ts +76 -0
- package/dist/commands.js +326 -78
- package/dist/comment-config.generated.d.ts +6 -0
- package/dist/comment-config.generated.js +27 -1
- package/dist/comment-render.generated.d.ts +4 -1
- package/dist/comment-render.generated.js +15 -2
- package/dist/config-file.d.ts +4 -1
- package/dist/config-file.js +10 -0
- package/dist/mcp/output-schemas.js +24 -1
- package/dist/mcp/tools.js +154 -49
- package/dist/project-context-nudge.d.ts +9 -0
- package/dist/project-context-nudge.js +39 -0
- package/package.json +1 -1
package/dist/cli-help.js
CHANGED
|
@@ -131,7 +131,7 @@ ${section(style, "Config")} ${style.muted("(first match wins, per key):")}
|
|
|
131
131
|
${section(style, "Workspace")} ${style.muted("(within config layers):")}
|
|
132
132
|
--workspace, -w override — global (before command) or per-command (after)
|
|
133
133
|
UPLOADS_WORKSPACE env / config file
|
|
134
|
-
(else inferred from token up_<name>_…,
|
|
134
|
+
(else inferred from token up_<name>_…, or the API key's only workspace)
|
|
135
135
|
|
|
136
136
|
${section(style, "Other globals")} ${style.muted("(before command):")}
|
|
137
137
|
--api-url <url> default: https://api.uploads.sh
|
package/dist/client.d.ts
CHANGED
|
@@ -305,6 +305,32 @@ export interface PromoteBranchAttachmentsResult {
|
|
|
305
305
|
promoted: string[];
|
|
306
306
|
skipped: PromoteSkip[];
|
|
307
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* `POST /v1/workspaces/:workspace/github/attach` request/response (server
|
|
310
|
+
* contract, issue #702). `source` is a raw object key or an uploads.sh URL
|
|
311
|
+
* (storage host, embed host, or `/f/` page). Exactly one of `pr`/`issue` is
|
|
312
|
+
* required. Copy by default; `move: true` deletes the source object after a
|
|
313
|
+
* successful copy.
|
|
314
|
+
*/
|
|
315
|
+
export interface AttachExistingOptions {
|
|
316
|
+
source: string;
|
|
317
|
+
repo: string;
|
|
318
|
+
pr?: number;
|
|
319
|
+
issue?: number;
|
|
320
|
+
move?: boolean;
|
|
321
|
+
filename?: string;
|
|
322
|
+
}
|
|
323
|
+
export interface AttachExistingResult {
|
|
324
|
+
key: string;
|
|
325
|
+
url: string | null;
|
|
326
|
+
embedUrl: string | null;
|
|
327
|
+
pageUrl?: string;
|
|
328
|
+
moved: boolean;
|
|
329
|
+
source: {
|
|
330
|
+
key: string;
|
|
331
|
+
};
|
|
332
|
+
comment: GithubCommentResult;
|
|
333
|
+
}
|
|
308
334
|
/** `GET`/`POST /v1/workspaces/:workspace/github/link` result (server contract, phase 4b). */
|
|
309
335
|
export interface GithubLinkResult {
|
|
310
336
|
repo: string;
|
|
@@ -565,7 +591,7 @@ export declare function mintWorkspaceToken(apiUrl: string, accessToken: string,
|
|
|
565
591
|
workspace: string;
|
|
566
592
|
scopes?: Array<TokenScope>;
|
|
567
593
|
label?: string;
|
|
568
|
-
ttlSeconds?: number;
|
|
594
|
+
ttlSeconds?: number | null;
|
|
569
595
|
}): Promise<MintTokenResult>;
|
|
570
596
|
/**
|
|
571
597
|
* Parse API error bodies. Prefers the nested envelope
|
|
@@ -670,6 +696,15 @@ export declare function createUploadsClient(config: UploadsClientConfig): {
|
|
|
670
696
|
* that doesn't have this route yet, as "nothing promoted").
|
|
671
697
|
*/
|
|
672
698
|
promoteBranchAttachments(opts: PromoteBranchAttachmentsOptions): Promise<PromoteBranchAttachmentsResult>;
|
|
699
|
+
/**
|
|
700
|
+
* Attach an already-uploaded object to a PR/issue via a server-side copy
|
|
701
|
+
* (issue #702) — see `AttachExistingOptions`. Throws `UploadsError` on
|
|
702
|
+
* any failure (including a 404 from an older/self-hosted server without
|
|
703
|
+
* this route, or `source_not_found`) — unlike the degrade-safe promote/
|
|
704
|
+
* comment calls, this is the CLI's only way to move the object, so a
|
|
705
|
+
* failure must be visible, not silently swallowed.
|
|
706
|
+
*/
|
|
707
|
+
attachExisting(opts: AttachExistingOptions): Promise<AttachExistingResult>;
|
|
673
708
|
/** Current binding for `repo`, or `{ linked: false }` if unclaimed. Throws
|
|
674
709
|
* `UploadsError` (status 404) on an older/self-hosted server without this
|
|
675
710
|
* route — callers treat that as "bindings unsupported". */
|
package/dist/client.js
CHANGED
|
@@ -179,7 +179,7 @@ export function mintWorkspaceToken(apiUrl, accessToken, input) {
|
|
|
179
179
|
body: JSON.stringify({
|
|
180
180
|
grants: [{ workspace: input.workspace, ...(input.scopes ? { scopes: input.scopes } : {}) }],
|
|
181
181
|
...(input.label ? { label: input.label } : {}),
|
|
182
|
-
...(input.ttlSeconds ? { ttlSeconds: input.ttlSeconds } : {}),
|
|
182
|
+
...(input.ttlSeconds !== undefined ? { ttlSeconds: input.ttlSeconds } : {}),
|
|
183
183
|
}),
|
|
184
184
|
});
|
|
185
185
|
}
|
|
@@ -582,6 +582,20 @@ export function createUploadsClient(config) {
|
|
|
582
582
|
headers: { "Content-Type": "application/json" },
|
|
583
583
|
});
|
|
584
584
|
},
|
|
585
|
+
/**
|
|
586
|
+
* Attach an already-uploaded object to a PR/issue via a server-side copy
|
|
587
|
+
* (issue #702) — see `AttachExistingOptions`. Throws `UploadsError` on
|
|
588
|
+
* any failure (including a 404 from an older/self-hosted server without
|
|
589
|
+
* this route, or `source_not_found`) — unlike the degrade-safe promote/
|
|
590
|
+
* comment calls, this is the CLI's only way to move the object, so a
|
|
591
|
+
* failure must be visible, not silently swallowed.
|
|
592
|
+
*/
|
|
593
|
+
async attachExisting(opts) {
|
|
594
|
+
return request("POST", `${config.apiUrl}/v1/workspaces/${encodeURIComponent(config.workspace)}/github/attach`, {
|
|
595
|
+
body: new TextEncoder().encode(JSON.stringify(opts)),
|
|
596
|
+
headers: { "Content-Type": "application/json" },
|
|
597
|
+
});
|
|
598
|
+
},
|
|
585
599
|
/** Current binding for `repo`, or `{ linked: false }` if unclaimed. Throws
|
|
586
600
|
* `UploadsError` (status 404) on an older/self-hosted server without this
|
|
587
601
|
* route — callers treat that as "bindings unsupported". */
|
package/dist/commands/config.js
CHANGED
|
@@ -18,7 +18,7 @@ Subcommands:
|
|
|
18
18
|
Keys:
|
|
19
19
|
UPLOADS_API_URL API base URL (default: ${DEFAULT_API_URL})
|
|
20
20
|
UPLOADS_WORKSPACE Workspace / bucket tenant (default: ${DEFAULT_WORKSPACE})
|
|
21
|
-
UPLOADS_TOKEN Bearer token
|
|
21
|
+
UPLOADS_TOKEN Bearer token (up_<workspace>_…)
|
|
22
22
|
UPLOADS_SESSION_TOKEN Device-flow session (CLI version on account sessions)
|
|
23
23
|
UPLOADS_DEFAULT_PREFIX Default key prefix for put/list
|
|
24
24
|
UPLOADS_DEFAULT_REPO Default repo segment for put
|
package/dist/commands/hook.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `uploads hook pre-pr-screenshot` — agent PreToolUse / beforeShellExecution
|
|
3
|
-
* handler
|
|
4
|
-
* files
|
|
3
|
+
* handler, triggered on `gh pr create`. Two advisories, mutually exclusive:
|
|
4
|
+
* - staged-but-unattached files exist for the branch (any reason they got
|
|
5
|
+
* there) → a promote suggestion (issue #700): `uploads attach --promote`
|
|
6
|
+
* once the PR this command is about to open exists.
|
|
7
|
+
* - nothing is staged, but the branch touches UI files → the original
|
|
8
|
+
* (issue #379) "consider staging screenshots" advisory.
|
|
5
9
|
*
|
|
6
10
|
* Always fail-open. Disable with UPLOADS_HOOK_DISABLE=1.
|
|
7
11
|
*/
|
package/dist/commands/hook.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `uploads hook pre-pr-screenshot` — agent PreToolUse / beforeShellExecution
|
|
3
|
-
* handler
|
|
4
|
-
* files
|
|
3
|
+
* handler, triggered on `gh pr create`. Two advisories, mutually exclusive:
|
|
4
|
+
* - staged-but-unattached files exist for the branch (any reason they got
|
|
5
|
+
* there) → a promote suggestion (issue #700): `uploads attach --promote`
|
|
6
|
+
* once the PR this command is about to open exists.
|
|
7
|
+
* - nothing is staged, but the branch touches UI files → the original
|
|
8
|
+
* (issue #379) "consider staging screenshots" advisory.
|
|
5
9
|
*
|
|
6
10
|
* Always fail-open. Disable with UPLOADS_HOOK_DISABLE=1.
|
|
7
11
|
*/
|
|
@@ -22,9 +26,12 @@ Invoked by Claude Code / Codex / Grok / Cursor hooks. Never blocks.
|
|
|
22
26
|
Harness manifests no-op (exit 0, no output) when this binary is not on PATH.
|
|
23
27
|
|
|
24
28
|
pre-pr-screenshot
|
|
25
|
-
If the shell command is \`gh pr create
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
If the shell command is \`gh pr create\`:
|
|
30
|
+
- staged-but-unattached files exist for the branch → suggest promoting
|
|
31
|
+
them into the PR's managed comment once it exists (issue #700):
|
|
32
|
+
\`uploads attach --promote --pr <num>\`.
|
|
33
|
+
- otherwise, if the branch touches UI files → suggest staging with
|
|
34
|
+
\`uploads attach … --branch\`.
|
|
28
35
|
|
|
29
36
|
Disable with UPLOADS_HOOK_DISABLE=1.
|
|
30
37
|
`;
|
|
@@ -158,14 +165,29 @@ export async function runPrePrScreenshot(deps) {
|
|
|
158
165
|
const branch = git.branch();
|
|
159
166
|
if (!branch)
|
|
160
167
|
return null;
|
|
168
|
+
const staged = await (deps.countStaged ?? defaultCountStaged)(branch);
|
|
169
|
+
if (staged === null)
|
|
170
|
+
return null; // error/unconfigured → fail open
|
|
171
|
+
// Promote suggestion (issue #700): staged-but-unattached files already
|
|
172
|
+
// exist for this branch right as its PR is about to open. The PR doesn't
|
|
173
|
+
// exist yet at this PreToolUse point, so its number isn't knowable here —
|
|
174
|
+
// the wording still gives the exact command shape, and a bare
|
|
175
|
+
// `attach --promote` (which infers the PR from the branch) works too.
|
|
176
|
+
if (staged > 0) {
|
|
177
|
+
const fork = (deps.isFork ?? (() => defaultIsFork(cwd)))();
|
|
178
|
+
const forkNote = fork === true
|
|
179
|
+
? " Note: this looks like a fork branch, so staged screenshots won't auto-promote into the PR comment yet (see issue #317) — attach them manually if you use uploads."
|
|
180
|
+
: "";
|
|
181
|
+
const message = `${staged} file${staged === 1 ? "" : "s"} staged for branch '${branch}' on uploads.sh ` +
|
|
182
|
+
`${staged === 1 ? "isn't" : "aren't"} attached to a pull request yet. Once this PR opens, run ` +
|
|
183
|
+
"`uploads attach --promote --pr <num>` (or a bare `uploads attach --promote`, which infers " +
|
|
184
|
+
`the PR from the branch) to collect ${staged === 1 ? "it" : "them"} into the managed attachments comment.${forkNote}`;
|
|
185
|
+
return formatAdvisory(message, isCursorHookInput(raw));
|
|
186
|
+
}
|
|
161
187
|
const testFiles = deps.testFiles ?? process.env.UPLOADS_HOOK_TEST_FILES;
|
|
162
188
|
const changed = testFiles ? testFiles.split("\n").filter(Boolean) : git.changedFiles();
|
|
163
189
|
if (!anyVisual(changed))
|
|
164
190
|
return null;
|
|
165
|
-
const staged = await (deps.countStaged ?? defaultCountStaged)(branch);
|
|
166
|
-
// null = error/unconfigured → fail open; >0 = already staged
|
|
167
|
-
if (staged === null || staged > 0)
|
|
168
|
-
return null;
|
|
169
191
|
const fork = (deps.isFork ?? (() => defaultIsFork(cwd)))();
|
|
170
192
|
const forkNote = fork === true
|
|
171
193
|
? " Note: this looks like a fork branch, so staged screenshots won't auto-promote into the PR comment yet (see issue #317) — attach them manually if you use uploads."
|
|
@@ -2,12 +2,13 @@ import { readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { basename } from "node:path";
|
|
3
3
|
import { extractDashValue, flagBool, flagInt, flagString, flagValues, parseCommandArgs, UsageError, } from "../cli-args.js";
|
|
4
4
|
import { writeCommandHelp } from "../cli-style.js";
|
|
5
|
-
import { branchFromFlags, derivedMetaEnabled, frameOptionsFromFlags, ghTargetFromFlags, optimizeOptionsFromFlags, stateAppMetaFromFlags, warnNearMissMeta, syncAttachmentsComment, commentViaSuffix, uploadPreparedImage, resolvePutStagingTarget, putStagingNoteText, resolveStageBindingWarning, mergeStagingMeta, writeReplacedNote, resolveGhPrefixSafe, } from "../commands.js";
|
|
5
|
+
import { branchFromFlags, derivedMetaEnabled, frameOptionsFromFlags, ghTargetFromFlags, optimizeOptionsFromFlags, stateAppMetaFromFlags, warnNearMissMeta, syncAttachmentsComment, commentViaSuffix, uploadPreparedImage, resolvePutStagingTarget, putStagingNoteText, resolveStageBindingWarning, mergeStagingMeta, writeReplacedNote, resolveGhPrefixSafe, resolveAutoPrTarget, resolvePutNudgeContext, putNudgeText, autoPrNoteText, } from "../commands.js";
|
|
6
6
|
import { resolvePutDefaults } from "../config.js";
|
|
7
7
|
import { loadDefaultsRaw, resolveScreenshotDefaults } from "../config-file.js";
|
|
8
8
|
import { resolvePutPrefix } from "../destinations.js";
|
|
9
9
|
import { execRunner, ghMetadataFromTargetWithTitle, resolveRepo, } from "../github-gh.js";
|
|
10
10
|
import { deriveRepoSlugFromGit } from "../keys.js";
|
|
11
|
+
import { noProjectContextNudge } from "../project-context-nudge.js";
|
|
11
12
|
import { safeCaptureFacts } from "../capture-facts.js";
|
|
12
13
|
import { parseMetaFlags, validateMetaMap } from "../metadata.js";
|
|
13
14
|
import { mergeDerivedMeta } from "../metadata-vocab.js";
|
|
@@ -43,9 +44,18 @@ sets the whole object key verbatim (no folding).
|
|
|
43
44
|
After capture, screenshots share the put upload pipeline: optional --frame,
|
|
44
45
|
optimize-by-default, --pr/--issue attachment + --comment, --gallery, --meta.
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
--prefix/--destination, a screenshot taken on a
|
|
48
|
-
|
|
47
|
+
Auto-PR context (issue #700): with no --pr/--issue/--branch/--key/--ref/
|
|
48
|
+
--prefix/--destination, a screenshot taken on a branch that maps to exactly
|
|
49
|
+
one open PR behaves as if --pr <n> had been passed — stable gh/ key, managed
|
|
50
|
+
comment sync (with --comment) — instead of branch staging below. A one-line
|
|
51
|
+
note announces this. Opt out with --no-pr, UPLOADS_NO_AUTO_PR=1, or config
|
|
52
|
+
UPLOADS_NO_AUTO_PR=1; never fires outside a git repo, on the default branch,
|
|
53
|
+
with --no-git, or when no single open PR can be resolved.
|
|
54
|
+
|
|
55
|
+
Branch staging by default (pre-PR): when auto-PR above doesn't apply and none
|
|
56
|
+
of --pr/--issue/--branch/--key/--ref/--prefix/--destination is given, a
|
|
57
|
+
screenshot taken on a non-default git branch stages under
|
|
58
|
+
gh/<owner>/<repo>/branch/<branch>/<name> instead of the dated
|
|
49
59
|
screenshots/<repo>/<date>/... layout — same key/metadata as an explicit
|
|
50
60
|
--branch, carrying every derived fact (path/url/env/viewport, --state) along.
|
|
51
61
|
Staged files auto-attach with full metadata the first time you attach to that
|
|
@@ -101,6 +111,7 @@ Options:
|
|
|
101
111
|
--optimize-max-edge <px> Max long edge when optimizing (default: 2400)
|
|
102
112
|
--optimize-quality <1-100> WebP quality (default: 85)
|
|
103
113
|
--keep-exif Keep EXIF/XMP/ICC when optimizing
|
|
114
|
+
--no-pr Skip auto-PR context (or UPLOADS_NO_AUTO_PR=1) — see above
|
|
104
115
|
--pr <num> Attach to a pull request (stable URL, no hash)
|
|
105
116
|
--issue <num> Attach to an issue
|
|
106
117
|
--branch [name] Stage against a branch, pre-PR (default: current git branch):
|
|
@@ -319,7 +330,32 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
319
330
|
}
|
|
320
331
|
const putDefaults = resolvePutDefaults({ envFile: ctx.envFile }, rawDefaults);
|
|
321
332
|
const noGit = flagBool(parsed.flags, "--no-git") || putDefaults.noGit === true;
|
|
333
|
+
if (parsed.flags.has("--no-pr") && typeof parsed.flags.get("--no-pr") === "string") {
|
|
334
|
+
throw new UsageError("--no-pr takes no value");
|
|
335
|
+
}
|
|
336
|
+
const noAutoPr = flagBool(parsed.flags, "--no-pr") || putDefaults.noAutoPr === true;
|
|
322
337
|
const branchRepo = branchArg !== undefined ? resolveRepo(flagString(parsed.flags, "--repo"), run) : undefined;
|
|
338
|
+
// Auto-PR context (issue #700): when no --branch/--pr/--issue/--key/--ref/
|
|
339
|
+
// --prefix/--destination is given, git use isn't disabled, and --no-pr/
|
|
340
|
+
// UPLOADS_NO_AUTO_PR hasn't opted out, a screenshot taken on a branch that
|
|
341
|
+
// maps to exactly one open PR behaves as if --pr <n> had been passed —
|
|
342
|
+
// stable key + managed comment sync — instead of the #469 auto-staging
|
|
343
|
+
// default below. Mirrors put's #700 handling exactly (resolveAutoPrTarget).
|
|
344
|
+
const autoPrTarget = ghTarget || branchArg !== undefined
|
|
345
|
+
? undefined
|
|
346
|
+
: resolveAutoPrTarget({
|
|
347
|
+
ghTarget,
|
|
348
|
+
keyHint,
|
|
349
|
+
refArg: flagString(parsed.flags, "--ref"),
|
|
350
|
+
prefixArg: prefixFlag,
|
|
351
|
+
destinationArg: destFlag,
|
|
352
|
+
branchArg,
|
|
353
|
+
noGit,
|
|
354
|
+
noAutoPr,
|
|
355
|
+
repoArg: flagString(parsed.flags, "--repo") ?? putDefaults.repo,
|
|
356
|
+
run,
|
|
357
|
+
});
|
|
358
|
+
const effectiveGhTarget = ghTarget ?? autoPrTarget;
|
|
323
359
|
// Auto branch staging (issue #469 lever 1): mirrors bare `put`'s auto-staging
|
|
324
360
|
// (issue #403). When no --branch/--pr/--issue/--key/--ref/--prefix/--destination
|
|
325
361
|
// is given and git use isn't disabled, a screenshot taken on a non-default
|
|
@@ -328,10 +364,11 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
328
364
|
// `screenshots/<repo>/<date>/...` layout. This is what lets derived
|
|
329
365
|
// metadata (path/url/env/viewport, --state) ride through to PR-open
|
|
330
366
|
// promotion when the capture happens before the PR exists. Skipped
|
|
331
|
-
// entirely when --branch was given explicitly (already handled above)
|
|
367
|
+
// entirely when --branch was given explicitly (already handled above), or
|
|
368
|
+
// when the #700 auto-PR match above already took over.
|
|
332
369
|
const autoStagingTarget = branchArg === undefined
|
|
333
370
|
? resolvePutStagingTarget({
|
|
334
|
-
ghTarget,
|
|
371
|
+
ghTarget: effectiveGhTarget,
|
|
335
372
|
keyHint,
|
|
336
373
|
refArg: flagString(parsed.flags, "--ref"),
|
|
337
374
|
prefixArg: prefixFlag,
|
|
@@ -348,7 +385,7 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
348
385
|
destination: destFlag,
|
|
349
386
|
prefix: prefixFlag,
|
|
350
387
|
key: keyHint,
|
|
351
|
-
ghAttachment: Boolean(
|
|
388
|
+
ghAttachment: Boolean(effectiveGhTarget) || stagingTarget !== undefined,
|
|
352
389
|
});
|
|
353
390
|
}
|
|
354
391
|
catch (err) {
|
|
@@ -378,8 +415,8 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
378
415
|
...(repoSlug ? { repo: repoSlug } : {}),
|
|
379
416
|
});
|
|
380
417
|
let metadata = withFacts;
|
|
381
|
-
if (
|
|
382
|
-
metadata = { ...withFacts, ...ghMetadataFromTargetWithTitle(
|
|
418
|
+
if (effectiveGhTarget) {
|
|
419
|
+
metadata = { ...withFacts, ...ghMetadataFromTargetWithTitle(effectiveGhTarget, run) };
|
|
383
420
|
validateMetaMap(metadata);
|
|
384
421
|
}
|
|
385
422
|
else if (stagingTarget !== undefined) {
|
|
@@ -388,6 +425,9 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
388
425
|
else if (Object.keys(withFacts).length > 0) {
|
|
389
426
|
validateMetaMap(withFacts);
|
|
390
427
|
}
|
|
428
|
+
// #692 follow-up: same advisory as put — a capture with no repo/app context
|
|
429
|
+
// and only a local origin lands in the screenshots page's fallback buckets.
|
|
430
|
+
const contextNudge = !ctx.quiet && !putDefaults.noNudge && !noGit ? noProjectContextNudge(metadata) : undefined;
|
|
391
431
|
const logHuman = !ctx.quiet && format === "human";
|
|
392
432
|
if (logHuman)
|
|
393
433
|
process.stderr.write(`>> capturing ${target}\n`);
|
|
@@ -471,10 +511,10 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
471
511
|
// Resolved once (issue #631), only when it's actually needed for the
|
|
472
512
|
// upload about to happen (never for the noUpload/no-target bailouts
|
|
473
513
|
// above) — never per file (screenshot only ever uploads one).
|
|
474
|
-
const ghPrefix =
|
|
514
|
+
const ghPrefix = effectiveGhTarget
|
|
475
515
|
? await resolveGhPrefixSafe(ctx.client, {
|
|
476
|
-
repo:
|
|
477
|
-
target: { kind:
|
|
516
|
+
repo: effectiveGhTarget.repo,
|
|
517
|
+
target: { kind: effectiveGhTarget.kind, num: effectiveGhTarget.num },
|
|
478
518
|
})
|
|
479
519
|
: stagingTarget !== undefined
|
|
480
520
|
? await resolveGhPrefixSafe(ctx.client, {
|
|
@@ -482,11 +522,29 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
482
522
|
branch: stagingTarget.branch,
|
|
483
523
|
})
|
|
484
524
|
: undefined;
|
|
525
|
+
// Bare-screenshot nudge context (issue #393/#700): only relevant when
|
|
526
|
+
// neither auto-PR nor staging took over — mirrors put's handling exactly.
|
|
527
|
+
// Resolved before upload; finished into text below once the key is known.
|
|
528
|
+
const nudgeContext = effectiveGhTarget || stagingTarget
|
|
529
|
+
? undefined
|
|
530
|
+
: resolvePutNudgeContext({
|
|
531
|
+
quiet: ctx.quiet,
|
|
532
|
+
noNudge: putDefaults.noNudge === true,
|
|
533
|
+
ghTarget,
|
|
534
|
+
keyHint,
|
|
535
|
+
hasBranchFlag: branchArg !== undefined,
|
|
536
|
+
noGit,
|
|
537
|
+
repoArg: flagString(parsed.flags, "--repo") ?? putDefaults.repo,
|
|
538
|
+
run,
|
|
539
|
+
});
|
|
540
|
+
const autoPrNote = autoPrTarget && !ctx.quiet && !putDefaults.noNudge
|
|
541
|
+
? autoPrNoteText(autoPrTarget.num)
|
|
542
|
+
: undefined;
|
|
485
543
|
const alt = altFlag ?? basename(captured.filename);
|
|
486
544
|
const { result, prepared, markdown } = await uploadPreparedImage(ctx.client, finalPng, captured.filename, {
|
|
487
545
|
frame: frameOpts,
|
|
488
546
|
optimize: optimizeOpts,
|
|
489
|
-
ghTarget,
|
|
547
|
+
ghTarget: effectiveGhTarget,
|
|
490
548
|
ghBranchTarget: stagingTarget,
|
|
491
549
|
ghPrefix,
|
|
492
550
|
key: keyHint,
|
|
@@ -528,11 +586,16 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
528
586
|
gallery = { id: galleryId, error: err instanceof Error ? err.message : String(err) };
|
|
529
587
|
}
|
|
530
588
|
}
|
|
589
|
+
// Concrete bare-screenshot nudge text (issue #700): built once the upload
|
|
590
|
+
// key exists, so the ready-made follow-up names it verbatim.
|
|
591
|
+
const nudge = nudgeContext
|
|
592
|
+
? putNudgeText(nudgeContext.branch, nudgeContext.pr, [result.key])
|
|
593
|
+
: undefined;
|
|
531
594
|
let comment;
|
|
532
595
|
let commentError;
|
|
533
|
-
if (wantComment &&
|
|
596
|
+
if (wantComment && effectiveGhTarget) {
|
|
534
597
|
try {
|
|
535
|
-
comment = await syncAttachmentsComment(ctx.client,
|
|
598
|
+
comment = await syncAttachmentsComment(ctx.client, effectiveGhTarget, run, ctx.config.workspace);
|
|
536
599
|
if (logHuman)
|
|
537
600
|
process.stderr.write(`>> attachments comment ${comment.action}${commentViaSuffix(comment.via)}\n`);
|
|
538
601
|
}
|
|
@@ -560,23 +623,36 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
560
623
|
`(or run \`uploads attach --promote\` after opening)\n`);
|
|
561
624
|
}
|
|
562
625
|
}
|
|
626
|
+
if (autoPrNote)
|
|
627
|
+
process.stderr.write(`${autoPrNote}\n`);
|
|
628
|
+
if (nudge)
|
|
629
|
+
process.stderr.write(`${nudge}\n`);
|
|
563
630
|
if (bindingWarning)
|
|
564
631
|
process.stderr.write(`${bindingWarning}\n`);
|
|
632
|
+
if (contextNudge)
|
|
633
|
+
process.stderr.write(`${contextNudge}\n`);
|
|
565
634
|
process.stderr.write("\n");
|
|
566
635
|
}
|
|
567
636
|
// One JSON `hint` slot (mirrors bare put): the clip note (issue #652) wins
|
|
568
637
|
// first — it's about the just-captured image itself, more immediately
|
|
569
638
|
// actionable than the other three, which are about upload/staging
|
|
570
|
-
// mechanics. Then the
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
//
|
|
639
|
+
// mechanics. Then the auto-PR note and the #393/#700 nudge (issue #700),
|
|
640
|
+
// then the binding warning, more actionable than the generic staging note;
|
|
641
|
+
// a replaced-object note (issue #618) is lowest priority — it only
|
|
642
|
+
// surfaces when nothing else already claimed the slot. Since state folds
|
|
643
|
+
// into the derived key, replaced + state means a same-side re-capture,
|
|
574
644
|
// which is the intended replace-in-place flow — word it as informational,
|
|
575
645
|
// not as a problem.
|
|
576
646
|
const replacedHint = result.replaced && explicitMeta.state
|
|
577
647
|
? `re-capture replaced the previous state=${explicitMeta.state} object at ${result.key} — expected for repeat captures of the same URL + state`
|
|
578
648
|
: undefined;
|
|
579
|
-
const jsonHint = clipHint ??
|
|
649
|
+
const jsonHint = clipHint ??
|
|
650
|
+
autoPrNote ??
|
|
651
|
+
nudge ??
|
|
652
|
+
bindingWarning ??
|
|
653
|
+
stagingNote ??
|
|
654
|
+
replacedHint ??
|
|
655
|
+
contextNudge;
|
|
580
656
|
switch (format) {
|
|
581
657
|
case "json":
|
|
582
658
|
await writeJson({
|
package/dist/commands.d.ts
CHANGED
|
@@ -411,6 +411,82 @@ export declare function resolveStageBindingWarning(opts: {
|
|
|
411
411
|
defaults: PutDefaults;
|
|
412
412
|
repo: string;
|
|
413
413
|
}): Promise<string | undefined>;
|
|
414
|
+
/**
|
|
415
|
+
* The bare-put nudge's wording (issue #393, made concrete by issue #700):
|
|
416
|
+
* teaches `--pr`/`attach --branch` as an upgrade from a targetless `put`.
|
|
417
|
+
* `pr` present → names the PR and, once upload `keys` are known, appends a
|
|
418
|
+
* ready-made follow-up command naming them verbatim (e.g. `uploads attach
|
|
419
|
+
* --pr 1250 f/abc123.webp`); otherwise a generic variant that still points
|
|
420
|
+
* at `--pr <num>`. Used verbatim for both the human-mode stderr line and the
|
|
421
|
+
* JSON `hint` field.
|
|
422
|
+
*/
|
|
423
|
+
export declare function putNudgeText(branch: string, pr: number | undefined, keys?: string[]): string;
|
|
424
|
+
/**
|
|
425
|
+
* Auto-PR note (issue #700): announces at the moment it fires that a bare
|
|
426
|
+
* put/screenshot on this branch was auto-attached to `pr` — the default
|
|
427
|
+
* behavior change this issue introduces — and how to opt out.
|
|
428
|
+
*/
|
|
429
|
+
export declare function autoPrNoteText(pr: number): string;
|
|
430
|
+
/**
|
|
431
|
+
* Best-effort bare-put/screenshot nudge context (issue #393): resolves the
|
|
432
|
+
* branch and, when detectable, the open PR for it — fires only when there is
|
|
433
|
+
* no targeting flag at all (`--pr`/`--issue`/`--key`; `--branch` too, though
|
|
434
|
+
* `put` doesn't currently accept it — defensive parity with `attach`), is
|
|
435
|
+
* inside a git repo (reusing `deriveRepoFromGit`, the same detection the
|
|
436
|
+
* default screenshot key's repo segment uses), and the current branch isn't
|
|
437
|
+
* the default one. Never throws — any failure (not a repo, detached HEAD,
|
|
438
|
+
* `gh` missing/unauthenticated/timed out) degrades to "no nudge" or, once a
|
|
439
|
+
* branch is already known, to a context with `pr: undefined` (the generic
|
|
440
|
+
* no-PR wording). Must never affect put's exit code, stdout, or upload
|
|
441
|
+
* behavior. Callers turn the result into text via `putNudgeText`, once any
|
|
442
|
+
* upload keys are known.
|
|
443
|
+
*/
|
|
444
|
+
export declare function resolvePutNudgeContext(opts: {
|
|
445
|
+
quiet: boolean;
|
|
446
|
+
noNudge: boolean;
|
|
447
|
+
ghTarget: GhTarget | undefined;
|
|
448
|
+
keyHint: string | undefined;
|
|
449
|
+
/** True when an explicit `--branch`-style flag was given (CLI `attach`
|
|
450
|
+
* parity; `put`/MCP `put` don't accept one — pass false there). */
|
|
451
|
+
hasBranchFlag?: boolean;
|
|
452
|
+
noGit: boolean;
|
|
453
|
+
repoArg: string | undefined;
|
|
454
|
+
run: CommandRunner;
|
|
455
|
+
}): {
|
|
456
|
+
branch: string;
|
|
457
|
+
pr: number | undefined;
|
|
458
|
+
} | undefined;
|
|
459
|
+
/**
|
|
460
|
+
* Auto-PR context (issue #700): when a bare put/screenshot has no explicit
|
|
461
|
+
* destination flag at all (`--pr`/`--issue`/`--key`/`--ref`/`--prefix`/
|
|
462
|
+
* `--destination`, and for `screenshot` no explicit `--branch`) and runs on a
|
|
463
|
+
* branch that maps to exactly one open PR, this resolves that PR so the
|
|
464
|
+
* caller can behave as if `--pr <n>` had been passed — stable key + managed
|
|
465
|
+
* comment sync — instead of the #403/#469 staging default or the plain dated
|
|
466
|
+
* layout. `resolveCurrentPullRequest`'s `gh pr view <branch>` lookup is
|
|
467
|
+
* already the unambiguous case: it names the single open PR whose head is
|
|
468
|
+
* that branch, or fails (no open PR, or `gh` unavailable/unauthenticated) —
|
|
469
|
+
* there is no "ambiguous, more than one" state to further disambiguate.
|
|
470
|
+
* Opt-out: `noAutoPr` (the caller folds in `--no-pr` and
|
|
471
|
+
* `UPLOADS_NO_AUTO_PR=1`/config). Never fires outside a git checkout, on the
|
|
472
|
+
* default branch, or with `--no-git`; any failure (not a repo, detached
|
|
473
|
+
* HEAD, gh missing/unauthenticated/timed out, no open PR) degrades to
|
|
474
|
+
* undefined so the caller falls back to its normal staging/dated behavior.
|
|
475
|
+
*/
|
|
476
|
+
export declare function resolveAutoPrTarget(opts: {
|
|
477
|
+
ghTarget: GhTarget | undefined;
|
|
478
|
+
keyHint: string | undefined;
|
|
479
|
+
refArg: string | undefined;
|
|
480
|
+
prefixArg: string | undefined;
|
|
481
|
+
destinationArg: string | undefined;
|
|
482
|
+
/** Explicit `--branch` (screenshot only) also opts out — put has no
|
|
483
|
+
* `--branch` flag today, so callers pass undefined there. */
|
|
484
|
+
branchArg?: string | undefined;
|
|
485
|
+
noGit: boolean;
|
|
486
|
+
noAutoPr: boolean;
|
|
487
|
+
repoArg: string | undefined;
|
|
488
|
+
run: CommandRunner;
|
|
489
|
+
}): GhTarget | undefined;
|
|
414
490
|
/**
|
|
415
491
|
* Bare-put branch-staging trigger (issue #403): put on a non-default git
|
|
416
492
|
* branch stages to the branch prefix by default — the branch becomes the
|