@unpolarize/code-sessions 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.
@@ -634,17 +634,32 @@ var GitStore = class {
634
634
  isRepo() {
635
635
  return existsSync5(join3(this.dir, ".git"));
636
636
  }
637
- /** Initialize the store repo (idempotent): git init, scaffolding files, remote. */
637
+ /** Initialize the store repo (idempotent): git init, connect remote (adopting
638
+ * its existing history on a fresh clone so a second machine continues the same
639
+ * store instead of forking), then write scaffolding files if still absent. */
638
640
  init() {
639
641
  if (!this.isRepo()) {
640
642
  const r = spawnSync("git", ["init", "-b", "main", this.dir], { encoding: "utf8" });
641
643
  if (r.status !== 0) throw new Error(`git init failed: ${r.stderr}`);
642
644
  }
645
+ if (this.opts.remote) {
646
+ this.ensureRemote(this.opts.remote);
647
+ this.adoptRemoteIfEmpty();
648
+ }
643
649
  const giPath = join3(this.dir, ".gitignore");
644
650
  if (!existsSync5(giPath)) writeFileSync3(giPath, GITIGNORE);
645
651
  const gaPath = join3(this.dir, ".gitattributes");
646
652
  if (!existsSync5(gaPath)) writeFileSync3(gaPath, GITATTRIBUTES);
647
- if (this.opts.remote) this.ensureRemote(this.opts.remote);
653
+ }
654
+ /** When this clone has no commits yet, pull the remote's existing store so we
655
+ * continue its history (multi-machine) rather than starting a divergent one.
656
+ * No-op for an empty/unreachable remote. */
657
+ adoptRemoteIfEmpty() {
658
+ if (this.run(["rev-parse", "--verify", "HEAD"]).ok) return;
659
+ if (!this.run(["fetch", "origin"]).ok) return;
660
+ if (!this.run(["rev-parse", "--verify", "origin/main"]).ok) return;
661
+ this.run(["reset", "--hard", "origin/main"]);
662
+ this.run(["branch", "--set-upstream-to=origin/main", "main"]);
648
663
  }
649
664
  ensureRemote(remote) {
650
665
  const existing = this.run(["remote", "get-url", "origin"]);
@@ -2498,7 +2513,17 @@ function cmdInit(cfg) {
2498
2513
  if (!existsSync16(configPath)) {
2499
2514
  writeFileSync9(
2500
2515
  configPath,
2501
- `${JSON.stringify({ insights: cfg.insights, batch: cfg.batch, hygiene: cfg.hygiene }, null, 2)}
2516
+ `${JSON.stringify(
2517
+ {
2518
+ insights: cfg.insights,
2519
+ batch: cfg.batch,
2520
+ hygiene: cfg.hygiene,
2521
+ // persist the remote so the daemon/CLI keep pushing here without flags
2522
+ git: { ...cfg.git.remote ? { remote: cfg.git.remote } : {}, autoPush: cfg.git.autoPush }
2523
+ },
2524
+ null,
2525
+ 2
2526
+ )}
2502
2527
  `
2503
2528
  );
2504
2529
  }
package/dist/cli.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  parseFlags,
25
25
  readStdin,
26
26
  startDaemon
27
- } from "./chunk-3VPXOUIE.js";
27
+ } from "./chunk-Y7DRROK7.js";
28
28
 
29
29
  // src/analytics/command.ts
30
30
  import { mkdirSync, writeFileSync } from "fs";
package/dist/index.js CHANGED
@@ -93,7 +93,7 @@ import {
93
93
  writeBlobFile,
94
94
  writeImportedSession,
95
95
  writeTurnFile
96
- } from "./chunk-3VPXOUIE.js";
96
+ } from "./chunk-Y7DRROK7.js";
97
97
  export {
98
98
  CaptureEngine,
99
99
  DEFAULT_HOOK_EVENTS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unpolarize/code-sessions",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Headless, event-driven cross-agent session capture agent (daemon + CLI)",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,7 +25,7 @@
25
25
  "build": "tsup src/index.ts src/cli.ts --format esm --clean --out-dir dist"
26
26
  },
27
27
  "dependencies": {
28
- "@unpolarize/code-sessions-schema": "^0.4.0",
28
+ "@unpolarize/code-sessions-schema": "^0.5.0",
29
29
  "zod": "^3.23.8"
30
30
  }
31
31
  }
package/src/commands.ts CHANGED
@@ -59,7 +59,17 @@ export function cmdInit(cfg: CodeSessionsConfig): CommandResult {
59
59
  if (!existsSync(configPath)) {
60
60
  writeFileSync(
61
61
  configPath,
62
- `${JSON.stringify({ insights: cfg.insights, batch: cfg.batch, hygiene: cfg.hygiene }, null, 2)}\n`,
62
+ `${JSON.stringify(
63
+ {
64
+ insights: cfg.insights,
65
+ batch: cfg.batch,
66
+ hygiene: cfg.hygiene,
67
+ // persist the remote so the daemon/CLI keep pushing here without flags
68
+ git: { ...(cfg.git.remote ? { remote: cfg.git.remote } : {}), autoPush: cfg.git.autoPush },
69
+ },
70
+ null,
71
+ 2,
72
+ )}\n`,
63
73
  );
64
74
  }
65
75
  git.commit('init store');
package/src/store/git.ts CHANGED
@@ -58,17 +58,35 @@ export class GitStore {
58
58
  return existsSync(join(this.dir, '.git'));
59
59
  }
60
60
 
61
- /** Initialize the store repo (idempotent): git init, scaffolding files, remote. */
61
+ /** Initialize the store repo (idempotent): git init, connect remote (adopting
62
+ * its existing history on a fresh clone so a second machine continues the same
63
+ * store instead of forking), then write scaffolding files if still absent. */
62
64
  init(): void {
63
65
  if (!this.isRepo()) {
64
66
  const r = spawnSync('git', ['init', '-b', 'main', this.dir], { encoding: 'utf8' });
65
67
  if (r.status !== 0) throw new Error(`git init failed: ${r.stderr}`);
66
68
  }
69
+ // Connect + adopt remote BEFORE writing scaffolding, so an adopted checkout
70
+ // doesn't collide with untracked .gitignore/.gitattributes we'd write.
71
+ if (this.opts.remote) {
72
+ this.ensureRemote(this.opts.remote);
73
+ this.adoptRemoteIfEmpty();
74
+ }
67
75
  const giPath = join(this.dir, '.gitignore');
68
76
  if (!existsSync(giPath)) writeFileSync(giPath, GITIGNORE);
69
77
  const gaPath = join(this.dir, '.gitattributes');
70
78
  if (!existsSync(gaPath)) writeFileSync(gaPath, GITATTRIBUTES);
71
- if (this.opts.remote) this.ensureRemote(this.opts.remote);
79
+ }
80
+
81
+ /** When this clone has no commits yet, pull the remote's existing store so we
82
+ * continue its history (multi-machine) rather than starting a divergent one.
83
+ * No-op for an empty/unreachable remote. */
84
+ private adoptRemoteIfEmpty(): void {
85
+ if (this.run(['rev-parse', '--verify', 'HEAD']).ok) return; // already has local history
86
+ if (!this.run(['fetch', 'origin']).ok) return; // empty or unreachable remote
87
+ if (!this.run(['rev-parse', '--verify', 'origin/main']).ok) return; // remote has no main
88
+ this.run(['reset', '--hard', 'origin/main']);
89
+ this.run(['branch', '--set-upstream-to=origin/main', 'main']);
72
90
  }
73
91
 
74
92
  ensureRemote(remote: string): void {