kith-coord 0.1.4 → 0.1.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.5 — 2026-10-22
4
+
5
+ - Group read-only radar observations by checkout, distinguishing current recorded Kith leases, retained sessions, unregistered checkouts and unavailable ownership. Label counts as paths to review before integration, with full checkout/owner details and preservation guidance. Reuse the same snapshot in the optional editor view without extra status subprocesses. No ownership, recovery or cleanup behavior changes.
6
+ - Label checkout counts as shared paths and recent file mtimes as the six shown within a five-minute window; improve read-only menu text contrast.
7
+
3
8
  ## 0.1.4 — 2026-10-22
4
9
 
5
10
  - Include the optional existing VS Code/Cursor adapter, bundling the same Kith core and a read-only view. Preserve existing MCP configuration and native trust/approval controls.
package/README.md CHANGED
@@ -43,7 +43,7 @@ Git recording and integration require a commit identity configured for the repos
43
43
  Install the release:
44
44
 
45
45
  ```sh
46
- npm install --global kith-coord@0.1.4
46
+ npm install --global kith-coord@0.1.5
47
47
  ```
48
48
 
49
49
  Choose the setup scope your client needs:
package/bin/kith-menubar CHANGED
Binary file
package/dist/cli/index.js CHANGED
@@ -6,7 +6,7 @@ import path19 from "path";
6
6
  import { spawn } from "child_process";
7
7
 
8
8
  // src/version.ts
9
- var KITH_VERSION = "0.1.4";
9
+ var KITH_VERSION = "0.1.5";
10
10
  var KITH_PROTOCOL_VERSION = "1.3.0";
11
11
 
12
12
  // src/cli/setup-ledger.ts
@@ -793,7 +793,7 @@ function previewIntegration(repoRoot, agent) {
793
793
 
794
794
  // src/core/observation.ts
795
795
  var { DatabaseSync: DatabaseSync2 } = createRequire2(import.meta.url)("node:sqlite");
796
- function readCoordinationState(directory = process.cwd()) {
796
+ function readCoordinationState(directory = process.cwd(), options = {}) {
797
797
  const layout = discoverRepository(directory), observedAt = Date.now();
798
798
  const empty = {
799
799
  protocolVersion: KITH_PROTOCOL_VERSION,
@@ -852,6 +852,10 @@ function readCoordinationState(directory = process.cwd()) {
852
852
  if (CoordDatabase.inspectSchema(db) !== 2) throw new Error("Offline observation requires schema 2; preserve state and upgrade with clients stopped");
853
853
  const all = (sql) => db.prepare(sql).all();
854
854
  const agents = all("SELECT * FROM agents ORDER BY registered_at DESC");
855
+ if (options.ownersOnly) {
856
+ db.exec("COMMIT");
857
+ return { ...empty, state: "ready", agents };
858
+ }
855
859
  const state = {
856
860
  ...empty,
857
861
  state: "ready",
@@ -4925,6 +4929,8 @@ var PassiveConflictWatcher = class _PassiveConflictWatcher {
4925
4929
  activeCollisions = /* @__PURE__ */ new Map();
4926
4930
  repositories = /* @__PURE__ */ new Map();
4927
4931
  errors = [];
4932
+ primaryRoots = /* @__PURE__ */ new Map();
4933
+ worktrees = [];
4928
4934
  static notifyMac(title, message) {
4929
4935
  if (process.platform !== "darwin") return;
4930
4936
  try {
@@ -4935,6 +4941,7 @@ var PassiveConflictWatcher = class _PassiveConflictWatcher {
4935
4941
  discoverGitRepos() {
4936
4942
  this.errors = [];
4937
4943
  this.repositories.clear();
4944
+ this.primaryRoots.clear();
4938
4945
  const candidates = [];
4939
4946
  const marker = (dir) => {
4940
4947
  try {
@@ -4963,6 +4970,7 @@ var PassiveConflictWatcher = class _PassiveConflictWatcher {
4963
4970
  const layout = discoverRepository(candidate);
4964
4971
  if (!layout.commonGitDir) throw new Error("Git repository unavailable");
4965
4972
  this.repositories.set(layout.checkoutRoot, layout.commonGitDir);
4973
+ if (!layout.isLinkedWorktree) this.primaryRoots.set(layout.commonGitDir, layout.checkoutRoot);
4966
4974
  if (inspected.has(layout.commonGitDir)) continue;
4967
4975
  inspected.add(layout.commonGitDir);
4968
4976
  const listing = execFileSync10("git", ["worktree", "list", "--porcelain", "-z"], { cwd: layout.checkoutRoot, encoding: "utf8", timeout: 4e3, maxBuffer: 4 * 1024 * 1024, stdio: ["ignore", "pipe", "pipe"] });
@@ -4970,6 +4978,7 @@ var PassiveConflictWatcher = class _PassiveConflictWatcher {
4970
4978
  const linked = discoverRepository(record.slice(9));
4971
4979
  if (linked.commonGitDir !== layout.commonGitDir) throw new Error("Worktree repository identity changed during discovery");
4972
4980
  this.repositories.set(linked.checkoutRoot, layout.commonGitDir);
4981
+ if (!linked.isLinkedWorktree) this.primaryRoots.set(layout.commonGitDir, linked.checkoutRoot);
4973
4982
  }
4974
4983
  } catch (error) {
4975
4984
  this.errors.push({ repository: candidate, message: `Cannot inspect repository: ${error instanceof Error ? error.message : String(error)}` });
@@ -4982,10 +4991,11 @@ var PassiveConflictWatcher = class _PassiveConflictWatcher {
4982
4991
  const repos = this.discoverGitRepos();
4983
4992
  const now = Date.now();
4984
4993
  const seen = /* @__PURE__ */ new Set();
4994
+ const dirtyCounts = /* @__PURE__ */ new Map();
4985
4995
  const overlaps = /* @__PURE__ */ new Map();
4986
4996
  for (const repo of repos) {
4987
4997
  try {
4988
- const { stdout } = await execFileAsync("git", ["-c", "core.fsmonitor=false", "status", "--porcelain=v1", "-z", "--untracked-files=all"], { cwd: repo, timeout: 4e3, maxBuffer: 4 * 1024 * 1024 });
4998
+ const { stdout } = await execFileAsync("git", ["-c", "core.fsmonitor=false", "status", "--porcelain=v1", "-z", "--untracked-files=all"], { cwd: repo, timeout: 4e3, maxBuffer: 4 * 1024 * 1024, env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" } });
4989
4999
  const records = stdout.split("\0");
4990
5000
  const dirty = /* @__PURE__ */ new Map();
4991
5001
  for (let i = 0; i < records.length; i++) {
@@ -4995,6 +5005,7 @@ var PassiveConflictWatcher = class _PassiveConflictWatcher {
4995
5005
  dirty.set(record.slice(3), status);
4996
5006
  if (/[RC]/.test(status)) dirty.set(records[++i], status);
4997
5007
  }
5008
+ dirtyCounts.set(repo, [...dirty.keys()].filter((file) => file && file !== ".coord" && !file.startsWith(".coord/")).length);
4998
5009
  for (const [file, status] of dirty) {
4999
5010
  if (!file || file === ".coord" || file.startsWith(".coord/")) continue;
5000
5011
  const key = JSON.stringify([repo, file]);
@@ -5051,6 +5062,39 @@ var PassiveConflictWatcher = class _PassiveConflictWatcher {
5051
5062
  }
5052
5063
  }
5053
5064
  this.activeCollisions = this.errors.length ? new Map([...this.activeCollisions, ...current]) : current;
5065
+ const states = /* @__PURE__ */ new Map();
5066
+ this.worktrees = repos.map((repo) => {
5067
+ const common = this.repositories.get(repo);
5068
+ const primary = this.primaryRoots.get(common);
5069
+ const row = {
5070
+ path: repo,
5071
+ label: `${path17.basename(primary ?? path17.dirname(common))} / ${repo === primary ? "primary" : path17.basename(repo)}`,
5072
+ activity: "unknown",
5073
+ agentIds: [],
5074
+ detail: "Ownership unavailable; inspect kith status --offline before recovery.",
5075
+ dirtyFiles: dirtyCounts.get(repo) ?? null,
5076
+ sharedDirtyPaths: [...this.activeCollisions.values()].filter((alert) => alert.repos.includes(repo)).length
5077
+ };
5078
+ try {
5079
+ let state = states.get(common);
5080
+ if (!state) {
5081
+ state = readCoordinationState(primary ?? repo, { ownersOnly: true });
5082
+ states.set(common, state);
5083
+ }
5084
+ if (state.state === "unavailable") {
5085
+ row.detail += ` ${state.error}`;
5086
+ return row;
5087
+ }
5088
+ const owners = state.agents.filter((agent) => agent.worktree_path ? physicalPath(agent.worktree_path) === repo : repo === primary);
5089
+ const active = owners.filter((agent) => agent.status === "active" && Number.isFinite(agent.lease_expires_at) && agent.lease_expires_at > state.observedAt);
5090
+ row.agentIds = (active.length ? active : owners).map((agent) => agent.id);
5091
+ row.activity = active.length ? "active" : owners.length ? "retained" : "unregistered";
5092
+ row.detail = active.length ? "Current recorded Kith lease; does not identify file writers." : owners.length ? `Recorded ${[...new Set(owners.map((agent) => agent.status === "active" ? "expired lease" : agent.status))].join(", ")}; preserve work and inspect before cleanup.` : "No recorded Kith session; activity and ownership outside Kith are unknown.";
5093
+ } catch (error) {
5094
+ row.detail += ` ${error instanceof Error ? error.message : String(error)}`;
5095
+ }
5096
+ return row;
5097
+ });
5054
5098
  if (this.errors.length) this.options.onScanError?.(this.errors);
5055
5099
  return this.getActiveCollisions();
5056
5100
  }
@@ -5074,7 +5118,7 @@ var PassiveConflictWatcher = class _PassiveConflictWatcher {
5074
5118
  this.watchTimer = null;
5075
5119
  }
5076
5120
  getScanState() {
5077
- return { complete: this.errors.length === 0, errors: [...this.errors], repos: [...this.repositories.keys()] };
5121
+ return { complete: this.errors.length === 0, errors: [...this.errors], repos: [...this.repositories.keys()], ownershipComplete: this.worktrees.every((row) => row.activity !== "unknown"), worktrees: this.worktrees.map((row) => ({ ...row, agentIds: [...row.agentIds] })) };
5078
5122
  }
5079
5123
  getActiveCollisions() {
5080
5124
  return [...this.activeCollisions.values()];
@@ -5375,13 +5419,13 @@ ${message}`);
5375
5419
  lastScanError = message;
5376
5420
  },
5377
5421
  onCollision: (alert) => {
5378
- if (!isRaw) {
5422
+ if (!isRaw && !flags.once) {
5379
5423
  console.log(`
5380
5424
  \u26A0\uFE0F [WORKTREE OVERLAP] ${alert.message}`);
5381
5425
  }
5382
5426
  },
5383
5427
  onFileChange: (e) => {
5384
- if (!flags.quiet && !isRaw) {
5428
+ if (!flags.quiet && !isRaw && !flags.once) {
5385
5429
  console.log(`\u{1F4DD} [FILE MTIME] ${path19.basename(e.repoPath)}/${e.filePath}`);
5386
5430
  }
5387
5431
  }
@@ -5400,6 +5444,12 @@ ${message}`);
5400
5444
  \u{1F4E1} Kith Passive Radar: ${targetDir}`);
5401
5445
  console.log(` Repositories (${repos.length}): ${repos.map((r) => path19.basename(r)).join(", ")}`);
5402
5446
  console.log(` Recent Modified Files: ${edits.length}`);
5447
+ for (const row of state.worktrees) {
5448
+ const activity = { active: "Active Kith lease", retained: "Retained", unregistered: "No Kith session", unknown: "Ownership unavailable" }[row.activity];
5449
+ console.log(` ${row.label} \u2014 ${activity}; ${row.dirtyFiles ?? "unknown"} Git changes; ${row.sharedDirtyPaths} shared dirty paths`);
5450
+ console.log(` ${row.path}${row.agentIds.length ? ` (${row.agentIds.join(", ")})` : ""}
5451
+ ${row.detail}`);
5452
+ }
5403
5453
  if (!state.complete) {
5404
5454
  console.error(`Scan unavailable:
5405
5455
  ${state.errors.map((error) => error.message).join("\n")}`);
@@ -5407,10 +5457,9 @@ ${state.errors.map((error) => error.message).join("\n")}`);
5407
5457
  console.log("\nNo differing or unverified shared dirty paths observed. Identical copies are excluded; compatibility is not verified.\n");
5408
5458
  } else {
5409
5459
  console.log(`
5410
- \u26A0\uFE0F Observed worktree overlaps (${alerts.length}):`);
5460
+ \u26A0\uFE0F Review before integration: ${alerts.length} shared dirty paths:`);
5411
5461
  for (const a of alerts) {
5412
5462
  console.log(` \u26A0\uFE0F ${a.message}`);
5413
- console.log(` Files: ${a.filePath}`);
5414
5463
  }
5415
5464
  console.log("");
5416
5465
  }