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 +5 -0
- package/README.md +1 -1
- package/bin/kith-menubar +0 -0
- package/dist/cli/index.js +57 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/docs/integrations.md +2 -2
- package/docs/reliability.md +5 -1
- package/docs/roadmap.md +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
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
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.
|
|
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
|
|
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
|
}
|