claude-git-sessions 0.2.0 → 0.3.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
@@ -74,26 +74,32 @@ Branch names are validated with `git check-ref-format` before use.
74
74
 
75
75
  ## Commands
76
76
 
77
- ### `ccgs pull`
77
+ ### `ccgs pull [--force] [--exclude-memory]`
78
78
 
79
- Fetches `@ccgs/<name>` and writes each session where Claude Code expects it:
80
- `~/.claude/projects/<local-slug>/<id>.jsonl`. The structural `cwd` field in each
81
- transcript line is rewritten from the author's path to your local equivalent so
82
- `claude --resume` works. (Absolute paths inside tool *output* are left as-is
83
- cosmetic only.)
79
+ Fetches `@ccgs/<name>` and writes each session into the **repo root's** project
80
+ slug — `~/.claude/projects/<root-slug>/<id>.jsonl` so that `claude --resume`
81
+ run at the repo root lists every pulled session. The structural `cwd` field in
82
+ each transcript line is rewritten from the author's path to your repo root so
83
+ resume works. (Absolute paths inside tool *output* are left as-is — cosmetic.)
84
84
 
85
- If a local session is **newer** than the shared copy it is skipped with a
86
- warning; pass `--force` to overwrite anyway. Prints what was pulled / skipped.
85
+ > Sessions are always placed under the repo-root slug, even if the author
86
+ > launched Claude in a subdirectory. Honoring the author's subdir would put the
87
+ > session in a separate (and possibly non-existent) slug dir where `--resume`
88
+ > at the root can't see it. The original subdir is still recorded in `meta.json`.
87
89
 
88
- If the branch doesn't exist, it prints `nothing to pull` and exits 0.
90
+ By default this **also pulls shared memory** (see `ccgs memory`); pass
91
+ `--exclude-memory` for sessions only. If a local session is **newer** than the
92
+ shared copy it is skipped with a warning; pass `--force` to overwrite. If the
93
+ branch doesn't exist, it prints `nothing to pull` and exits 0.
89
94
 
90
- ### `ccgs push [targets...]`
95
+ ### `ccgs push [targets...] [--exclude-memory]`
91
96
 
92
97
  Finds local sessions whose working directory is this repo (or a subdirectory),
93
98
  copies each transcript verbatim onto the orphan branch, and (re)generates its
94
99
  `meta.json`. With no arguments it pushes all of them; pass session ids/names to
95
- push only those. Creates the orphan branch on first push. Reports added vs
96
- updated.
100
+ push only those. By default this **also pushes shared memory** (project /
101
+ reference facts); pass `--exclude-memory` for sessions only. Creates the orphan
102
+ branch on first push. Reports added vs updated.
97
103
 
98
104
  ### `ccgs delete <id|name> [--yes] [--local]`
99
105
 
@@ -109,6 +115,10 @@ Claude Code keeps per-project memory in `~/.claude/projects/<slug>/memory/` —
109
115
  one Markdown file per fact, plus a `MEMORY.md` index. `ccgs memory` shares those
110
116
  facts on the same orphan branch (under a `memory/` prefix).
111
117
 
118
+ Plain `ccgs push` / `ccgs pull` already include memory by default (with the
119
+ type filter below, i.e. no personal facts). Use these `memory` subcommands when
120
+ you want memory **only**, or need `--all` to include personal facts.
121
+
112
122
  Memory is *mixed-sensitivity*, so it's filtered by the fact's frontmatter
113
123
  `type`:
114
124
 
@@ -1,5 +1,4 @@
1
1
  import fs from "node:fs";
2
- import path from "node:path";
3
2
  import { SESSIONS_DIR } from "../constants.js";
4
3
  import { assertRemote, fetchBranch, localTrackingRef, showFile, validateBranchName, } from "../git.js";
5
4
  import { listBranchSessions } from "../branch-sessions.js";
@@ -28,9 +27,13 @@ export async function pull(opts) {
28
27
  skipped.push(`${shortId(id)}: missing transcript blob`);
29
28
  continue;
30
29
  }
31
- // Reconstruct the local cwd for this session: repo root + recorded subdir.
32
- const rel = meta?.cwdRelativeToRepoRoot ?? "";
33
- const localCwd = rel ? path.join(opts.repoRoot, rel) : opts.repoRoot;
30
+ // Always land the session under the repo ROOT slug, so `claude --resume`
31
+ // run at the repo root lists it. (We deliberately do NOT honor the author's
32
+ // `cwdRelativeToRepoRoot` here: that placed subdir-authored sessions in a
33
+ // separate — and often non-existent — slug dir, making them invisible to
34
+ // resume at the root. The author's subdir is still recorded in meta for
35
+ // provenance.)
36
+ const localCwd = opts.repoRoot;
34
37
  const name = meta?.name ?? id;
35
38
  // Conflict policy: newer local copy is kept unless --force.
36
39
  const localFile = sessionFilePath(localCwd, id);
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { deleteSession } from "./commands/delete.js";
8
8
  import { memoryPull, memoryPush } from "./commands/memory.js";
9
9
  // Kept in sync with package.json at publish time; hardcoded to avoid a JSON
10
10
  // import assertion just for --version.
11
- const VERSION = "0.2.0";
11
+ const VERSION = "0.3.0";
12
12
  function globals(cmd) {
13
13
  const opts = cmd.optsWithGlobals();
14
14
  return {
@@ -34,24 +34,44 @@ program
34
34
  .option("--remote <remote>", "git remote to use", DEFAULT_REMOTE);
35
35
  program
36
36
  .command("pull")
37
- .description("fetch shared sessions and place them where Claude Code can resume them")
37
+ .description("fetch shared sessions (and memory) and place them where Claude Code can resume them")
38
38
  .option("--force", "overwrite local copies even if they are newer", false)
39
+ .option("--exclude-memory", "pull sessions only; do not also pull memory", false)
39
40
  .action(async (localOpts, cmd) => {
40
41
  const g = globals(cmd);
41
42
  await run(async () => {
42
43
  const root = await repoRoot();
43
- return pull({ repoRoot: root, remote: g.remote, branch: g.branch, force: localOpts.force });
44
+ let code = await pull({ repoRoot: root, remote: g.remote, branch: g.branch, force: localOpts.force });
45
+ if (!localOpts.excludeMemory) {
46
+ console.log("\nMemory:");
47
+ const m = await memoryPull({
48
+ repoRoot: root,
49
+ remote: g.remote,
50
+ branch: g.branch,
51
+ all: false,
52
+ force: localOpts.force,
53
+ });
54
+ code = code || m;
55
+ }
56
+ return code;
44
57
  });
45
58
  });
46
59
  program
47
60
  .command("push")
48
- .description("publish this repo's local sessions to the orphan branch")
61
+ .description("publish this repo's local sessions (and shared memory) to the orphan branch")
49
62
  .argument("[targets...]", "optional session ids/names to push (default: all)")
50
- .action(async (targets, _localOpts, cmd) => {
63
+ .option("--exclude-memory", "push sessions only; do not also push memory", false)
64
+ .action(async (targets, localOpts, cmd) => {
51
65
  const g = globals(cmd);
52
66
  await run(async () => {
53
67
  const root = await repoRoot();
54
- return push({ repoRoot: root, remote: g.remote, branch: g.branch, filters: targets });
68
+ let code = await push({ repoRoot: root, remote: g.remote, branch: g.branch, filters: targets });
69
+ if (!localOpts.excludeMemory) {
70
+ console.log("\nMemory:");
71
+ const m = await memoryPush({ repoRoot: root, remote: g.remote, branch: g.branch, all: false });
72
+ code = code || m;
73
+ }
74
+ return code;
55
75
  });
56
76
  });
57
77
  program
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-git-sessions",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Share Claude Code sessions across a team through an orphan git branch in your existing repo.",
5
5
  "keywords": ["claude", "claude-code", "git", "sessions", "cli"],
6
6
  "license": "MIT",