jj-octo 0.9.0 → 0.9.1

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 CHANGED
@@ -42,10 +42,10 @@ With `jj-octo.github-auth` set to `git-proxy`, it sends a placeholder token inst
42
42
 
43
43
  A Claude Code [cloud session](https://code.claude.com/docs/en/claude-code-on-the-web) runs in an Anthropic-hosted environment, or in your organization's self-hosted environment. Each needs its own setup.
44
44
 
45
- The session's GitHub proxy can append a "Generated by Claude Code" footer to each pull request body that it creates or edits. `jj gh submit` would read the footer as an edit made on GitHub, and write it into the change. To have `submit` leave the footer out when it compares a pull request's body with its change's description, set:
45
+ The session's GitHub proxy can append a "Generated by Claude Code" footer to each pull request body that it creates or edits. `jj gh submit` would read the footer as an edit made on GitHub, and write it into the change. So where `CLAUDE_CODE_REMOTE` is `true`, as in a cloud session, `submit` leaves the footer out when it compares a pull request's body with its change's description. `jj-octo.ignore-claude-code-footer` overrides that: set it to `true` to leave the footer out elsewhere, or to `false` to keep it in a cloud session:
46
46
 
47
47
  ```
48
- jj config set --repo jj-octo.ignore-claude-code-footer true
48
+ jj config set --repo jj-octo.ignore-claude-code-footer false
49
49
  ```
50
50
 
51
51
  ### In an Anthropic-hosted environment
package/dist/jj-octo.js CHANGED
@@ -1670,6 +1670,27 @@ var match2 = /* @__PURE__ */ dual(2, (self, {
1670
1670
  onFailure,
1671
1671
  onSuccess
1672
1672
  }) => isFailure2(self) ? onFailure(self.failure) : onSuccess(self.success));
1673
+ var all = (input) => {
1674
+ if (Symbol.iterator in input) {
1675
+ const out = [];
1676
+ for (const e of input) {
1677
+ if (isFailure2(e)) {
1678
+ return e;
1679
+ }
1680
+ out.push(e.success);
1681
+ }
1682
+ return succeed2(out);
1683
+ }
1684
+ const out = {};
1685
+ for (const key of Object.keys(input)) {
1686
+ const e = input[key];
1687
+ if (isFailure2(e)) {
1688
+ return e;
1689
+ }
1690
+ assignProperty(out, key, e.success);
1691
+ }
1692
+ return succeed2(out);
1693
+ };
1673
1694
 
1674
1695
  // node_modules/effect/dist/Tuple.js
1675
1696
  var makeEquivalence = Tuple;
@@ -3812,7 +3833,7 @@ var uninterruptibleMask = (f) => withFiber((fiber) => {
3812
3833
  fiber._stack.push(setInterruptibleTrue);
3813
3834
  return f(interruptible);
3814
3835
  });
3815
- var all = (arg, options) => {
3836
+ var all2 = (arg, options) => {
3816
3837
  if (isIterable(arg)) {
3817
3838
  return options?.mode === "result" ? forEach(arg, result, options) : forEach(arg, identity, options);
3818
3839
  } else if (options?.discard) {
@@ -4985,7 +5006,7 @@ var buildFromOptions = (options) => {
4985
5006
 
4986
5007
  // node_modules/effect/dist/Effect.js
4987
5008
  var isEffect2 = isEffect;
4988
- var all2 = all;
5009
+ var all3 = all2;
4989
5010
  var forEach2 = forEach;
4990
5011
  var whileLoop2 = whileLoop;
4991
5012
  var promise2 = promise;
@@ -10000,7 +10021,7 @@ var watchNode = (path, info, options) => callback3((queue) => acquireRelease2(sy
10000
10021
  });
10001
10022
  return watcher;
10002
10023
  }), (watcher) => sync2(() => watcher.close())).pipe(tap2(() => sync2(() => options?.onReady?.()))));
10003
- var watch2 = (backend, path, options) => all2([stat2(path), serviceOption2(WatchBackend)]).pipe(map7(([stat, callerBackend]) => orElse(callerBackend, () => backend).pipe(flatMap((_) => _.register(path, stat, options)), getOrElse(() => watchNode(path, stat, options)))), unwrap4);
10024
+ var watch2 = (backend, path, options) => all3([stat2(path), serviceOption2(WatchBackend)]).pipe(map7(([stat, callerBackend]) => orElse(callerBackend, () => backend).pipe(flatMap((_) => _.register(path, stat, options)), getOrElse(() => watchNode(path, stat, options)))), unwrap4);
10004
10025
  var writeFile2 = (path, data, options) => callback2((resume, signal) => {
10005
10026
  try {
10006
10027
  NFS.writeFile(path, data, {
@@ -12232,6 +12253,10 @@ var orElse2 = /* @__PURE__ */ dual(2, (self, that) => {
12232
12253
  onSuccess: (resolution) => isFailure2(resolution) ? evaluateAt(that(resolution.failure), provider, pathPrefix) : succeed6(resolution)
12233
12254
  }));
12234
12255
  });
12256
+ function all4(arg) {
12257
+ const configs = globalThis.Array.isArray(arg) ? arg : (Symbol.iterator in arg) ? [...arg] : arg;
12258
+ return make30((provider, pathPrefix) => globalThis.Array.isArray(configs) ? mapEager2(all3(configs.map((config) => evaluateAt(config, provider, pathPrefix))), all) : mapEager2(all3(map3(configs, (config) => evaluateAt(config, provider, pathPrefix))), all));
12259
+ }
12235
12260
  var withDefault3 = /* @__PURE__ */ dual(2, (self, defaultValue) => {
12236
12261
  return make30((provider, pathPrefix) => mapEager2(evaluateAt(self, provider, pathPrefix), (resolution) => isFailure2(resolution) ? succeed2(defaultValue) : resolution));
12237
12262
  });
@@ -12355,6 +12380,9 @@ function schema(codec, path) {
12355
12380
  }))), mapReasons((reason) => isDieReason2(reason) && isSourceError(reason.defect) ? makeFailReason(new ConfigError(reason.defect)).annotate(reasonAnnotations2(reason)) : reason));
12356
12381
  });
12357
12382
  }
12383
+ function String5(name) {
12384
+ return schema(String4, name);
12385
+ }
12358
12386
  function Redacted2(name) {
12359
12387
  return schema(Redacted(String4), name);
12360
12388
  }
@@ -12632,7 +12660,7 @@ var makeSchemaPrimitive = (tag, schema) => {
12632
12660
  return makePrimitive2(tag, (value) => mapError3(decode(value), (error) => error.message));
12633
12661
  };
12634
12662
  var Boolean4 = /* @__PURE__ */ makeSchemaPrimitive("Boolean", BooleanLiterals);
12635
- var String5 = /* @__PURE__ */ makePrimitive2("String", (value) => succeed6(value));
12663
+ var String6 = /* @__PURE__ */ makePrimitive2("String", (value) => succeed6(value));
12636
12664
  var Choice = (choices) => {
12637
12665
  const choiceMap = new Map(choices);
12638
12666
  const validChoices = choices.map(([key]) => format(key)).join(" | ");
@@ -12975,7 +13003,7 @@ var succeed9 = (value) => {
12975
13003
  op.value = value;
12976
13004
  return op;
12977
13005
  };
12978
- var String6 = (options) => basePrompt(options, "text");
13006
+ var String7 = (options) => basePrompt(options, "text");
12979
13007
  var Toggle = (options) => {
12980
13008
  const opts = {
12981
13009
  initial: false,
@@ -14431,9 +14459,9 @@ var makeSingle2 = (params) => {
14431
14459
  parse
14432
14460
  }, Proto10);
14433
14461
  };
14434
- var String7 = (kind, name) => makeSingle2({
14462
+ var String8 = (kind, name) => makeSingle2({
14435
14463
  name,
14436
- primitiveType: String5,
14464
+ primitiveType: String6,
14437
14465
  kind
14438
14466
  });
14439
14467
  var Boolean5 = (kind, name) => makeSingle2({
@@ -16234,7 +16262,7 @@ var generate5 = (executableName, shell, descriptor) => {
16234
16262
  };
16235
16263
 
16236
16264
  // node_modules/effect/dist/cli/Flag.js
16237
- var String8 = (name) => String7(flagKind, name);
16265
+ var String9 = (name) => String8(flagKind, name);
16238
16266
  var Boolean6 = (name) => Boolean5(flagKind, name);
16239
16267
  var ChoiceWithValue2 = (name, choices) => ChoiceWithValue(flagKind, name, choices);
16240
16268
  var withAlias2 = /* @__PURE__ */ dual(2, (self, alias) => withAlias(self, alias));
@@ -17190,7 +17218,7 @@ var promptSingle = (single) => {
17190
17218
  message
17191
17219
  })), (value) => commandLineArg(value2(value), "<redacted>"));
17192
17220
  default:
17193
- return map7(run3(String6({
17221
+ return map7(run3(String7({
17194
17222
  message
17195
17223
  })), commandLineArg);
17196
17224
  }
@@ -18292,7 +18320,7 @@ var layer14 = /* @__PURE__ */ layerMergedContext(/* @__PURE__ */ succeed6(fetch)
18292
18320
  // package.json
18293
18321
  var package_default = {
18294
18322
  name: "jj-octo",
18295
- version: "0.9.0",
18323
+ version: "0.9.1",
18296
18324
  description: "A jj subcommand for GitHub that supports stacked pull requests and works in Claude Code cloud sessions",
18297
18325
  license: "MIT",
18298
18326
  repository: {
@@ -18346,7 +18374,7 @@ var asUserError = (program) => program.pipe(mapError3(({ message }) => userError
18346
18374
  var withHelp = (summary, ...paragraphs) => (command) => command.pipe(withShortDescription(summary), withDescription3([summary, ...paragraphs].join(`
18347
18375
 
18348
18376
  `)));
18349
- var revisionFlag = (description) => String8("revision").pipe(withAlias2("r"), withDefault5("@"), withDescription2(`${description}, the working copy by default`));
18377
+ var revisionFlag = (description) => String9("revision").pipe(withAlias2("r"), withDefault5("@"), withDescription2(`${description}, the working copy by default`));
18350
18378
  // src/clients/git.ts
18351
18379
  class GitFailed extends TaggedError2("GitFailed") {
18352
18380
  }
@@ -18364,7 +18392,7 @@ var gitLayer = ({ cwd, env } = {}) => effect(Git, gen2(function* () {
18364
18392
  `, github: `${remote}
18365
18393
  `, local: `${local}
18366
18394
  ` };
18367
- yield* all2(Object.entries(written).map(([name, text]) => fs.writeFileString(path.join(directory, name), text)));
18395
+ yield* all3(Object.entries(written).map(([name, text]) => fs.writeFileString(path.join(directory, name), text)));
18368
18396
  const handle = yield* spawner.spawn(make16("git", [
18369
18397
  "merge-file",
18370
18398
  "-p",
@@ -18378,7 +18406,7 @@ var gitLayer = ({ cwd, env } = {}) => effect(Git, gen2(function* () {
18378
18406
  "base",
18379
18407
  "github"
18380
18408
  ], { cwd: directory }));
18381
- const [text, stderr, code] = yield* all2([
18409
+ const [text, stderr, code] = yield* all3([
18382
18410
  mkString(decodeText(handle.stdout)),
18383
18411
  mkString(decodeText(handle.stderr)),
18384
18412
  handle.exitCode
@@ -18390,7 +18418,7 @@ var gitLayer = ({ cwd, env } = {}) => effect(Git, gen2(function* () {
18390
18418
  })).pipe(catchTag2("PlatformError", (error) => fail6(new GitFailed({ message: `git merge-file failed: ${error.message}` })))),
18391
18419
  read: (args) => scoped2(gen2(function* () {
18392
18420
  const handle = yield* spawner.spawn(make16("git", args, { cwd, env, extendEnv: true }));
18393
- const [stdout, stderr, code] = yield* all2([
18421
+ const [stdout, stderr, code] = yield* all3([
18394
18422
  mkString(decodeText(handle.stdout)),
18395
18423
  mkString(decodeText(handle.stderr)),
18396
18424
  handle.exitCode
@@ -18435,7 +18463,7 @@ var jjLayer = ({ cwd, env, printed } = {}) => effect(Jj, gen2(function* () {
18435
18463
  const location2 = { cwd, env, extendEnv: true };
18436
18464
  const captured = (args) => scoped2(gen2(function* () {
18437
18465
  const handle = yield* spawner.spawn(make16("jj", args, location2));
18438
- return yield* all2([
18466
+ return yield* all3([
18439
18467
  mkString(decodeText(handle.stdout)),
18440
18468
  mkString(decodeText(handle.stderr)),
18441
18469
  handle.exitCode
@@ -18476,10 +18504,13 @@ var jjConfig = (key) => gen2(function* () {
18476
18504
  message: `${key} is ${JSON.stringify(value)}; set it to a string`
18477
18505
  });
18478
18506
  });
18479
- var jjFlag = (key) => gen2(function* () {
18507
+ var jjFlag = (key, fallback = false) => gen2(function* () {
18480
18508
  const value = yield* readJjConfig(key);
18481
- if (value === undefined || typeof value === "boolean") {
18482
- return value === true;
18509
+ if (value === undefined) {
18510
+ return fallback;
18511
+ }
18512
+ if (typeof value === "boolean") {
18513
+ return value;
18483
18514
  }
18484
18515
  return yield* new JjFailed({
18485
18516
  message: `${key} is ${JSON.stringify(value)}; set it to true or false`
@@ -18891,6 +18922,9 @@ var init = gen2(function* () {
18891
18922
  });
18892
18923
  var initCommand = make32("init", {}, () => asUserError(init)).pipe(withHelp("Set up a git checkout for the other commands", "Makes it a jj repository colocated with git, unless it already is one, and copies git's name and email into the repository's jj config where jj has none. Where neither has them, set them with `jj config set --user`.", "Reads the default branch from `origin`, fetches and tracks it, and sets `trunk()` to it in the repository's config. jj's own `trunk()` guesses from branch names, so without this a default branch it doesn't guess would be the base of pull requests but not of local rebases. `submit`, `merge` and `sync` check that the two agree.", "Sets the `jj gh` alias to run `jj-octo` from `PATH`, in the repository's config, unless the repository has one, such as one that runs another build.", "Needs no GitHub token. Running it again changes nothing that is already set up. Run it again after the default branch changes on GitHub. Fails with what it couldn't set up, after setting up the rest. Where it can't make a colocated jj repository, such as in a git worktree, it sets up nothing else."));
18893
18924
 
18925
+ // src/clients/claude-code.ts
18926
+ var claudeCodeCloudSession = orDie2(String5("CLAUDE_CODE_REMOTE").pipe(withDefault3(""), map10((value) => value === "true")));
18927
+
18894
18928
  // src/clients/clipboard.ts
18895
18929
  class ClipboardFailed extends TaggedError2("ClipboardFailed") {
18896
18930
  }
@@ -18909,50 +18943,62 @@ function run([html, text]) {
18909
18943
  throw new Error("the pasteboard didn't take the links");
18910
18944
  }
18911
18945
  }`;
18912
- var commandsFor = ({ html, text }, platform, env) => {
18913
- const make = (name, args, input) => ({
18914
- command: make16(name, args, {
18915
- detached: false,
18916
- env,
18917
- extendEnv: true,
18918
- stdout: "ignore",
18919
- ...input === "stdin" ? { stderr: "ignore", stdin: make12(html).pipe(encodeText) } : { stderr: "inherit", stdin: "ignore" }
18920
- }),
18921
- name
18922
- });
18946
+ var environment = all4({
18947
+ path: String5("PATH").pipe(withDefault3("")),
18948
+ wayland: String5("WAYLAND_DISPLAY").pipe(withDefault3(""))
18949
+ });
18950
+ var commandsFor = ({ html, text }, platform, wayland) => {
18923
18951
  if (platform === "darwin") {
18924
- return [make("osascript", ["-l", "JavaScript", "-e", MACOS_SCRIPT, html, text], "arguments")];
18952
+ return [
18953
+ {
18954
+ args: ["-l", "JavaScript", "-e", MACOS_SCRIPT, html, text],
18955
+ input: "arguments",
18956
+ name: "osascript"
18957
+ }
18958
+ ];
18925
18959
  }
18926
18960
  if (platform !== "linux") {
18927
18961
  return [];
18928
18962
  }
18929
- const xclip = make("xclip", ["-selection", "clipboard", "-t", "text/html"], "stdin");
18930
- const wayland = env["WAYLAND_DISPLAY"] ?? process.env["WAYLAND_DISPLAY"] ?? "";
18931
- return wayland === "" ? [xclip] : [make("wl-copy", ["--type", "text/html"], "stdin"), xclip];
18963
+ const xclip = {
18964
+ args: ["-selection", "clipboard", "-t", "text/html"],
18965
+ input: "stdin",
18966
+ name: "xclip"
18967
+ };
18968
+ return wayland === "" ? [xclip] : [{ args: ["--type", "text/html"], input: "stdin", name: "wl-copy" }, xclip];
18932
18969
  };
18933
- var clipboardLayer = ({
18934
- env = {},
18935
- platform = process.platform
18936
- } = {}) => effect(Clipboard, gen2(function* () {
18970
+ var childProcess = ({ args, input }, executable, html) => make16(executable, args, {
18971
+ detached: false,
18972
+ stdout: "ignore",
18973
+ ...input === "stdin" ? { stderr: "ignore", stdin: make12(html).pipe(encodeText) } : { stderr: "inherit", stdin: "ignore" }
18974
+ });
18975
+ var clipboardLayer = ({ platform = process.platform } = {}) => effect(Clipboard, gen2(function* () {
18976
+ const { path, wayland } = yield* orDie2(environment);
18977
+ if (yield* claudeCodeCloudSession) {
18978
+ return Clipboard.of({ write: () => succeed6("unreachable") });
18979
+ }
18937
18980
  const spawner = yield* ChildProcessSpawner;
18938
- return {
18981
+ return Clipboard.of({
18939
18982
  write: (content) => {
18940
- const commands = commandsFor(content, platform, env);
18983
+ const commands = commandsFor(content, platform, wayland);
18941
18984
  if (commands.length === 0) {
18942
18985
  return fail6(new ClipboardFailed({
18943
18986
  message: `copying to the clipboard needs macOS, or Linux with wl-copy or xclip, not ${platform}`
18944
18987
  }));
18945
18988
  }
18946
- const PATH = env["PATH"] ?? process.env["PATH"] ?? "";
18947
- const installed = commands.find(({ name }) => Bun.which(name, { PATH }) !== null);
18989
+ const [installed] = commands.flatMap((command) => {
18990
+ const executable = Bun.which(command.name, { PATH: path });
18991
+ return executable === null ? [] : [{ command, executable }];
18992
+ });
18948
18993
  if (installed === undefined) {
18949
18994
  const names = commands.map(({ name }) => name).join(" or ");
18950
18995
  return fail6(new ClipboardFailed({ message: `couldn't find ${names}` }));
18951
18996
  }
18952
- const { command, name } = installed;
18953
- return spawner.exitCode(command).pipe(catchTag2("PlatformError", (error) => fail6(new ClipboardFailed({ message: `${name} failed: ${error.message}` }))), flatMap4((code) => code === 0 ? void_3 : fail6(new ClipboardFailed({ message: `${name} exited with ${code}` }))));
18997
+ const { command, executable } = installed;
18998
+ const { name } = command;
18999
+ return spawner.exitCode(childProcess(command, executable, content.html)).pipe(catchTag2("PlatformError", (error) => fail6(new ClipboardFailed({ message: `${name} failed: ${error.message}` }))), flatMap4((code) => code === 0 ? succeed6("copied") : fail6(new ClipboardFailed({ message: `${name} exited with ${code}` }))));
18954
19000
  }
18955
- };
19001
+ });
18956
19002
  }));
18957
19003
  var ClipboardLive = clipboardLayer();
18958
19004
 
@@ -19036,7 +19082,7 @@ class NoPullRequests extends TaggedError2("NoPullRequests") {
19036
19082
  var EMOJI = {
19037
19083
  closed: "\uD83D\uDEAB",
19038
19084
  draft: "\uD83D\uDCDD",
19039
- merged: "\u2705",
19085
+ merged: "\uD83C\uDFC1",
19040
19086
  ready: "\uD83D\uDC40"
19041
19087
  };
19042
19088
  var OPEN = {
@@ -19045,6 +19091,14 @@ var OPEN = {
19045
19091
  merged: false,
19046
19092
  ready: true
19047
19093
  };
19094
+ var COPY_REPORT = {
19095
+ copied: "copied the links to the clipboard",
19096
+ unreachable: [
19097
+ "This Claude Code cloud session can't reach the user's clipboard.",
19098
+ "To give the user the links, put the Markdown links this command printed in your reply, outside a code block.",
19099
+ "The Claude app shows them as links, which paste into Slack as links."
19100
+ ].join(" ")
19101
+ };
19048
19102
  var markdownLinks = (pullRequests) => pullRequests.map(({ number, state, title, url }) => `${EMOJI[state]} [#${number} ${title}](${url})`).join(`
19049
19103
  `);
19050
19104
  var htmlLinks = (pullRequests) => `<meta charset="utf-8">${pullRequests.map(({ number, state, title, url }) => `${EMOJI[state]} <a href="${Bun.escapeHTML(url)}">#${number} ${Bun.escapeHTML(title)}</a>`).join("<br>")}`;
@@ -19052,7 +19106,7 @@ var pullRequestOf = (pullRequests, commitId, pushed) => fromNullishOr(pullReques
19052
19106
  var stackPullRequests = (revision) => gen2(function* () {
19053
19107
  const github = yield* GitHub;
19054
19108
  const stack = yield* stackChanges(revision);
19055
- const [targets, found] = yield* all2([
19109
+ const [targets, found] = yield* all3([
19056
19110
  bookmarkTargets,
19057
19111
  forEach2(stack, ({ bookmark }) => github.pullRequestsFrom(bookmark), {
19058
19112
  concurrency: "unbounded"
@@ -19069,14 +19123,14 @@ var links = ({
19069
19123
  yield* log(text);
19070
19124
  if (copy) {
19071
19125
  const clipboard = yield* Clipboard;
19072
- yield* clipboard.write({ html: htmlLinks(pullRequests), text });
19073
- yield* error("copied the links to the clipboard");
19126
+ const written = yield* clipboard.write({ html: htmlLinks(pullRequests), text });
19127
+ yield* error(COPY_REPORT[written]);
19074
19128
  }
19075
19129
  });
19076
19130
  var linksCommand = make32("links", {
19077
- copy: Boolean6("copy").pipe(withDefault5(false), withDescription2("Also copy the links to the clipboard, as links that Slack pastes")),
19131
+ copy: Boolean6("copy").pipe(withDefault5(false), withDescription2("Also copy the links to the clipboard, as links that Slack pastes, or in a Claude Code cloud session say how to give them to the user")),
19078
19132
  revision: revisionFlag("A change of the stack to list")
19079
- }, (options) => asUserError(links(options).pipe(provide3([GitHubFromOrigin, ClipboardLive])))).pipe(withHelp("Print the stack's pull requests as Markdown links", "Prints one line per pull request, top first: \uD83D\uDCDD for a draft, \uD83D\uDC40 for one ready for review, \u2705 for a merged one and \uD83D\uDEAB for one closed unmerged, then a Markdown link showing its number and title.", "A change's pull request is the open one from its branch, or else the newest closed one whose head is the change or the commit pushed for it. A change with no pull request is left out, as is one that `sync` abandoned because its pull request merged. Fails where no change has one.", "With `--copy`, also puts the links on the clipboard as HTML, which Slack and other apps paste as links. That runs `osascript` on macOS, which also copies the Markdown as plain text, and `wl-copy` or `xclip` on Linux, which copy the HTML alone.", "Fails if the changes aren't a chain, if one has conflicts, or if one doesn't have exactly one bookmark, as `log` does. Changes nothing, locally or on GitHub."));
19133
+ }, (options) => asUserError(links(options).pipe(provide3([GitHubFromOrigin, ClipboardLive])))).pipe(withHelp("Print the stack's pull requests as Markdown links", "Prints one line per pull request, top first: \uD83D\uDCDD for a draft, \uD83D\uDC40 for one ready for review, \uD83C\uDFC1 for a merged one and \uD83D\uDEAB for one closed unmerged, then a Markdown link showing its number and title.", "A change's pull request is the open one from its branch, or else the newest closed one whose head is the change or the commit pushed for it. A change with no pull request is left out, as is one that `sync` abandoned because its pull request merged. Fails where no change has one.", "With `--copy`, also puts the links on the clipboard as HTML, which Slack and other apps paste as links. That runs `osascript` on macOS, which also copies the Markdown as plain text, and `wl-copy` or `xclip` on Linux, which copy the HTML alone.", "In a Claude Code cloud session, where `CLAUDE_CODE_REMOTE` is `true`, `--copy` copies nothing. It tells the agent to put the Markdown in its reply, which the Claude app shows as links that paste into Slack.", "Fails if the changes aren't a chain, if one has conflicts, or if one doesn't have exactly one bookmark, as `log` does. Changes nothing, locally or on GitHub."));
19080
19134
 
19081
19135
  // src/cli/gh/log.ts
19082
19136
  var logLines2 = (entries) => {
@@ -19095,7 +19149,7 @@ var log2 = (revision) => gen2(function* () {
19095
19149
  const github = yield* GitHub;
19096
19150
  const stack = yield* stackChanges(revision);
19097
19151
  const targets = yield* bookmarkTargets;
19098
- const pullRequests = yield* all2(stack.map(({ bookmark }) => github.openPullRequest(bookmark)), { concurrency: "unbounded" });
19152
+ const pullRequests = yield* all3(stack.map(({ bookmark }) => github.openPullRequest(bookmark)), { concurrency: "unbounded" });
19099
19153
  return logLines2(stack.map((change, index) => ({
19100
19154
  bookmark: change.bookmark,
19101
19155
  pullRequest: pullRequests[index] ?? none2(),
@@ -19233,7 +19287,7 @@ class DescriptionConflict extends TaggedError2("DescriptionConflict") {
19233
19287
  var kept = (text) => ({ send: false, text });
19234
19288
  var sent = (text) => ({ send: true, text });
19235
19289
  var ignoreClaudeCodeFooterKey = "jj-octo.ignore-claude-code-footer";
19236
- var ignoresClaudeCodeFooter = jjFlag(ignoreClaudeCodeFooterKey);
19290
+ var ignoresClaudeCodeFooter = flatMap4(claudeCodeCloudSession, (cloudSession) => jjFlag(ignoreClaudeCodeFooterKey, cloudSession));
19237
19291
  var descriptionsOf = (commits) => commits.length === 0 ? succeed6(new Map) : readLog(commits.join(" | "), String.raw`commit_id ++ "\t" ++ description.escape_json() ++ "\n"`).pipe(map7((output) => new Map(lines2(output).map((line) => {
19238
19292
  const [commit = "", json = '""'] = line.split("\t");
19239
19293
  return [commit, parseDescription(json)];
@@ -19542,7 +19596,7 @@ var changedOnGitHub = (bookmarks) => gen2(function* () {
19542
19596
  var syncStack = (revision, trunk) => gen2(function* () {
19543
19597
  const downstack = downstackRevset(revision);
19544
19598
  const before = yield* bookmarksBeforeFetch(downstack);
19545
- const [heads, onGitHub] = yield* all2([mergedHeadsOf(before), changedOnGitHub(before)], {
19599
+ const [heads, onGitHub] = yield* all3([mergedHeadsOf(before), changedOnGitHub(before)], {
19546
19600
  concurrency: "unbounded"
19547
19601
  });
19548
19602
  const merged = yield* mergedIn(before, heads);
@@ -19584,7 +19638,7 @@ class SeveralStacks extends TaggedError2("SeveralStacks") {
19584
19638
  }
19585
19639
  var stackContaining = (pullRequests) => gen2(function* () {
19586
19640
  const github = yield* GitHub;
19587
- const found = yield* all2(pullRequests.map((pullRequest) => github.stackOf(pullRequest)), { concurrency: "unbounded" });
19641
+ const found = yield* all3(pullRequests.map((pullRequest) => github.stackOf(pullRequest)), { concurrency: "unbounded" });
19588
19642
  const stacks = dedupeWith(found.flatMap((stack) => toArray(stack)), (a, b) => a.number === b.number);
19589
19643
  if (stacks.length > 1) {
19590
19644
  return yield* new SeveralStacks({
@@ -19603,7 +19657,7 @@ var publish = (stack, { draft, overwriteDescriptions, trunk }) => gen2(function*
19603
19657
  const records = yield* PrDescriptions;
19604
19658
  const bookmarks = stack.map(({ bookmark }) => bookmark);
19605
19659
  const bases = basesFor(bookmarks, trunk);
19606
- const found = yield* all2(stack.map((change, index) => github.openPullRequest(change.bookmark).pipe(map7((pullRequest) => ({ base: bases[index] ?? trunk, change, pullRequest })))), { concurrency: "unbounded" });
19660
+ const found = yield* all3(stack.map((change, index) => github.openPullRequest(change.bookmark).pipe(map7((pullRequest) => ({ base: bases[index] ?? trunk, change, pullRequest })))), { concurrency: "unbounded" });
19607
19661
  const targets = yield* bookmarkTargets;
19608
19662
  const pushedDescriptions = yield* descriptionsOf(bookmarks.flatMap((bookmark) => targets.get(bookmark)?.origin ?? []));
19609
19663
  const ignoreClaudeCodeFooter = yield* ignoresClaudeCodeFooter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jj-octo",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "A jj subcommand for GitHub that supports stacked pull requests and works in Claude Code cloud sessions",
5
5
  "license": "MIT",
6
6
  "repository": {