jj-octo 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # jj-octo
2
2
 
3
- A [jj](https://github.com/jj-vcs/jj) subcommand for working with GitHub that supports stacking.
3
+ A [jj](https://github.com/jj-vcs/jj) subcommand for GitHub that supports stacked pull requests and works in Claude Code cloud sessions.
4
4
 
5
5
  > [!WARNING]
6
6
  > `jj-octo` is in early development, and its commands may change. So far it is largely vibe coded.
@@ -30,13 +30,27 @@ Then run `jj-octo init` once in each checkout. It makes the checkout a jj reposi
30
30
 
31
31
  ## GitHub access
32
32
 
33
- `jj-octo` reads the repository from the `origin` remote. It takes its token from `GH_TOKEN` or `GITHUB_TOKEN`, and otherwise from `gh auth token`. It sends requests through the proxy that `HTTPS_PROXY` names, and trusts the certificate authorities in `NODE_EXTRA_CA_CERTS` as well as Bun's own.
33
+ `jj-octo` reads the repository from the `origin` remote.
34
34
 
35
- ## In a Claude Code cloud session
35
+ By default, it reads its token from `GH_TOKEN` or `GITHUB_TOKEN`, and otherwise runs `gh auth token`.
36
36
 
37
- In a [Claude Code](https://claude.com/claude-code) cloud session, a proxy carries all traffic to GitHub. It allows REST calls only for the session's repositories, and blocks GraphQL. That's why `jj-octo` uses only GitHub's REST API, while `gh pr` and other tools that depend on GraphQL fail there.
37
+ With `jj-octo.github-auth` set to `git-proxy`, it sends a placeholder token instead. It sends requests through git's proxy for github.com, and trusts git's certificate authorities for it in place of Bun's own. [In a self-hosted environment](#in-a-self-hosted-environment) describes when to use it.
38
38
 
39
- mise can't read this repository's releases there, since it reads them through the GitHub API. Install the npm package instead:
39
+ ## In Claude Code cloud sessions
40
+
41
+ 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.
42
+
43
+ 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:
44
+
45
+ ```
46
+ jj config set --repo jj-octo.ignore-claude-code-footer true
47
+ ```
48
+
49
+ ### In an Anthropic-hosted environment
50
+
51
+ There, the [GitHub proxy](https://code.claude.com/docs/en/cloud-environments#github-proxy) carries all GitHub traffic. It serves GitHub API requests only for the repositories attached to the session, and rejects GraphQL requests. That's why `jj-octo` uses only GitHub's REST API, while `gh pr` and other tools that depend on GraphQL fail there.
52
+
53
+ mise reads this repository's releases through the GitHub API, so it can't install them unless the session has this repository attached. Install the npm package instead:
40
54
 
41
55
  ```toml
42
56
  [tools]
@@ -48,11 +62,11 @@ version = "X.Y.Z"
48
62
  allow_low_downloads = true
49
63
  ```
50
64
 
51
- The session sets `HTTPS_PROXY`, `NODE_EXTRA_CA_CERTS` and a placeholder `GH_TOKEN`. Its proxy replaces the placeholder with real credentials. So `jj-octo` works there with its default settings.
65
+ The session sets `HTTPS_PROXY`, `NODE_EXTRA_CA_CERTS`, and `GH_TOKEN` to the placeholder `proxy-injected`. The GitHub proxy replaces the placeholder with your real credentials. So `jj-octo` works there with its default settings.
52
66
 
53
- ## In a Claude Code self-hosted environment
67
+ ### In a self-hosted environment
54
68
 
55
- A self-hosted runner started with `--use-anthropic-git-proxy` sends git through Anthropic's git proxy, which adds the session user's credentials. The sandbox then holds no GitHub token, and only git's config names the proxy. There, have `jj-octo` use the same proxy:
69
+ A [self-hosted runner](https://code.claude.com/docs/en/self-hosted-environments-deploy#use-the-anthropic-git-proxy) started with `--use-anthropic-git-proxy` sends git through the Anthropic git proxy. That proxy adds the GitHub credentials of the session's creator. The session then holds no GitHub token, and only git's config names the proxy. There, have `jj-octo` use the same proxy:
56
70
 
57
71
  ```
58
72
  jj config set --repo jj-octo.github-auth git-proxy
package/dist/jj-octo.js CHANGED
@@ -18239,8 +18239,8 @@ var layer14 = /* @__PURE__ */ layerMergedContext(/* @__PURE__ */ succeed6(fetch)
18239
18239
  // package.json
18240
18240
  var package_default = {
18241
18241
  name: "jj-octo",
18242
- version: "0.4.0",
18243
- description: "A jj subcommand for working with GitHub that supports stacking",
18242
+ version: "0.5.0",
18243
+ description: "A jj subcommand for GitHub that supports stacked pull requests and works in Claude Code cloud sessions",
18244
18244
  license: "MIT",
18245
18245
  repository: {
18246
18246
  type: "git",
@@ -18839,9 +18839,13 @@ var descriptionFrom = ({
18839
18839
  }) => body === "" ? title : `${title}
18840
18840
 
18841
18841
  ${body}`;
18842
- var normalizedDescription = (description) => descriptionFrom(titleAndBody(description.replaceAll(`\r
18842
+ var claudeCodeFooter = /\n*---\n_Generated by \[Claude Code\]\(https:\/\/claude\.ai\/code[^\s)]*\)_\s*$/u;
18843
+ var normalizedDescription = (description, { ignoreClaudeCodeFooter }) => {
18844
+ const text = description.replaceAll(`\r
18843
18845
  `, `
18844
- `)));
18846
+ `);
18847
+ return descriptionFrom(titleAndBody(ignoreClaudeCodeFooter ? text.replace(claudeCodeFooter, "") : text));
18848
+ };
18845
18849
  var descriptionSyncFor = ({
18846
18850
  local,
18847
18851
  onGitHub,
@@ -18859,18 +18863,39 @@ var descriptionSyncFor = ({
18859
18863
  // src/cli/gh/stack/descriptions.ts
18860
18864
  class DescriptionConflict extends TaggedError2("DescriptionConflict") {
18861
18865
  }
18866
+ var ignoreClaudeCodeFooterKey = "jj-octo.ignore-claude-code-footer";
18867
+ var ignoresClaudeCodeFooter = gen2(function* () {
18868
+ const jj = yield* Jj;
18869
+ const value = (yield* jj.read(["config", "get", ignoreClaudeCodeFooterKey]).pipe(orElseSucceed2(() => ""))).trim();
18870
+ switch (value) {
18871
+ case "":
18872
+ case "false": {
18873
+ return false;
18874
+ }
18875
+ case "true": {
18876
+ return true;
18877
+ }
18878
+ default: {
18879
+ return yield* new JjFailed({
18880
+ message: `${ignoreClaudeCodeFooterKey} is ${JSON.stringify(value)}; set it to true or false`
18881
+ });
18882
+ }
18883
+ }
18884
+ });
18862
18885
  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) => {
18863
18886
  const [commit = "", json = '""'] = line.split("\t");
18864
18887
  const parsed = JSON.parse(json);
18865
18888
  return [commit, typeof parsed === "string" ? parsed : ""];
18866
18889
  }))));
18867
- var syncDescription = ({ bookmark, description }, pullRequest, { overwriteDescriptions, pushedDescription }) => gen2(function* () {
18890
+ var syncDescription = ({ bookmark, description }, pullRequest, { ignoreClaudeCodeFooter, overwriteDescriptions, pushedDescription }) => gen2(function* () {
18868
18891
  const git = yield* Git;
18869
18892
  const jj = yield* Jj;
18870
18893
  const describe = (text) => jj.run(["describe", "-r", bookmarkRevset(bookmark), "-m", text]);
18871
- const local = normalizedDescription(description);
18872
- const onGitHub = normalizedDescription(descriptionFrom(pullRequest));
18873
- const pushed = normalizedDescription(pushedDescription);
18894
+ const local = normalizedDescription(description, { ignoreClaudeCodeFooter });
18895
+ const onGitHub = normalizedDescription(descriptionFrom(pullRequest), {
18896
+ ignoreClaudeCodeFooter
18897
+ });
18898
+ const pushed = normalizedDescription(pushedDescription, { ignoreClaudeCodeFooter });
18874
18899
  return yield* value4(descriptionSyncFor({ local, onGitHub, pushed })).pipe(tag3("InSync", () => succeed6(none2())), tag3("TakeGitHub", () => describe(onGitHub).pipe(andThen2(error(`took the description of #${pullRequest.number} from GitHub`)), as2(none2()))), tag3("SendLocal", () => succeed6(some2(local))), tag3("Merge", () => gen2(function* () {
18875
18900
  if (overwriteDescriptions) {
18876
18901
  return some2(local);
@@ -18920,12 +18945,14 @@ var publish = (stack, { draft, overwriteDescriptions }) => gen2(function* () {
18920
18945
  const found = yield* all2(stack.map((change, index) => github.openPullRequest(change.bookmark).pipe(map7((pullRequest) => ({ base: bases[index] ?? trunk, change, pullRequest })))), { concurrency: "unbounded" });
18921
18946
  const targets = yield* bookmarkTargets;
18922
18947
  const pushedDescriptions = yield* descriptionsOf(bookmarks.flatMap((bookmark) => targets.get(bookmark)?.origin ?? []));
18948
+ const ignoreClaudeCodeFooter = yield* ignoresClaudeCodeFooter;
18923
18949
  const changes = [];
18924
18950
  for (const entry of found) {
18925
18951
  const pushed = targets.get(entry.change.bookmark)?.origin;
18926
18952
  changes.push({
18927
18953
  ...entry,
18928
18954
  description: isNone2(entry.pullRequest) ? none2() : yield* syncDescription(entry.change, entry.pullRequest.value, {
18955
+ ignoreClaudeCodeFooter,
18929
18956
  overwriteDescriptions,
18930
18957
  pushedDescription: (pushed === undefined ? undefined : pushedDescriptions.get(pushed)) ?? ""
18931
18958
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jj-octo",
3
- "version": "0.4.0",
4
- "description": "A jj subcommand for working with GitHub that supports stacking",
3
+ "version": "0.5.0",
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": {
7
7
  "type": "git",