@leantli/agent-handoff 0.5.2 → 0.6.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
@@ -66,7 +66,7 @@ For project-specific decisions or branch-specific context:
66
66
 
67
67
  ```bash
68
68
  agent-handoff learn --scope project --kind decision --note "Use TypeScript for the CLI."
69
- agent-handoff learn --scope branch --kind context --note "This branch is testing v0.5."
69
+ agent-handoff learn --scope branch --kind context --note "This branch is testing the sync workflow."
70
70
  ```
71
71
 
72
72
  ## Cross-Device Sync
@@ -74,18 +74,29 @@ agent-handoff learn --scope branch --kind context --note "This branch is testing
74
74
  Local cross-session memory works immediately after `agent-handoff enable`. No
75
75
  git repository is needed for the vault.
76
76
 
77
- Cross-device sync is optional. To share memory across devices, create a private
78
- git repository for the vault, then run:
77
+ Cross-device sync is optional. Create a dedicated private repository for the
78
+ vault. Do not use a project code repository as the vault. Then run:
79
79
 
80
80
  ```bash
81
81
  agent-handoff sync init git@github.com:you/agent-handoff-vault.git
82
82
  agent-handoff sync
83
83
  ```
84
84
 
85
+ Use a private repository because the vault can contain project background,
86
+ preferences, decisions, and handoff notes.
87
+
85
88
  Run `agent-handoff sync init <same-git-url>` once on each device that should
86
89
  share the vault. After that, run `agent-handoff sync` before starting on another
87
90
  device and after writing useful checkpoints.
88
91
 
92
+ By default the vault lives at `~/.agent-handoff/vault`. Use `--home` or
93
+ `--vault` when you need a different location. `agent-handoff sync` commits local
94
+ vault changes, pulls/rebases remote vault changes, then pushes the result.
95
+ If `sync init` says the local vault has unsynced memory and the remote already
96
+ has data, back up or manually merge the local vault before joining that remote.
97
+ If git reports a conflict or active operation, resolve the conflict in the vault,
98
+ finish or abort the active git operation, then run `agent-handoff sync` again.
99
+
89
100
  ## How Projects Are Identified
90
101
 
91
102
  By default, `agent-handoff` identifies the current project from the git `origin`
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ function buildProgram(stdout, stderr, stdin, cwd) {
25
25
  program
26
26
  .name("agent-handoff")
27
27
  .description("Shared vault handoff memory for coding agents.")
28
- .version("agent-handoff 0.5.2")
28
+ .version(`agent-handoff ${packageVersion()}`)
29
29
  .exitOverride()
30
30
  .configureOutput({
31
31
  writeOut: (str) => stdout.write(str),
@@ -94,7 +94,7 @@ function buildProgram(stdout, stderr, stdin, cwd) {
94
94
  const sync = program.command("sync").description("Sync the handoff vault.");
95
95
  sync
96
96
  .command("init <git-url>")
97
- .description("Enable cross-device sync with a private git repository.")
97
+ .description("Enable cross-device sync with a dedicated private vault repository.")
98
98
  .option("--vault <path>", "Vault directory. Defaults to HOME/vault.")
99
99
  .action((gitUrl, options) => {
100
100
  const result = enableSync({ home: globalHome(program), vault: options.vault, syncUrl: gitUrl });
@@ -141,6 +141,13 @@ function readNote(options, stdin) {
141
141
  }
142
142
  return readFileSync(0, "utf8");
143
143
  }
144
+ function packageVersion() {
145
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
146
+ if (typeof pkg.version !== "string") {
147
+ throw new HandoffError("package.json is missing a string version");
148
+ }
149
+ return pkg.version;
150
+ }
144
151
  function printProblems(problems, stdout) {
145
152
  for (const problem of problems) {
146
153
  stdout.write(`- ${problem}\n`);
package/dist/core.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
3
3
  import { hostname, homedir } from "node:os";
4
- import { join, resolve } from "node:path";
4
+ import { isAbsolute, join, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  export const DEFAULT_HOME = join(homedir(), ".agent-handoff");
7
7
  export const CONFIG_FILE = "config.json";
@@ -249,6 +249,9 @@ export function syncVault(opts = {}) {
249
249
  if (syncProblem)
250
250
  throw new HandoffError(syncProblem);
251
251
  const outputs = [];
252
+ if (hasActiveGitOperation(setup.vault) || hasUnmergedFiles(setup.vault)) {
253
+ throw syncConflictError(setup.vault);
254
+ }
252
255
  gitChecked(setup.vault, ["add", "-A"]);
253
256
  const staged = gitRun(setup.vault, ["diff", "--cached", "--quiet"]).status !== 0;
254
257
  if (staged) {
@@ -266,6 +269,9 @@ export function syncVault(opts = {}) {
266
269
  const pull = gitRun(setup.vault, ["pull", "--rebase", "--autostash", "origin", branch]);
267
270
  const ignoredEmptyRemotePull = pull.status !== 0 && isEmptyRemotePull(pull.output);
268
271
  if (pull.status !== 0 && !ignoredEmptyRemotePull) {
272
+ if (hasActiveGitOperation(setup.vault) || hasUnmergedFiles(setup.vault)) {
273
+ throw syncConflictError(setup.vault, pull.output);
274
+ }
269
275
  throw new HandoffError(pull.output.trim() || "git pull failed");
270
276
  }
271
277
  if (!ignoredEmptyRemotePull && pull.output.trim())
@@ -721,6 +727,24 @@ function gitRun(root, args) {
721
727
  output: `${result.stdout ?? ""}${result.stderr ?? ""}${error}`,
722
728
  };
723
729
  }
730
+ function hasUnmergedFiles(vault) {
731
+ const result = gitRun(vault, ["diff", "--name-only", "--diff-filter=U"]);
732
+ return result.status === 0 && result.output.trim().length > 0;
733
+ }
734
+ function hasActiveGitOperation(vault) {
735
+ return ["rebase-merge", "rebase-apply", "MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD"].some((name) => gitPathExists(vault, name));
736
+ }
737
+ function gitPathExists(vault, name) {
738
+ const path = gitOutput(vault, ["rev-parse", "--git-path", name]);
739
+ if (!path)
740
+ return false;
741
+ return existsSync(isAbsolute(path) ? path : join(vault, path));
742
+ }
743
+ function syncConflictError(vault, detail) {
744
+ const message = `sync conflict in ${vault}; resolve conflicts in that vault, finish or abort the active git operation, then run agent-handoff sync again`;
745
+ const trimmed = detail?.trim();
746
+ return new HandoffError(trimmed ? `${message}\n\n${trimmed}` : message);
747
+ }
724
748
  function isEmptyRemotePull(output) {
725
749
  const lowered = output.toLowerCase();
726
750
  return lowered.includes("couldn't find remote ref") || lowered.includes("could not find remote ref");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leantli/agent-handoff",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Shared vault handoff memory for coding agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -22,6 +22,32 @@ When beginning work in a repository:
22
22
  Do not edit `AGENTS.md`, `CLAUDE.md`, or other instruction files to install
23
23
  agent-handoff.
24
24
 
25
+ ## Cross-Device Sync Setup
26
+
27
+ Use a dedicated private vault repository for sync. Do not use a project code
28
+ repository as the vault. The vault can contain project context, preferences,
29
+ decisions, and handoff notes.
30
+
31
+ If the user asks you to set up cross-device sync, make sure the vault repository
32
+ they created is private, then run:
33
+
34
+ ```bash
35
+ agent-handoff sync init <private-git-url>
36
+ agent-handoff sync
37
+ ```
38
+
39
+ On another device, install and enable the CLI, then run `sync init` with the same
40
+ private git URL, `agent-handoff sync`, and `agent-handoff start`.
41
+
42
+ If `sync init` says the local vault has unsynced memory and the remote already
43
+ has data, keep the local files and report it. The user or agent should back up
44
+ or manually merge the local vault before joining that remote.
45
+
46
+ If sync fails because git reports a conflict or active operation, keep the local
47
+ files and report the conflict. The user or agent should resolve it inside the
48
+ vault, finish or abort the active git operation, then run `agent-handoff sync`
49
+ again.
50
+
25
51
  ## During Work
26
52
 
27
53
  Use `learn` only for stable facts that should survive future sessions, clones,