@yhong91/vibetime 0.1.51 → 0.1.53

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.
Files changed (2) hide show
  1. package/bin/vibetime.mjs +1202 -291
  2. package/package.json +1 -1
package/bin/vibetime.mjs CHANGED
@@ -18,7 +18,7 @@ var __export = (target, all) => {
18
18
  // src/lib/fs.ts
19
19
  var fs_exports = {};
20
20
  __export(fs_exports, {
21
- GENERATED_MARKER: () => GENERATED_MARKER,
21
+ GENERATED_MARKER: () => GENERATED_MARKER2,
22
22
  countDirectoryEntries: () => countDirectoryEntries,
23
23
  listFilesByExtensions: () => listFilesByExtensions,
24
24
  listJsonlFiles: () => listJsonlFiles,
@@ -72,7 +72,7 @@ async function writeGeneratedFile(filePath, content, { dryRun, force, onWrite })
72
72
  onWrite(`Already installed ${filePath}`);
73
73
  return;
74
74
  }
75
- if (existing !== null && !existing.includes(GENERATED_MARKER) && !existing.includes(LEGACY_GENERATED_MARKER) && !force) {
75
+ if (existing !== null && !existing.includes(GENERATED_MARKER2) && !existing.includes(LEGACY_GENERATED_MARKER) && !force) {
76
76
  throw new Error(
77
77
  `Refusing to overwrite non-vibetime file: ${filePath}. Re-run with --force if this is intentional.`
78
78
  );
@@ -148,11 +148,11 @@ async function countDirectoryEntries(candidatePath) {
148
148
  throw error;
149
149
  }
150
150
  }
151
- var GENERATED_MARKER, LEGACY_GENERATED_MARKER;
151
+ var GENERATED_MARKER2, LEGACY_GENERATED_MARKER;
152
152
  var init_fs = __esm({
153
153
  "src/lib/fs.ts"() {
154
154
  "use strict";
155
- GENERATED_MARKER = "Generated by vibetime.";
155
+ GENERATED_MARKER2 = "Generated by vibetime.";
156
156
  LEGACY_GENERATED_MARKER = "Generated by codetime.";
157
157
  }
158
158
  });
@@ -886,7 +886,7 @@ var init_esm = __esm({
886
886
  import { spawn as spawn2, spawnSync } from "node:child_process";
887
887
  import { mkdir as mkdir5, open, rm, stat as stat13, writeFile as writeFile4 } from "node:fs/promises";
888
888
  import os11 from "node:os";
889
- import path24 from "node:path";
889
+ import path25 from "node:path";
890
890
  import { fileURLToPath } from "node:url";
891
891
 
892
892
  // ../shared/src/index.ts
@@ -924,7 +924,7 @@ var TELEMETRY_EVENT_TYPES = [
924
924
  "agent.operation"
925
925
  ];
926
926
  var FILE_ACTIVITY_OPERATIONS = ["read", "search", "create", "write", "edit", "delete"];
927
- var BACKFILL_SOURCE_IDS = ["codex", "claude-code", "claude-cowork", "copilot", "opencode", "pi", "agy", "codebuddy", "qoder", "qoder-cn", "workbuddy", "zcode", "grok-build", "zed"];
927
+ var BACKFILL_SOURCE_IDS = ["codex", "claude-code", "claude-cowork", "copilot", "opencode", "pi", "agy", "codebuddy", "qoder", "qoder-cn", "workbuddy", "zcode", "grok-build", "zed", "kimi-code"];
928
928
  function createWorkspaceId(input) {
929
929
  const basis = input.repoUrl || input.repoRoot || input.projectName || "unknown";
930
930
  return `workspace_${fnv1a(basis)}`;
@@ -2047,7 +2047,8 @@ function claudeStyleFileMetrics(tool, input) {
2047
2047
  }
2048
2048
 
2049
2049
  // src/lib/constants.ts
2050
- var PACKAGE_VERSION = true ? "0.1.51" : "0.1.1";
2050
+ var PACKAGE_VERSION = true ? "0.1.53" : "0.1.1";
2051
+ var GENERATED_MARKER = "Generated by vibetime.";
2051
2052
  var DEFAULT_API_URL = "http://121.196.224.82:3001";
2052
2053
  var DEFAULT_BACKFILL_BATCH_SIZE = 50;
2053
2054
  var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
@@ -6935,120 +6936,761 @@ function createGrokBuildAdapter() {
6935
6936
  };
6936
6937
  }
6937
6938
 
6938
- // src/adapters/opencode.ts
6939
- import os6 from "node:os";
6939
+ // src/adapters/kimi-code.ts
6940
+ import { readFile as readFile9 } from "node:fs/promises";
6940
6941
  import path14 from "node:path";
6941
- async function parseOpenCodeSessionFile(dbPath, options) {
6942
- const { DatabaseSync } = await import("node:sqlite");
6943
- if (!dbPath.endsWith(".db")) {
6944
- return [];
6942
+
6943
+ // src/lib/toml-hooks.ts
6944
+ init_fs();
6945
+ async function hasTomlHookCommand(filePath, command) {
6946
+ const text = await readTextIfExists(filePath);
6947
+ if (!text) {
6948
+ return false;
6945
6949
  }
6946
- const db = new DatabaseSync(dbPath, { readOnly: true });
6947
- const events = [];
6950
+ return parseTomlHookRules(text).some((rule) => rule.command === command);
6951
+ }
6952
+ function parseTomlHookRules(text) {
6953
+ const rules = [];
6954
+ const chunks = text.split(/^\[\[hooks\]\][ \t]*\r?\n?/m);
6955
+ for (const chunk of chunks.slice(1)) {
6956
+ const body = chunk.split(/^\[[^\]]+\]/m)[0] ?? "";
6957
+ const event = tomlStringField(body, "event");
6958
+ const command = tomlStringField(body, "command");
6959
+ if (!event || !command) {
6960
+ continue;
6961
+ }
6962
+ const matcher = tomlStringField(body, "matcher");
6963
+ const timeout = tomlNumberField(body, "timeout");
6964
+ const rule = { event, command };
6965
+ if (matcher !== void 0) {
6966
+ rule.matcher = matcher;
6967
+ }
6968
+ if (timeout !== void 0) {
6969
+ rule.timeout = timeout;
6970
+ }
6971
+ rules.push(rule);
6972
+ }
6973
+ return rules;
6974
+ }
6975
+ function mergeTomlHookRules(existingText, desired) {
6976
+ const existingKeys = new Set(parseTomlHookRules(existingText).map(ruleKey));
6977
+ const toAppend = desired.filter((rule) => !existingKeys.has(ruleKey(rule)));
6978
+ if (toAppend.length === 0) {
6979
+ return existingText;
6980
+ }
6981
+ let base = existingText;
6982
+ if (base.length > 0 && !base.endsWith("\n")) {
6983
+ base += "\n";
6984
+ }
6985
+ if (base.length > 0 && !base.endsWith("\n\n") && base.trim().length > 0) {
6986
+ base += "\n";
6987
+ }
6988
+ const blocks = toAppend.map(serializeTomlHookRule).join("\n\n");
6989
+ return `${base}${blocks}
6990
+ `;
6991
+ }
6992
+ function removeTomlHookRulesByCommand(existingText, commands) {
6993
+ const commandSet = new Set(commands.filter(Boolean));
6994
+ if (commandSet.size === 0 || !existingText) {
6995
+ return existingText;
6996
+ }
6997
+ const parts = [];
6998
+ let cursor = 0;
6999
+ const headerRe = /^\[\[hooks\]\][ \t]*\r?\n?/gm;
7000
+ let match = headerRe.exec(existingText);
7001
+ let removed = 0;
7002
+ while (match) {
7003
+ const headerStart = match.index;
7004
+ const bodyStart = headerRe.lastIndex;
7005
+ const rest = existingText.slice(bodyStart);
7006
+ const nextHeader = rest.search(/^\[[^\]]+\]/m);
7007
+ const bodyEnd = nextHeader === -1 ? existingText.length : bodyStart + nextHeader;
7008
+ const body = existingText.slice(bodyStart, bodyEnd);
7009
+ const command = tomlStringField(body, "command");
7010
+ parts.push(existingText.slice(cursor, headerStart));
7011
+ if (command && commandSet.has(command)) {
7012
+ removed += 1;
7013
+ } else {
7014
+ parts.push(existingText.slice(headerStart, bodyEnd));
7015
+ }
7016
+ cursor = bodyEnd;
7017
+ match = headerRe.exec(existingText);
7018
+ }
7019
+ if (removed === 0) {
7020
+ return existingText;
7021
+ }
7022
+ parts.push(existingText.slice(cursor));
7023
+ let next = parts.join("");
7024
+ next = next.replace(/\n{3,}/g, "\n\n");
7025
+ if (next.length > 0 && !next.endsWith("\n")) {
7026
+ next += "\n";
7027
+ }
7028
+ return next;
7029
+ }
7030
+ function looksLikeToml(text) {
7031
+ const sample = text.trim();
7032
+ if (!sample) {
7033
+ return true;
7034
+ }
7035
+ if (sample.startsWith("{")) {
7036
+ return false;
7037
+ }
7038
+ if (/^\s*\[\s*\{/.test(sample)) {
7039
+ return false;
7040
+ }
7041
+ return true;
7042
+ }
7043
+ function ruleKey(rule) {
7044
+ return `${rule.event}\0${rule.command}`;
7045
+ }
7046
+ function serializeTomlHookRule(rule) {
7047
+ const lines = [
7048
+ "[[hooks]]",
7049
+ `event = "${escapeTomlString(rule.event)}"`
7050
+ ];
7051
+ if (rule.matcher !== void 0 && rule.matcher !== "") {
7052
+ lines.push(`matcher = "${escapeTomlString(rule.matcher)}"`);
7053
+ }
7054
+ lines.push(`command = "${escapeTomlString(rule.command)}"`);
7055
+ if (rule.timeout !== void 0) {
7056
+ lines.push(`timeout = ${Math.floor(rule.timeout)}`);
7057
+ }
7058
+ return lines.join("\n");
7059
+ }
7060
+ function escapeTomlString(value) {
7061
+ return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n").replaceAll(" ", "\\t");
7062
+ }
7063
+ function tomlStringField(body, key) {
7064
+ const re = new RegExp(
7065
+ `^\\s*${key}\\s*=\\s*(?:"((?:\\\\.|[^"\\\\])*)"|'([^']*)')\\s*$`,
7066
+ "m"
7067
+ );
7068
+ const match = body.match(re);
7069
+ if (!match) {
7070
+ return void 0;
7071
+ }
7072
+ if (match[1] !== void 0) {
7073
+ return match[1].replaceAll("\\n", "\n").replaceAll("\\t", " ").replaceAll('\\"', '"').replaceAll("\\\\", "\\");
7074
+ }
7075
+ return match[2];
7076
+ }
7077
+ function tomlNumberField(body, key) {
7078
+ const re = new RegExp(`^\\s*${key}\\s*=\\s*(-?\\d+)\\s*$`, "m");
7079
+ const match = body.match(re);
7080
+ if (!match?.[1]) {
7081
+ return void 0;
7082
+ }
7083
+ const n = Number.parseInt(match[1], 10);
7084
+ return Number.isFinite(n) ? n : void 0;
7085
+ }
7086
+
7087
+ // src/adapters/kimi-code.ts
7088
+ var HOOK_COMMAND2 = "vibetime hook --agent kimi-code";
7089
+ var HOOK_TIMEOUT_SECONDS = 10;
7090
+ function kimiCodeHome(home, env) {
7091
+ const override = env?.KIMI_CODE_HOME || env?.KIMI_HOME;
7092
+ if (override && override.trim()) {
7093
+ return path14.resolve(override);
7094
+ }
7095
+ return path14.join(home, ".kimi-code");
7096
+ }
7097
+ function kimiCodeSessionsDir(home, env) {
7098
+ return path14.join(kimiCodeHome(home, env), "sessions");
7099
+ }
7100
+ function kimiCodeConfigPath(home, env) {
7101
+ return path14.join(kimiCodeHome(home, env), "config.toml");
7102
+ }
7103
+ function kimiHookRules() {
7104
+ const events = [
7105
+ "SessionStart",
7106
+ "SessionEnd",
7107
+ "UserPromptSubmit",
7108
+ "TurnStarted",
7109
+ "PostToolUse",
7110
+ "PostToolUseFailure",
7111
+ "Stop",
7112
+ "StopFailure",
7113
+ "PermissionResult",
7114
+ "SubagentStop"
7115
+ ];
7116
+ return events.map((event) => ({
7117
+ event,
7118
+ command: HOOK_COMMAND2,
7119
+ timeout: HOOK_TIMEOUT_SECONDS
7120
+ }));
7121
+ }
7122
+ function baseKimiEvent(event) {
7123
+ return {
7124
+ schemaVersion: AGENT_TIME_SCHEMA_VERSION,
7125
+ source: "kimi-code",
7126
+ agent: "kimi-code",
7127
+ workspaceId: createWorkspaceId({ projectName: event.project, repoRoot: event.cwd }),
7128
+ ...event
7129
+ };
7130
+ }
7131
+ function extractTextParts(input) {
7132
+ if (typeof input === "string") {
7133
+ return input;
7134
+ }
7135
+ if (!Array.isArray(input)) {
7136
+ return "";
7137
+ }
7138
+ return input.filter((item) => isPlainObject(item) && item.type === "text").map((item) => stringField(item, "text") || "").join("");
7139
+ }
7140
+ function kimiUsageFromRecord(usage) {
7141
+ const inputOther = numberField(usage, "inputOther") || 0;
7142
+ const output = numberField(usage, "output") || 0;
7143
+ const cacheRead = numberField(usage, "inputCacheRead") || 0;
7144
+ const cacheWrite = numberField(usage, "inputCacheCreation") || 0;
7145
+ const inputTokens = numberField(usage, "input_tokens") || numberField(usage, "input") || 0;
7146
+ const outputTokens = numberField(usage, "output_tokens") || 0;
7147
+ const nonCached = inputOther || inputTokens;
7148
+ const out = output || outputTokens;
7149
+ const total = nonCached + out + cacheRead + cacheWrite;
7150
+ if (total <= 0) {
7151
+ return void 0;
7152
+ }
7153
+ return {
7154
+ tokensInput: nonCached + cacheRead + cacheWrite || void 0,
7155
+ tokensOutput: out || void 0,
7156
+ tokensCacheReadInput: cacheRead || void 0,
7157
+ tokensCacheCreationInput: cacheWrite || void 0,
7158
+ tokensCachedInput: cacheRead + cacheWrite || void 0,
7159
+ tokensTotal: total,
7160
+ modelCalls: 1
7161
+ };
7162
+ }
7163
+ function normalizeTurnId(raw) {
7164
+ if (typeof raw === "number" && Number.isFinite(raw)) {
7165
+ return `turn_${raw}`;
7166
+ }
7167
+ if (typeof raw === "string" && raw.trim()) {
7168
+ return raw.startsWith("turn_") ? raw : `turn_${raw}`;
7169
+ }
7170
+ return void 0;
7171
+ }
7172
+ function sessionIdFromWirePath(filePath) {
7173
+ const normalized = filePath.replaceAll("\\", "/");
7174
+ const match = normalized.match(/\/(session_[^/]+)\/agents\/[^/]+\/wire\.jsonl$/);
7175
+ return match?.[1];
7176
+ }
7177
+ async function readSessionState(filePath) {
7178
+ const sessionDir = path14.dirname(path14.dirname(path14.dirname(filePath)));
7179
+ const statePath = path14.join(sessionDir, "state.json");
6948
7180
  try {
6949
- const sessionCols = new Set(
6950
- db.prepare("PRAGMA table_info(session)").all().map((row) => row.name)
6951
- );
6952
- const hasDirectory = sessionCols.has("directory");
6953
- const hasPath = sessionCols.has("path");
6954
- const hasArchived = sessionCols.has("time_archived");
6955
- const hasParentId = sessionCols.has("parent_id");
6956
- const selectCols = ["id", "title", "time_created"];
6957
- if (hasDirectory) {
6958
- selectCols.push("directory");
7181
+ const text = await readFile9(statePath, "utf8");
7182
+ const raw = JSON.parse(text);
7183
+ if (!isPlainObject(raw)) {
7184
+ return {};
6959
7185
  }
6960
- if (hasPath) {
6961
- selectCols.push("path");
7186
+ return {
7187
+ sessionId: stringField(raw, "id"),
7188
+ cwd: stringField(raw, "cwd"),
7189
+ title: stringField(raw, "title")
7190
+ };
7191
+ } catch {
7192
+ return {};
7193
+ }
7194
+ }
7195
+ async function parseKimiCodeSessionFile(filePath, options) {
7196
+ if (path14.basename(filePath) !== "wire.jsonl") {
7197
+ return [];
7198
+ }
7199
+ const text = await readFile9(filePath, "utf8");
7200
+ const lines = text.split("\n").filter(Boolean);
7201
+ const stateMeta = await readSessionState(filePath);
7202
+ let sessionId = stateMeta.sessionId || sessionIdFromWirePath(filePath);
7203
+ let cwd = stateMeta.cwd;
7204
+ let project = cwd ? path14.basename(cwd) : void 0;
7205
+ let model;
7206
+ let provider;
7207
+ let reasoningEffort;
7208
+ const pendingToolCalls = /* @__PURE__ */ new Map();
7209
+ const pendingPermissions = /* @__PURE__ */ new Map();
7210
+ let sawUsageRecord = false;
7211
+ const state = new SessionParserState(filePath, options, (event) => baseKimiEvent({ ...event, cwd, project, model, provider }));
7212
+ if (sessionId) {
7213
+ state.sessionId = sessionId;
7214
+ }
7215
+ const push = (event, ln, topType) => {
7216
+ state.push(
7217
+ {
7218
+ ...event,
7219
+ sessionId: event.sessionId || sessionId,
7220
+ workspaceId: event.workspaceId || createWorkspaceId({ projectName: project, repoRoot: cwd })
7221
+ },
7222
+ ln,
7223
+ topType || event.type,
7224
+ event.type
7225
+ );
7226
+ };
7227
+ for (const [index, line] of lines.entries()) {
7228
+ const lineNumber = index + 1;
7229
+ const raw = parseJsonLine(line);
7230
+ if (!raw) {
7231
+ continue;
6962
7232
  }
6963
- if (hasArchived) {
6964
- selectCols.push("time_archived");
7233
+ const entryType = stringField(raw, "type");
7234
+ const ts = timestampFrom(raw.time) || timestampFrom(raw.created_at) || timestampFrom(raw.timestamp);
7235
+ if (!ts || !entryType) {
7236
+ continue;
6965
7237
  }
6966
- if (hasParentId) {
6967
- selectCols.push("parent_id");
7238
+ if (entryType === "metadata") {
7239
+ state.ensureSessionStarted(ts, lineNumber, entryType);
7240
+ continue;
6968
7241
  }
6969
- const sessions = db.prepare(
6970
- `SELECT ${selectCols.join(", ")} FROM session WHERE time_created IS NOT NULL ORDER BY time_created`
6971
- ).all();
6972
- const rootIdByRawId = /* @__PURE__ */ new Map();
6973
- if (hasParentId) {
6974
- const byId = new Map(sessions.map((s) => [s.id, s]));
6975
- for (const session of sessions) {
6976
- let current = session.id;
6977
- const visited = /* @__PURE__ */ new Set();
6978
- while (true) {
6979
- if (visited.has(current)) {
6980
- break;
6981
- }
6982
- visited.add(current);
6983
- const node = byId.get(current);
6984
- const parentId = node?.parent_id || void 0;
6985
- if (!parentId || !byId.has(parentId)) {
6986
- break;
6987
- }
6988
- current = parentId;
6989
- }
6990
- rootIdByRawId.set(session.id, current);
7242
+ if (entryType === "profile.bind") {
7243
+ state.ensureSessionStarted(ts, lineNumber, entryType);
7244
+ model = stringField(raw, "modelAlias") || stringField(raw, "model") || model;
7245
+ reasoningEffort = stringField(raw, "thinkingEffort") || reasoningEffort;
7246
+ const disclosure = objectField(raw, "environmentDisclosure");
7247
+ const disclosedCwd = stringField(disclosure, "cwd");
7248
+ if (disclosedCwd) {
7249
+ cwd = disclosedCwd;
7250
+ project = path14.basename(disclosedCwd);
6991
7251
  }
7252
+ continue;
6992
7253
  }
6993
- for (const session of sessions) {
6994
- const rawSessionId = session.id;
6995
- const sessionId = rootIdByRawId.get(rawSessionId) || rawSessionId;
6996
- const cwd = session.directory || session.path || void 0;
6997
- const project = cwd ? path14.basename(cwd) : void 0;
6998
- const sessionTs = msToIso(session.time_created);
6999
- events.push(baseOpenCodeEvent({
7000
- ts: sessionTs,
7001
- type: "session.started",
7254
+ if (entryType === "turn.prompt") {
7255
+ state.ensureSessionStarted(ts, lineNumber, entryType);
7256
+ if (isTurnIdle(state.currentTurnLastEventAt) || state.currentTurnId) {
7257
+ state.closeTurn(ts, lineNumber, entryType);
7258
+ }
7259
+ const nextOrdinal = state.currentTurnId ? Number.parseInt(state.currentTurnId.replace(/^turn_/, ""), 10) : Number.NaN;
7260
+ const turnOrdinal = Number.isFinite(nextOrdinal) ? nextOrdinal + 1 : 0;
7261
+ const turnId = `turn_${turnOrdinal}`;
7262
+ state.startTurn(turnId, ts);
7263
+ push(baseKimiEvent({
7264
+ ts,
7265
+ type: "turn.started",
7002
7266
  sessionId,
7267
+ turnId,
7003
7268
  cwd,
7004
7269
  project,
7005
- operation: "session start"
7006
- }));
7007
- const messages = db.prepare(
7008
- "SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created"
7009
- ).all(rawSessionId);
7010
- let currentTurnId;
7011
- let currentProvider;
7012
- let turnTs;
7013
- let reasoningEffort;
7014
- const modelSwitchedEvents = db.prepare(
7015
- "SELECT data FROM event WHERE aggregate_id = ? AND type = 'session.next.model.switched.1' ORDER BY seq"
7016
- ).all(rawSessionId);
7017
- for (const evt of modelSwitchedEvents) {
7018
- try {
7019
- const evtData = JSON.parse(evt.data);
7020
- const variant = isPlainObject(evtData) ? stringField(evtData.model || evtData, "variant") : void 0;
7021
- if (variant && variant !== "default") {
7022
- reasoningEffort = variant;
7023
- }
7024
- } catch {
7270
+ model,
7271
+ provider,
7272
+ confidence: "exact"
7273
+ }), lineNumber, entryType);
7274
+ const promptText = extractTextParts(raw.input);
7275
+ push(baseKimiEvent({
7276
+ ts,
7277
+ type: "prompt.submitted",
7278
+ sessionId,
7279
+ turnId,
7280
+ cwd,
7281
+ project,
7282
+ model,
7283
+ provider,
7284
+ confidence: "exact",
7285
+ metrics: {
7286
+ prompts: 1,
7287
+ promptChars: promptText.length || void 0
7288
+ },
7289
+ refs: stringRefs({
7290
+ promptHash: promptText ? `sha256:${createStableHash(promptText)}` : void 0
7291
+ })
7292
+ }), lineNumber, entryType);
7293
+ continue;
7294
+ }
7295
+ if (entryType === "llm.request") {
7296
+ state.ensureSessionStarted(ts, lineNumber, entryType);
7297
+ model = stringField(raw, "modelAlias") || stringField(raw, "model") || model;
7298
+ provider = stringField(raw, "provider") || provider;
7299
+ reasoningEffort = stringField(raw, "thinkingEffort") || reasoningEffort;
7300
+ const turnStep = stringField(raw, "turnStep");
7301
+ if (turnStep && !state.currentTurnId) {
7302
+ const turnPart = turnStep.split(".")[0];
7303
+ const turnId = normalizeTurnId(turnPart);
7304
+ if (turnId) {
7305
+ state.startTurn(turnId, ts);
7025
7306
  }
7026
7307
  }
7027
- for (const msg of messages) {
7028
- let info;
7029
- try {
7030
- info = JSON.parse(msg.data);
7031
- } catch {
7032
- continue;
7308
+ continue;
7309
+ }
7310
+ if (entryType === "usage.record") {
7311
+ state.ensureSessionStarted(ts, lineNumber, entryType);
7312
+ sawUsageRecord = true;
7313
+ model = stringField(raw, "model") || model;
7314
+ const usage = objectField(raw, "usage");
7315
+ const metrics = kimiUsageFromRecord(usage);
7316
+ if (metrics) {
7317
+ if (reasoningEffort) {
7318
+ metrics.reasoningEffort = reasoningEffort;
7033
7319
  }
7034
- if (!isPlainObject(info)) {
7035
- continue;
7320
+ push(baseKimiEvent({
7321
+ ts,
7322
+ type: "model.usage",
7323
+ sessionId,
7324
+ turnId: state.currentTurnId,
7325
+ cwd,
7326
+ project,
7327
+ model,
7328
+ provider,
7329
+ confidence: "exact",
7330
+ metrics
7331
+ }), lineNumber, entryType);
7332
+ }
7333
+ continue;
7334
+ }
7335
+ if (entryType === "context.append_loop_event") {
7336
+ state.ensureSessionStarted(ts, lineNumber, entryType);
7337
+ const loopEvent = objectField(raw, "event");
7338
+ const loopType = stringField(loopEvent, "type");
7339
+ const turnId = normalizeTurnId(loopEvent.turnId) || state.currentTurnId;
7340
+ if (turnId && turnId !== state.currentTurnId) {
7341
+ if (state.currentTurnId) {
7342
+ state.closeTurn(ts, lineNumber, entryType);
7036
7343
  }
7037
- const role = stringField(info, "role");
7038
- const timeObj = objectField(info, "time");
7039
- const timeCreated = numberField(timeObj, "created");
7040
- if (!role || !timeCreated) {
7041
- continue;
7344
+ state.startTurn(turnId, ts);
7345
+ }
7346
+ if (loopType === "tool.call") {
7347
+ const toolCallId = stringField(loopEvent, "toolCallId") || stringField(loopEvent, "uuid");
7348
+ const toolName = stringField(loopEvent, "name") || "tool";
7349
+ const toolInput = isPlainObject(loopEvent.args) ? loopEvent.args : {};
7350
+ if (toolCallId) {
7351
+ pendingToolCalls.set(toolCallId, {
7352
+ toolName,
7353
+ startedAt: ts,
7354
+ turnId: turnId || state.currentTurnId,
7355
+ input: toolInput
7356
+ });
7042
7357
  }
7043
- if (role === "user") {
7044
- currentTurnId = `turn_${createStableHash([sessionId, msg.id]).slice(0, 24)}`;
7045
- turnTs = msToIso(timeCreated);
7046
- const userAgent = stringField(info, "agent") || "opencode";
7047
- const modelObj = objectField(info, "model");
7048
- const model = modelObj ? stringField(modelObj, "modelID") : void 0;
7049
- currentProvider = modelObj ? stringField(modelObj, "providerID") : void 0;
7050
- events.push(baseOpenCodeEvent({
7051
- ts: turnTs,
7358
+ push(baseKimiEvent({
7359
+ ts,
7360
+ type: "tool.started",
7361
+ operation: `${toolName} started`,
7362
+ sessionId,
7363
+ turnId: turnId || state.currentTurnId,
7364
+ cwd,
7365
+ project,
7366
+ model,
7367
+ provider,
7368
+ tool: toolName,
7369
+ confidence: "exact",
7370
+ metrics: { toolCalls: 1 },
7371
+ refs: stringRefs({
7372
+ sourceId: toolCallId,
7373
+ commandHash: toolName.toLowerCase() === "bash" && stringField(toolInput, "command") ? createStableHash(stringField(toolInput, "command")) : void 0
7374
+ })
7375
+ }), lineNumber, entryType);
7376
+ const fileActivities = claudeStyleToolFileActivities(
7377
+ toolName,
7378
+ toolInput,
7379
+ ts,
7380
+ cwd
7381
+ );
7382
+ if (fileActivities.length > 0) {
7383
+ push(baseKimiEvent({
7384
+ ts,
7385
+ type: eventTypeFromFileActivities(fileActivities),
7386
+ operation: `${toolName} file activity`,
7387
+ sessionId,
7388
+ turnId: turnId || state.currentTurnId,
7389
+ cwd,
7390
+ project,
7391
+ model,
7392
+ provider,
7393
+ tool: toolName,
7394
+ confidence: "derived",
7395
+ fileActivities,
7396
+ metrics: summarizeFileActivities(fileActivities),
7397
+ refs: stringRefs({ sourceId: toolCallId })
7398
+ }), lineNumber, entryType);
7399
+ }
7400
+ continue;
7401
+ }
7402
+ if (loopType === "tool.result") {
7403
+ const toolCallId = stringField(loopEvent, "toolCallId");
7404
+ const pending = toolCallId ? pendingToolCalls.get(toolCallId) : void 0;
7405
+ if (toolCallId) {
7406
+ pendingToolCalls.delete(toolCallId);
7407
+ }
7408
+ const result = objectField(loopEvent, "result");
7409
+ const isError = Boolean(result.isError);
7410
+ const toolName = pending?.toolName || "tool";
7411
+ const durationMs = pending ? durationMsBetween(pending.startedAt, ts) : void 0;
7412
+ push(baseKimiEvent({
7413
+ ts,
7414
+ type: isError ? "tool.failed" : "tool.completed",
7415
+ operation: isError ? `${toolName} failed` : `${toolName} completed`,
7416
+ sessionId,
7417
+ turnId: pending?.turnId || turnId || state.currentTurnId,
7418
+ cwd,
7419
+ project,
7420
+ model,
7421
+ provider,
7422
+ tool: toolName,
7423
+ success: !isError,
7424
+ confidence: "exact",
7425
+ metrics: {
7426
+ toolDurationMs: durationMs,
7427
+ durationMs
7428
+ },
7429
+ refs: stringRefs({ sourceId: toolCallId })
7430
+ }), lineNumber, entryType);
7431
+ if (toolName.toLowerCase() === "bash") {
7432
+ push(baseKimiEvent({
7433
+ ts,
7434
+ type: isError ? "command.failed" : "command.completed",
7435
+ operation: "command completed",
7436
+ sessionId,
7437
+ turnId: pending?.turnId || turnId || state.currentTurnId,
7438
+ cwd,
7439
+ project,
7440
+ model,
7441
+ provider,
7442
+ tool: "Bash",
7443
+ success: !isError,
7444
+ confidence: "derived",
7445
+ metrics: {
7446
+ commandCalls: 1,
7447
+ commandDurationMs: durationMs,
7448
+ durationMs
7449
+ },
7450
+ refs: stringRefs({
7451
+ sourceId: toolCallId,
7452
+ commandHash: pending?.input.command ? `sha256:${createStableHash(String(pending.input.command))}` : void 0
7453
+ })
7454
+ }), lineNumber, entryType);
7455
+ }
7456
+ continue;
7457
+ }
7458
+ if (loopType === "step.end") {
7459
+ if (!sawUsageRecord) {
7460
+ const streamMs = numberField(loopEvent, "llmStreamDurationMs") || numberField(loopEvent, "llmServerDecodeMs");
7461
+ const usage = objectField(loopEvent, "usage");
7462
+ const metrics = kimiUsageFromRecord(usage);
7463
+ if (metrics) {
7464
+ if (streamMs) {
7465
+ metrics.modelDurationMs = streamMs;
7466
+ }
7467
+ if (reasoningEffort) {
7468
+ metrics.reasoningEffort = reasoningEffort;
7469
+ }
7470
+ push(baseKimiEvent({
7471
+ ts,
7472
+ type: "model.usage",
7473
+ sessionId,
7474
+ turnId: turnId || state.currentTurnId,
7475
+ cwd,
7476
+ project,
7477
+ model,
7478
+ provider,
7479
+ confidence: "exact",
7480
+ metrics
7481
+ }), lineNumber, entryType);
7482
+ }
7483
+ }
7484
+ continue;
7485
+ }
7486
+ continue;
7487
+ }
7488
+ if (entryType === "interaction.request") {
7489
+ state.ensureSessionStarted(ts, lineNumber, entryType);
7490
+ const requestId = stringField(raw, "id") || stringField(raw, "toolCallId");
7491
+ const request2 = objectField(raw, "request");
7492
+ const toolName = stringField(request2, "toolName") || stringField(raw, "toolName");
7493
+ const turnId = normalizeTurnId(request2.turnId) || state.currentTurnId;
7494
+ if (requestId) {
7495
+ pendingPermissions.set(requestId, { toolName, startedAt: ts, turnId });
7496
+ }
7497
+ push(baseKimiEvent({
7498
+ ts,
7499
+ type: "permission.requested",
7500
+ operation: toolName ? `${toolName} permission` : "permission requested",
7501
+ sessionId,
7502
+ turnId,
7503
+ cwd,
7504
+ project,
7505
+ model,
7506
+ provider,
7507
+ tool: toolName,
7508
+ confidence: "exact",
7509
+ refs: stringRefs({ sourceId: requestId })
7510
+ }), lineNumber, entryType);
7511
+ continue;
7512
+ }
7513
+ if (entryType === "interaction.resolved") {
7514
+ state.ensureSessionStarted(ts, lineNumber, entryType);
7515
+ const requestId = stringField(raw, "id");
7516
+ const pending = requestId ? pendingPermissions.get(requestId) : void 0;
7517
+ if (requestId) {
7518
+ pendingPermissions.delete(requestId);
7519
+ }
7520
+ const response = objectField(raw, "response");
7521
+ const decision = stringField(response, "decision") || stringField(raw, "decision");
7522
+ const granted = decision === "approved" || decision === "allow" || decision === "granted";
7523
+ const denied = decision === "denied" || decision === "reject" || decision === "rejected";
7524
+ push(baseKimiEvent({
7525
+ ts,
7526
+ type: granted ? "permission.granted" : denied ? "permission.denied" : "permission.resolved",
7527
+ operation: decision ? `permission ${decision}` : "permission resolved",
7528
+ sessionId,
7529
+ turnId: pending?.turnId || state.currentTurnId,
7530
+ cwd,
7531
+ project,
7532
+ model,
7533
+ provider,
7534
+ tool: pending?.toolName,
7535
+ success: granted ? true : denied ? false : void 0,
7536
+ confidence: "exact",
7537
+ metrics: {
7538
+ durationMs: pending ? durationMsBetween(pending.startedAt, ts) : void 0,
7539
+ approvalWaitMs: pending ? durationMsBetween(pending.startedAt, ts) : void 0
7540
+ },
7541
+ refs: stringRefs({ sourceId: requestId })
7542
+ }), lineNumber, entryType);
7543
+ continue;
7544
+ }
7545
+ }
7546
+ if (isTurnIdle(state.currentTurnLastEventAt)) {
7547
+ state.closeTurn(state.currentTurnLastEventAt, lines.length);
7548
+ }
7549
+ return state.events.filter((event) => matchesBackfillFilters(event, options));
7550
+ }
7551
+ function createKimiCodeAdapter() {
7552
+ return {
7553
+ id: "kimi-code",
7554
+ label: "Kimi Code",
7555
+ agentName: "kimi-code",
7556
+ kind: "agent",
7557
+ detectPath(home, env) {
7558
+ return kimiCodeHome(home, env);
7559
+ },
7560
+ installedPath(home, env) {
7561
+ return kimiCodeConfigPath(home, env);
7562
+ },
7563
+ async isInstalled(home, env) {
7564
+ return hasTomlHookCommand(kimiCodeConfigPath(home, env), HOOK_COMMAND2);
7565
+ },
7566
+ installEntries(home, env) {
7567
+ return [{
7568
+ kind: "hooks-toml",
7569
+ path: kimiCodeConfigPath(home, env),
7570
+ content: { hooks: kimiHookRules() }
7571
+ }];
7572
+ },
7573
+ sourcePaths(home, env) {
7574
+ return [kimiCodeSessionsDir(home, env)];
7575
+ },
7576
+ parseSessionFile: parseKimiCodeSessionFile
7577
+ };
7578
+ }
7579
+
7580
+ // src/adapters/opencode.ts
7581
+ import os6 from "node:os";
7582
+ import path15 from "node:path";
7583
+ async function parseOpenCodeSessionFile(dbPath, options) {
7584
+ const { DatabaseSync } = await import("node:sqlite");
7585
+ if (!dbPath.endsWith(".db")) {
7586
+ return [];
7587
+ }
7588
+ const db = new DatabaseSync(dbPath, { readOnly: true });
7589
+ const events = [];
7590
+ try {
7591
+ const sessionCols = new Set(
7592
+ db.prepare("PRAGMA table_info(session)").all().map((row) => row.name)
7593
+ );
7594
+ const hasDirectory = sessionCols.has("directory");
7595
+ const hasPath = sessionCols.has("path");
7596
+ const hasArchived = sessionCols.has("time_archived");
7597
+ const hasParentId = sessionCols.has("parent_id");
7598
+ const selectCols = ["id", "title", "time_created"];
7599
+ if (hasDirectory) {
7600
+ selectCols.push("directory");
7601
+ }
7602
+ if (hasPath) {
7603
+ selectCols.push("path");
7604
+ }
7605
+ if (hasArchived) {
7606
+ selectCols.push("time_archived");
7607
+ }
7608
+ if (hasParentId) {
7609
+ selectCols.push("parent_id");
7610
+ }
7611
+ const sessions = db.prepare(
7612
+ `SELECT ${selectCols.join(", ")} FROM session WHERE time_created IS NOT NULL ORDER BY time_created`
7613
+ ).all();
7614
+ const rootIdByRawId = /* @__PURE__ */ new Map();
7615
+ if (hasParentId) {
7616
+ const byId = new Map(sessions.map((s) => [s.id, s]));
7617
+ for (const session of sessions) {
7618
+ let current = session.id;
7619
+ const visited = /* @__PURE__ */ new Set();
7620
+ while (true) {
7621
+ if (visited.has(current)) {
7622
+ break;
7623
+ }
7624
+ visited.add(current);
7625
+ const node = byId.get(current);
7626
+ const parentId = node?.parent_id || void 0;
7627
+ if (!parentId || !byId.has(parentId)) {
7628
+ break;
7629
+ }
7630
+ current = parentId;
7631
+ }
7632
+ rootIdByRawId.set(session.id, current);
7633
+ }
7634
+ }
7635
+ for (const session of sessions) {
7636
+ const rawSessionId = session.id;
7637
+ const sessionId = rootIdByRawId.get(rawSessionId) || rawSessionId;
7638
+ const cwd = session.directory || session.path || void 0;
7639
+ const project = cwd ? path15.basename(cwd) : void 0;
7640
+ const sessionTs = msToIso(session.time_created);
7641
+ events.push(baseOpenCodeEvent({
7642
+ ts: sessionTs,
7643
+ type: "session.started",
7644
+ sessionId,
7645
+ cwd,
7646
+ project,
7647
+ operation: "session start"
7648
+ }));
7649
+ const messages = db.prepare(
7650
+ "SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created"
7651
+ ).all(rawSessionId);
7652
+ let currentTurnId;
7653
+ let currentProvider;
7654
+ let turnTs;
7655
+ let reasoningEffort;
7656
+ const modelSwitchedEvents = db.prepare(
7657
+ "SELECT data FROM event WHERE aggregate_id = ? AND type = 'session.next.model.switched.1' ORDER BY seq"
7658
+ ).all(rawSessionId);
7659
+ for (const evt of modelSwitchedEvents) {
7660
+ try {
7661
+ const evtData = JSON.parse(evt.data);
7662
+ const variant = isPlainObject(evtData) ? stringField(evtData.model || evtData, "variant") : void 0;
7663
+ if (variant && variant !== "default") {
7664
+ reasoningEffort = variant;
7665
+ }
7666
+ } catch {
7667
+ }
7668
+ }
7669
+ for (const msg of messages) {
7670
+ let info;
7671
+ try {
7672
+ info = JSON.parse(msg.data);
7673
+ } catch {
7674
+ continue;
7675
+ }
7676
+ if (!isPlainObject(info)) {
7677
+ continue;
7678
+ }
7679
+ const role = stringField(info, "role");
7680
+ const timeObj = objectField(info, "time");
7681
+ const timeCreated = numberField(timeObj, "created");
7682
+ if (!role || !timeCreated) {
7683
+ continue;
7684
+ }
7685
+ if (role === "user") {
7686
+ currentTurnId = `turn_${createStableHash([sessionId, msg.id]).slice(0, 24)}`;
7687
+ turnTs = msToIso(timeCreated);
7688
+ const userAgent = stringField(info, "agent") || "opencode";
7689
+ const modelObj = objectField(info, "model");
7690
+ const model = modelObj ? stringField(modelObj, "modelID") : void 0;
7691
+ currentProvider = modelObj ? stringField(modelObj, "providerID") : void 0;
7692
+ events.push(baseOpenCodeEvent({
7693
+ ts: turnTs,
7052
7694
  type: "turn.started",
7053
7695
  sessionId,
7054
7696
  turnId: currentTurnId,
@@ -7102,7 +7744,7 @@ async function parseOpenCodeSessionFile(dbPath, options) {
7102
7744
  const provider = currentProvider;
7103
7745
  const pathObj = objectField(info, "path");
7104
7746
  const assistantCwd = stringField(pathObj, "cwd") || cwd;
7105
- const assistantProject = assistantCwd ? path14.basename(assistantCwd) : project;
7747
+ const assistantProject = assistantCwd ? path15.basename(assistantCwd) : project;
7106
7748
  const completedTs = numberField(objectField(info, "time"), "completed");
7107
7749
  const createdTs = timeCreated;
7108
7750
  const tokens = opencodeUsageFromInfo(info);
@@ -7373,18 +8015,18 @@ function opencodeUsageFromInfo(info) {
7373
8015
  function opencodeConfigDir(home, env) {
7374
8016
  const override = env?.OPENCODE_CONFIG_DIR;
7375
8017
  if (override && override.trim()) {
7376
- return path14.resolve(override);
8018
+ return path15.resolve(override);
7377
8019
  }
7378
8020
  const xdgConfig = env?.XDG_CONFIG_HOME;
7379
8021
  if (xdgConfig && xdgConfig.trim()) {
7380
- return path14.join(path14.resolve(xdgConfig), "opencode");
8022
+ return path15.join(path15.resolve(xdgConfig), "opencode");
7381
8023
  }
7382
- return path14.join(home, ".config", "opencode");
8024
+ return path15.join(home, ".config", "opencode");
7383
8025
  }
7384
8026
  function opencodeDataCandidates(home, env) {
7385
8027
  const xdgData = env?.XDG_DATA_HOME;
7386
- const primary = xdgData && xdgData.trim() ? path14.join(path14.resolve(xdgData), "opencode", "opencode.db") : path14.join(home, ".local", "share", "opencode", "opencode.db");
7387
- return [primary, path14.join(home, ".opencode", "opencode.db")];
8028
+ const primary = xdgData && xdgData.trim() ? path15.join(path15.resolve(xdgData), "opencode", "opencode.db") : path15.join(home, ".local", "share", "opencode", "opencode.db");
8029
+ return [primary, path15.join(home, ".opencode", "opencode.db")];
7388
8030
  }
7389
8031
  async function opencodeBackfillFiles(sourceRoot, home = os6.homedir(), env) {
7390
8032
  const { stat: stat14 } = await import("node:fs/promises");
@@ -7474,12 +8116,12 @@ function createOpenCodeAdapter() {
7474
8116
  return opencodeConfigDir(home, env);
7475
8117
  },
7476
8118
  installedPath(home, env) {
7477
- return path14.join(opencodeConfigDir(home, env), PLUGIN_PATH);
8119
+ return path15.join(opencodeConfigDir(home, env), PLUGIN_PATH);
7478
8120
  },
7479
8121
  async isInstalled(home, env) {
7480
8122
  try {
7481
8123
  const { pathExists: pathExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
7482
- return await pathExists2(path14.join(opencodeConfigDir(home, env), PLUGIN_PATH)) || await pathExists2(path14.join(".opencode", PLUGIN_PATH));
8124
+ return await pathExists2(path15.join(opencodeConfigDir(home, env), PLUGIN_PATH)) || await pathExists2(path15.join(".opencode", PLUGIN_PATH));
7483
8125
  } catch {
7484
8126
  return false;
7485
8127
  }
@@ -7487,7 +8129,7 @@ function createOpenCodeAdapter() {
7487
8129
  installEntries(home, env) {
7488
8130
  return [{
7489
8131
  kind: "file",
7490
- path: path14.join(opencodeConfigDir(home, env), PLUGIN_PATH),
8132
+ path: path15.join(opencodeConfigDir(home, env), PLUGIN_PATH),
7491
8133
  content: opencodePluginContent()
7492
8134
  }];
7493
8135
  },
@@ -7499,12 +8141,12 @@ function createOpenCodeAdapter() {
7499
8141
  }
7500
8142
 
7501
8143
  // src/adapters/pi.ts
7502
- import { readFile as readFile9 } from "node:fs/promises";
7503
- import path15 from "node:path";
8144
+ import { readFile as readFile10 } from "node:fs/promises";
8145
+ import path16 from "node:path";
7504
8146
  function parsePiSubagentLink(filePath, headerParentSession) {
7505
8147
  if (headerParentSession) {
7506
- const parentFile = path15.isAbsolute(headerParentSession) ? headerParentSession : void 0;
7507
- const parentSessionId2 = parentFile ? path15.basename(parentFile, ".jsonl") : headerParentSession;
8148
+ const parentFile = path16.isAbsolute(headerParentSession) ? headerParentSession : void 0;
8149
+ const parentSessionId2 = parentFile ? path16.basename(parentFile, ".jsonl") : headerParentSession;
7508
8150
  return { parentSessionId: parentSessionId2, parentSessionFile: parentFile, explicit: true };
7509
8151
  }
7510
8152
  const normalized = filePath.replaceAll("\\", "/");
@@ -7517,20 +8159,20 @@ function parsePiSubagentLink(filePath, headerParentSession) {
7517
8159
  return void 0;
7518
8160
  }
7519
8161
  const parentSessionId = parentBasename.endsWith(".jsonl") ? parentBasename.slice(0, -".jsonl".length) : parentBasename;
7520
- const parentDir = path15.dirname(path15.dirname(path15.dirname(filePath)));
7521
- const parentSessionFile = path15.join(path15.dirname(parentDir), `${parentBasename}.jsonl`);
8162
+ const parentDir = path16.dirname(path16.dirname(path16.dirname(filePath)));
8163
+ const parentSessionFile = path16.join(path16.dirname(parentDir), `${parentBasename}.jsonl`);
7522
8164
  return { parentSessionId, parentSessionFile, explicit: false };
7523
8165
  }
7524
8166
  async function resolveParentContext(link, options) {
7525
8167
  if (link.parentSessionFile) {
7526
8168
  try {
7527
- const text = await readFile9(link.parentSessionFile, "utf8");
8169
+ const text = await readFile10(link.parentSessionFile, "utf8");
7528
8170
  const firstLine = text.split("\n").find((line) => line.trim().length > 0);
7529
8171
  const raw = firstLine ? parseJsonLine(firstLine) : void 0;
7530
8172
  if (raw) {
7531
8173
  const id = stringField(raw, "id");
7532
8174
  const cwd = stringField(raw, "cwd");
7533
- const project = cwd ? path15.basename(cwd) : void 0;
8175
+ const project = cwd ? path16.basename(cwd) : void 0;
7534
8176
  if (id || cwd || project) {
7535
8177
  return { sessionId: id, cwd, project };
7536
8178
  }
@@ -7575,7 +8217,7 @@ function rebuildEventIdentity2(event) {
7575
8217
  };
7576
8218
  }
7577
8219
  async function parsePiSessionFile(filePath, options) {
7578
- const text = await readFile9(filePath, "utf8");
8220
+ const text = await readFile10(filePath, "utf8");
7579
8221
  const lines = text.split("\n").filter(Boolean);
7580
8222
  let sessionId;
7581
8223
  let cwd;
@@ -7607,7 +8249,7 @@ async function parsePiSessionFile(filePath, options) {
7607
8249
  sessionId = stringField(raw, "id") || state.sessionId;
7608
8250
  state.sessionId = sessionId || state.sessionId;
7609
8251
  cwd = stringField(raw, "cwd") || cwd;
7610
- project = cwd ? path15.basename(cwd) : project;
8252
+ project = cwd ? path16.basename(cwd) : project;
7611
8253
  headerParentSession = stringField(raw, "parentSession") || headerParentSession;
7612
8254
  continue;
7613
8255
  }
@@ -8040,16 +8682,16 @@ export default function (pi: ExtensionAPI) {
8040
8682
  function piAgentDir(home, env) {
8041
8683
  const override = env?.PI_CODING_AGENT_DIR;
8042
8684
  if (override && override.trim()) {
8043
- return path15.resolve(override);
8685
+ return path16.resolve(override);
8044
8686
  }
8045
- return path15.join(home, ".pi", "agent");
8687
+ return path16.join(home, ".pi", "agent");
8046
8688
  }
8047
8689
  function piSessionDir(home, env) {
8048
8690
  const override = env?.PI_CODING_AGENT_SESSION_DIR;
8049
8691
  if (override && override.trim()) {
8050
- return path15.resolve(override);
8692
+ return path16.resolve(override);
8051
8693
  }
8052
- return path15.join(piAgentDir(home, env), "sessions");
8694
+ return path16.join(piAgentDir(home, env), "sessions");
8053
8695
  }
8054
8696
  function createPiAdapter() {
8055
8697
  return {
@@ -8061,12 +8703,12 @@ function createPiAdapter() {
8061
8703
  return piAgentDir(home, env);
8062
8704
  },
8063
8705
  installedPath(home, env) {
8064
- return path15.join(piAgentDir(home, env), "extensions", "vibetime.ts");
8706
+ return path16.join(piAgentDir(home, env), "extensions", "vibetime.ts");
8065
8707
  },
8066
8708
  async isInstalled(home, env) {
8067
8709
  try {
8068
8710
  const { pathExists: pathExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
8069
- return await pathExists2(path15.join(piAgentDir(home, env), "extensions", "vibetime.ts"));
8711
+ return await pathExists2(path16.join(piAgentDir(home, env), "extensions", "vibetime.ts"));
8070
8712
  } catch {
8071
8713
  return false;
8072
8714
  }
@@ -8074,7 +8716,7 @@ function createPiAdapter() {
8074
8716
  installEntries(home, env) {
8075
8717
  return [{
8076
8718
  kind: "file",
8077
- path: path15.join(piAgentDir(home, env), "extensions", "vibetime.ts"),
8719
+ path: path16.join(piAgentDir(home, env), "extensions", "vibetime.ts"),
8078
8720
  content: piExtensionContent()
8079
8721
  }];
8080
8722
  },
@@ -8086,14 +8728,14 @@ function createPiAdapter() {
8086
8728
  }
8087
8729
 
8088
8730
  // src/adapters/qoder-cn.ts
8089
- import { readdir as readdir7, readFile as readFile10, stat as stat8 } from "node:fs/promises";
8731
+ import { readdir as readdir7, readFile as readFile11, stat as stat8 } from "node:fs/promises";
8090
8732
  import os8 from "node:os";
8091
- import path17 from "node:path";
8733
+ import path18 from "node:path";
8092
8734
 
8093
8735
  // src/adapters/qoder-local-db.ts
8094
8736
  import { access } from "node:fs/promises";
8095
8737
  import os7 from "node:os";
8096
- import path16 from "node:path";
8738
+ import path17 from "node:path";
8097
8739
  function takeQoderDbModelCall(calls, requestId, blockStart) {
8098
8740
  if (requestId) {
8099
8741
  const call = calls.byRequestId.get(requestId)?.shift();
@@ -8112,12 +8754,12 @@ function takeQoderDbModelCall(calls, requestId, blockStart) {
8112
8754
  }
8113
8755
  function appDataRoot(appDirName, home = os7.homedir()) {
8114
8756
  if (process.platform === "darwin") {
8115
- return path16.join(home, "Library", "Application Support", appDirName);
8757
+ return path17.join(home, "Library", "Application Support", appDirName);
8116
8758
  }
8117
8759
  if (process.platform === "win32") {
8118
- return path16.join(process.env.APPDATA || path16.join(home, "AppData", "Roaming"), appDirName);
8760
+ return path17.join(process.env.APPDATA || path17.join(home, "AppData", "Roaming"), appDirName);
8119
8761
  }
8120
- return path16.join(home, ".config", appDirName);
8762
+ return path17.join(home, ".config", appDirName);
8121
8763
  }
8122
8764
  function qoderLocalDbCandidates(appDirName) {
8123
8765
  const candidates = [];
@@ -8127,8 +8769,8 @@ function qoderLocalDbCandidates(appDirName) {
8127
8769
  }
8128
8770
  const configRoot = appDataRoot(appDirName);
8129
8771
  candidates.push(
8130
- path16.join(configRoot, "SharedClientCache", "cache", "db", "local.db"),
8131
- path16.join(configRoot, "SharedClientCache", "db", "local.db")
8772
+ path17.join(configRoot, "SharedClientCache", "cache", "db", "local.db"),
8773
+ path17.join(configRoot, "SharedClientCache", "db", "local.db")
8132
8774
  );
8133
8775
  return candidates;
8134
8776
  }
@@ -8137,7 +8779,7 @@ async function loadQoderIdeModelCatalog(appDirName, home) {
8137
8779
  try {
8138
8780
  const { DatabaseSync } = await import("node:sqlite");
8139
8781
  const db = new DatabaseSync(
8140
- path16.join(appDataRoot(appDirName, home), "User", "globalStorage", "state.vscdb"),
8782
+ path17.join(appDataRoot(appDirName, home), "User", "globalStorage", "state.vscdb"),
8141
8783
  { readOnly: true }
8142
8784
  );
8143
8785
  try {
@@ -8279,7 +8921,7 @@ function resolveSessionPreferredModel(db, sessionId, modelMap) {
8279
8921
 
8280
8922
  // src/adapters/qoder-cn.ts
8281
8923
  function parseQoderCnPaths(filePath) {
8282
- const parts = filePath.split(path17.sep);
8924
+ const parts = filePath.split(path18.sep);
8283
8925
  const subagentsIdx = parts.lastIndexOf("subagents");
8284
8926
  let sessionId = "";
8285
8927
  let projectName = "";
@@ -8289,17 +8931,17 @@ function parseQoderCnPaths(filePath) {
8289
8931
  sessionId = parts[subagentsIdx - 1];
8290
8932
  projectName = parts[subagentsIdx - 2];
8291
8933
  const projectsIdx = parts.lastIndexOf("projects");
8292
- configDir2 = parts.slice(0, projectsIdx).join(path17.sep);
8293
- mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path17.sep);
8934
+ configDir2 = parts.slice(0, projectsIdx).join(path18.sep);
8935
+ mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path18.sep);
8294
8936
  } else {
8295
8937
  const filename = parts.at(-1) || "";
8296
- sessionId = path17.basename(filename, ".jsonl");
8938
+ sessionId = path18.basename(filename, ".jsonl");
8297
8939
  projectName = parts.at(-2) || "";
8298
8940
  if (projectName === "transcript") {
8299
8941
  projectName = parts.at(-3) || "";
8300
8942
  }
8301
8943
  const projectsIdx = parts.lastIndexOf("projects");
8302
- configDir2 = parts.slice(0, projectsIdx).join(path17.sep);
8944
+ configDir2 = parts.slice(0, projectsIdx).join(path18.sep);
8303
8945
  }
8304
8946
  return { configDir: configDir2, projectName, sessionId, mainTranscriptPath };
8305
8947
  }
@@ -8322,7 +8964,7 @@ function rebuildEventIdentity3(event) {
8322
8964
  }
8323
8965
  async function parseModelNamesFromDynamicTexts(dynamicTextsPath) {
8324
8966
  try {
8325
- const content = await readFile10(dynamicTextsPath, "utf8");
8967
+ const content = await readFile11(dynamicTextsPath, "utf8");
8326
8968
  const json = JSON.parse(content);
8327
8969
  const texts = json.texts || {};
8328
8970
  const map = {};
@@ -8338,10 +8980,10 @@ async function parseModelNamesFromDynamicTexts(dynamicTextsPath) {
8338
8980
  }
8339
8981
  }
8340
8982
  async function loadQoderCnModelNames(configDir2, home) {
8341
- const map = await parseModelNamesFromDynamicTexts(path17.join(configDir2, ".auth", "dynamic-texts.json"));
8983
+ const map = await parseModelNamesFromDynamicTexts(path18.join(configDir2, ".auth", "dynamic-texts.json"));
8342
8984
  const siblingConfigDir = configDir2.replace(/\.qoder-cn$/, ".qoder");
8343
8985
  if (siblingConfigDir !== configDir2) {
8344
- const siblingMap = await parseModelNamesFromDynamicTexts(path17.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
8986
+ const siblingMap = await parseModelNamesFromDynamicTexts(path18.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
8345
8987
  for (const [key, val] of Object.entries(siblingMap)) {
8346
8988
  if (!(key in map)) {
8347
8989
  map[key] = val;
@@ -8360,7 +9002,7 @@ async function loadQoderCnModelNames(configDir2, home) {
8360
9002
  }
8361
9003
  async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap) {
8362
9004
  const { configDir: configDir2, projectName, sessionId } = parseQoderCnPaths(filePath);
8363
- const segmentsPath = path17.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
9005
+ const segmentsPath = path18.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
8364
9006
  const modelCalls = [];
8365
9007
  try {
8366
9008
  const files = await readdir7(segmentsPath);
@@ -8368,7 +9010,7 @@ async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMa
8368
9010
  if (!file.endsWith(".jsonl")) {
8369
9011
  continue;
8370
9012
  }
8371
- const content = await readFile10(path17.join(segmentsPath, file), "utf8");
9013
+ const content = await readFile11(path18.join(segmentsPath, file), "utf8");
8372
9014
  let currentTurnIsSubagent = false;
8373
9015
  for (const line of content.split("\n").filter(Boolean)) {
8374
9016
  const raw = parseJsonLine(line);
@@ -8402,7 +9044,7 @@ async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMa
8402
9044
  return modelCalls.filter((call) => call.isSubagent === isSubagentSession);
8403
9045
  }
8404
9046
  async function parseQoderCnSessionFile(filePath, options) {
8405
- const text = await readFile10(filePath, "utf8");
9047
+ const text = await readFile11(filePath, "utf8");
8406
9048
  const lines = text.split("\n").filter(Boolean);
8407
9049
  const parsedPaths = parseQoderCnPaths(filePath);
8408
9050
  const { configDir: configDir2 } = parsedPaths;
@@ -8413,7 +9055,7 @@ async function parseQoderCnSessionFile(filePath, options) {
8413
9055
  let cwd;
8414
9056
  let project = projectContext.project;
8415
9057
  let model;
8416
- const home = path17.resolve(stringOption(options.home) || os8.homedir());
9058
+ const home = path18.resolve(stringOption(options.home) || os8.homedir());
8417
9059
  const modelMap = await loadQoderCnModelNames(configDir2, home);
8418
9060
  const isSubagentSession = filePath.includes("subagents");
8419
9061
  const segmentModelCalls = await loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap);
@@ -8448,7 +9090,7 @@ async function parseQoderCnSessionFile(filePath, options) {
8448
9090
  sessionId = stringField(raw, "sessionId") || sessionId;
8449
9091
  state.sessionId = sessionId;
8450
9092
  cwd = stringField(raw, "cwd") || cwd;
8451
- project = projectContext.project || (cwd ? path17.basename(cwd) : project || await qoderCnProjectFromFilePath(filePath, options));
9093
+ project = projectContext.project || (cwd ? path18.basename(cwd) : project || await qoderCnProjectFromFilePath(filePath, options));
8452
9094
  if (!ts) {
8453
9095
  continue;
8454
9096
  }
@@ -8783,7 +9425,7 @@ async function parseQoderCnSessionFile(filePath, options) {
8783
9425
  }
8784
9426
  dbModelCalls ??= await loadQoderDbModelCalls("QoderCN", parsedPaths.sessionId, modelMap);
8785
9427
  if (dbModelCalls.rootSessionId) {
8786
- const parentPath = path17.join(path17.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
9428
+ const parentPath = path18.join(path18.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
8787
9429
  const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
8788
9430
  return validEvents.map((event) => rebuildEventIdentity3({
8789
9431
  ...event,
@@ -8935,28 +9577,28 @@ function extractQoderAttachedPrompt(content) {
8935
9577
  }
8936
9578
  async function qoderCnProjectContextFromLines(filePath, lines, options, configDir2) {
8937
9579
  const { projectName: projectDir, sessionId } = parseQoderCnPaths(filePath);
8938
- const isSubagent = filePath.includes(`${path17.sep}subagents${path17.sep}`);
9580
+ const isSubagent = filePath.includes(`${path18.sep}subagents${path18.sep}`);
8939
9581
  const inherited = isSubagent ? await readPersistedSessionContextFromOptions(options, sessionId) : void 0;
8940
9582
  let cwds = [];
8941
9583
  for (const line of lines) {
8942
9584
  const raw = parseJsonLine(line);
8943
9585
  const cwd = raw ? stringField(raw, "cwd") : void 0;
8944
- if (cwd && path17.isAbsolute(cwd)) {
9586
+ if (cwd && path18.isAbsolute(cwd)) {
8945
9587
  cwds.push(cwd);
8946
9588
  }
8947
9589
  }
8948
9590
  if (isSubagent) {
8949
- if (inherited?.cwd && path17.isAbsolute(inherited.cwd)) {
9591
+ if (inherited?.cwd && path18.isAbsolute(inherited.cwd)) {
8950
9592
  cwds = [inherited.cwd];
8951
9593
  } else {
8952
- const parentSessionPath = path17.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
9594
+ const parentSessionPath = path18.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
8953
9595
  try {
8954
- const parentText = await readFile10(parentSessionPath, "utf8");
9596
+ const parentText = await readFile11(parentSessionPath, "utf8");
8955
9597
  const parentCwds = [];
8956
9598
  for (const line of parentText.split("\n").filter(Boolean)) {
8957
9599
  const raw = parseJsonLine(line);
8958
9600
  const cwd = raw ? stringField(raw, "cwd") : void 0;
8959
- if (cwd && path17.isAbsolute(cwd)) {
9601
+ if (cwd && path18.isAbsolute(cwd)) {
8960
9602
  parentCwds.push(cwd);
8961
9603
  }
8962
9604
  }
@@ -8968,7 +9610,7 @@ async function qoderCnProjectContextFromLines(filePath, lines, options, configDi
8968
9610
  }
8969
9611
  }
8970
9612
  const root = await gitRootFromCwds2(cwds) || qoderCnProjectRootFromCwds(projectDir, cwds);
8971
- const project = inherited?.project || (cwds.length > 0 ? path17.basename(cwds[0]) : root ? path17.basename(root) : await qoderCnProjectFromFilePath(filePath, options));
9613
+ const project = inherited?.project || (cwds.length > 0 ? path18.basename(cwds[0]) : root ? path18.basename(root) : await qoderCnProjectFromFilePath(filePath, options));
8972
9614
  return {
8973
9615
  project,
8974
9616
  workspaceId: createWorkspaceId({ projectName: project, repoRoot: root })
@@ -8977,15 +9619,15 @@ async function qoderCnProjectContextFromLines(filePath, lines, options, configDi
8977
9619
  async function gitRootFromCwds2(cwds) {
8978
9620
  const seen = /* @__PURE__ */ new Set();
8979
9621
  for (const cwd of cwds) {
8980
- let current = path17.resolve(cwd);
9622
+ let current = path18.resolve(cwd);
8981
9623
  while (!seen.has(current)) {
8982
9624
  seen.add(current);
8983
9625
  try {
8984
- await stat8(path17.join(current, ".git"));
9626
+ await stat8(path18.join(current, ".git"));
8985
9627
  return current;
8986
9628
  } catch {
8987
9629
  }
8988
- const parent = path17.dirname(current);
9630
+ const parent = path18.dirname(current);
8989
9631
  if (parent === current) {
8990
9632
  break;
8991
9633
  }
@@ -8996,12 +9638,12 @@ async function gitRootFromCwds2(cwds) {
8996
9638
  }
8997
9639
  function qoderCnProjectRootFromCwds(projectDir, cwds) {
8998
9640
  for (const cwd of cwds) {
8999
- let current = path17.resolve(cwd);
9641
+ let current = path18.resolve(cwd);
9000
9642
  while (true) {
9001
9643
  if (encodeQoderCnProjectPath(current) === projectDir) {
9002
9644
  return current;
9003
9645
  }
9004
- const parent = path17.dirname(current);
9646
+ const parent = path18.dirname(current);
9005
9647
  if (parent === current) {
9006
9648
  break;
9007
9649
  }
@@ -9011,14 +9653,14 @@ function qoderCnProjectRootFromCwds(projectDir, cwds) {
9011
9653
  return void 0;
9012
9654
  }
9013
9655
  function encodeQoderCnProjectPath(value) {
9014
- return path17.resolve(value).split(path17.sep).join("-").replaceAll("_", "-");
9656
+ return path18.resolve(value).split(path18.sep).join("-").replaceAll("_", "-");
9015
9657
  }
9016
9658
  async function qoderCnProjectFromFilePath(filePath, options) {
9017
- const projectDir = path17.basename(path17.dirname(filePath));
9018
- const home = options ? path17.resolve(stringOption(options.home) || os8.homedir()) : os8.homedir();
9659
+ const projectDir = path18.basename(path18.dirname(filePath));
9660
+ const home = options ? path18.resolve(stringOption(options.home) || os8.homedir()) : os8.homedir();
9019
9661
  const resolved = await resolveQoderCnProjectPath(projectDir, home);
9020
9662
  if (resolved) {
9021
- return path17.basename(resolved);
9663
+ return path18.basename(resolved);
9022
9664
  }
9023
9665
  const homePrefix = `${encodeQoderCnProjectPath(home)}-`;
9024
9666
  if (projectDir.startsWith(homePrefix)) {
@@ -9043,7 +9685,7 @@ async function resolveQoderCnProjectPath(projectDir, home) {
9043
9685
  if (!entry.isDirectory()) {
9044
9686
  continue;
9045
9687
  }
9046
- const candidate = path17.join(current, entry.name);
9688
+ const candidate = path18.join(current, entry.name);
9047
9689
  const encoded = encodeQoderCnProjectPath(candidate);
9048
9690
  if (encoded === projectDir) {
9049
9691
  return candidate;
@@ -9088,9 +9730,9 @@ function hookConfig6() {
9088
9730
  function qoderCnConfigDir(home, env) {
9089
9731
  const override = env?.QODER_CN_CONFIG_DIR;
9090
9732
  if (override && override.trim()) {
9091
- return path17.resolve(override);
9733
+ return path18.resolve(override);
9092
9734
  }
9093
- return path17.join(home, ".qoder-cn");
9735
+ return path18.join(home, ".qoder-cn");
9094
9736
  }
9095
9737
  function createQoderCnAdapter() {
9096
9738
  return {
@@ -9102,27 +9744,27 @@ function createQoderCnAdapter() {
9102
9744
  return qoderCnConfigDir(home, env);
9103
9745
  },
9104
9746
  installedPath(home, env) {
9105
- return path17.join(qoderCnConfigDir(home, env), "settings.json");
9747
+ return path18.join(qoderCnConfigDir(home, env), "settings.json");
9106
9748
  },
9107
9749
  async isInstalled(home, env) {
9108
9750
  return isHooksJsonInstalled(
9109
- path17.join(qoderCnConfigDir(home, env), "settings.json"),
9751
+ path18.join(qoderCnConfigDir(home, env), "settings.json"),
9110
9752
  "vibetime hook --agent qoder-cn"
9111
9753
  );
9112
9754
  },
9113
9755
  installEntries(home, env) {
9114
9756
  return [{
9115
9757
  kind: "hooks-json",
9116
- path: path17.join(qoderCnConfigDir(home, env), "settings.json"),
9758
+ path: path18.join(qoderCnConfigDir(home, env), "settings.json"),
9117
9759
  content: hookConfig6()
9118
9760
  }];
9119
9761
  },
9120
9762
  sourcePaths(home, env) {
9121
9763
  const base = qoderCnConfigDir(home, env);
9122
9764
  return [
9123
- path17.join(base, "projects"),
9124
- path17.join(base, ".qoder.json"),
9125
- path17.join(home, ".qoder.json")
9765
+ path18.join(base, "projects"),
9766
+ path18.join(base, ".qoder.json"),
9767
+ path18.join(home, ".qoder.json")
9126
9768
  ];
9127
9769
  },
9128
9770
  parseSessionFile: parseQoderCnSessionFile
@@ -9131,11 +9773,11 @@ function createQoderCnAdapter() {
9131
9773
 
9132
9774
  // src/adapters/qoder.ts
9133
9775
  import { existsSync } from "node:fs";
9134
- import { readdir as readdir8, readFile as readFile11, stat as stat9 } from "node:fs/promises";
9776
+ import { readdir as readdir8, readFile as readFile12, stat as stat9 } from "node:fs/promises";
9135
9777
  import os9 from "node:os";
9136
- import path18 from "node:path";
9778
+ import path19 from "node:path";
9137
9779
  function parseQoderPaths(filePath) {
9138
- const parts = filePath.split(path18.sep);
9780
+ const parts = filePath.split(path19.sep);
9139
9781
  const subagentsIdx = parts.lastIndexOf("subagents");
9140
9782
  let sessionId = "";
9141
9783
  let projectName = "";
@@ -9145,17 +9787,17 @@ function parseQoderPaths(filePath) {
9145
9787
  sessionId = parts[subagentsIdx - 1];
9146
9788
  projectName = parts[subagentsIdx - 2];
9147
9789
  const projectsIdx = parts.lastIndexOf("projects");
9148
- configDir2 = parts.slice(0, projectsIdx).join(path18.sep);
9149
- mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path18.sep);
9790
+ configDir2 = parts.slice(0, projectsIdx).join(path19.sep);
9791
+ mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path19.sep);
9150
9792
  } else {
9151
9793
  const filename = parts.at(-1) || "";
9152
- sessionId = path18.basename(filename, ".jsonl");
9794
+ sessionId = path19.basename(filename, ".jsonl");
9153
9795
  projectName = parts.at(-2) || "";
9154
9796
  if (projectName === "transcript") {
9155
9797
  projectName = parts.at(-3) || "";
9156
9798
  }
9157
9799
  const projectsIdx = parts.lastIndexOf("projects");
9158
- configDir2 = parts.slice(0, projectsIdx).join(path18.sep);
9800
+ configDir2 = parts.slice(0, projectsIdx).join(path19.sep);
9159
9801
  }
9160
9802
  return { configDir: configDir2, projectName, sessionId, mainTranscriptPath };
9161
9803
  }
@@ -9178,7 +9820,7 @@ function rebuildEventIdentity4(event) {
9178
9820
  }
9179
9821
  async function parseModelNamesFromDynamicTexts2(dynamicTextsPath) {
9180
9822
  try {
9181
- const content = await readFile11(dynamicTextsPath, "utf8");
9823
+ const content = await readFile12(dynamicTextsPath, "utf8");
9182
9824
  const json = JSON.parse(content);
9183
9825
  const texts = json.texts || {};
9184
9826
  const map = {};
@@ -9194,10 +9836,10 @@ async function parseModelNamesFromDynamicTexts2(dynamicTextsPath) {
9194
9836
  }
9195
9837
  }
9196
9838
  async function loadQoderModelNames(configDir2, home) {
9197
- const map = await parseModelNamesFromDynamicTexts2(path18.join(configDir2, ".auth", "dynamic-texts.json"));
9839
+ const map = await parseModelNamesFromDynamicTexts2(path19.join(configDir2, ".auth", "dynamic-texts.json"));
9198
9840
  const siblingConfigDir = configDir2.replace(/\.qoder$/, ".qoder-cn");
9199
9841
  if (siblingConfigDir !== configDir2) {
9200
- const siblingMap = await parseModelNamesFromDynamicTexts2(path18.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
9842
+ const siblingMap = await parseModelNamesFromDynamicTexts2(path19.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
9201
9843
  for (const [key, val] of Object.entries(siblingMap)) {
9202
9844
  if (!(key in map)) {
9203
9845
  map[key] = val;
@@ -9205,11 +9847,11 @@ async function loadQoderModelNames(configDir2, home) {
9205
9847
  }
9206
9848
  }
9207
9849
  if (isQwenworkConfigRoot(configDir2)) {
9208
- for (const dir of [path18.join(home, ".qoder"), path18.join(home, ".qoder-cn")]) {
9209
- if (path18.resolve(dir) === path18.resolve(configDir2)) {
9850
+ for (const dir of [path19.join(home, ".qoder"), path19.join(home, ".qoder-cn")]) {
9851
+ if (path19.resolve(dir) === path19.resolve(configDir2)) {
9210
9852
  continue;
9211
9853
  }
9212
- const fallbackMap = await parseModelNamesFromDynamicTexts2(path18.join(dir, ".auth", "dynamic-texts.json"));
9854
+ const fallbackMap = await parseModelNamesFromDynamicTexts2(path19.join(dir, ".auth", "dynamic-texts.json"));
9213
9855
  for (const [key, val] of Object.entries(fallbackMap)) {
9214
9856
  if (!(key in map)) {
9215
9857
  map[key] = val;
@@ -9228,17 +9870,17 @@ async function loadQoderModelNames(configDir2, home) {
9228
9870
  return map;
9229
9871
  }
9230
9872
  function isQwenworkConfigRoot(configDir2) {
9231
- const name = path18.basename(path18.resolve(configDir2));
9873
+ const name = path19.basename(path19.resolve(configDir2));
9232
9874
  if (name === ".qwenworkcn" || name === ".qwenwork") {
9233
9875
  return true;
9234
9876
  }
9235
9877
  const override = process.env.QWENWORK_CONFIG_DIR;
9236
- return Boolean(override && override.trim() && path18.basename(path18.resolve(override)) === name);
9878
+ return Boolean(override && override.trim() && path19.basename(path19.resolve(override)) === name);
9237
9879
  }
9238
9880
  var FAILED_SEGMENT_STOP_REASONS = /* @__PURE__ */ new Set(["cancelled", "canceled", "error", "failed", "refusal"]);
9239
9881
  async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap) {
9240
9882
  const { configDir: configDir2, projectName, sessionId } = parseQoderPaths(filePath);
9241
- const segmentsPath = path18.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
9883
+ const segmentsPath = path19.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
9242
9884
  const modelCalls = [];
9243
9885
  try {
9244
9886
  const files = await readdir8(segmentsPath);
@@ -9246,7 +9888,7 @@ async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap)
9246
9888
  if (!file.endsWith(".jsonl")) {
9247
9889
  continue;
9248
9890
  }
9249
- const content = await readFile11(path18.join(segmentsPath, file), "utf8");
9891
+ const content = await readFile12(path19.join(segmentsPath, file), "utf8");
9250
9892
  let currentTurnIsSubagent = false;
9251
9893
  for (const line of content.split("\n").filter(Boolean)) {
9252
9894
  const raw = parseJsonLine(line);
@@ -9280,7 +9922,7 @@ async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap)
9280
9922
  return modelCalls.filter((call) => call.isSubagent === isSubagentSession);
9281
9923
  }
9282
9924
  async function parseQoderSessionFile(filePath, options) {
9283
- const text = await readFile11(filePath, "utf8");
9925
+ const text = await readFile12(filePath, "utf8");
9284
9926
  const lines = text.split("\n").filter(Boolean);
9285
9927
  const parsedPaths = parseQoderPaths(filePath);
9286
9928
  const { configDir: configDir2 } = parsedPaths;
@@ -9291,7 +9933,7 @@ async function parseQoderSessionFile(filePath, options) {
9291
9933
  let cwd;
9292
9934
  let project = projectContext.project;
9293
9935
  let model;
9294
- const home = path18.resolve(stringOption(options.home) || os9.homedir());
9936
+ const home = path19.resolve(stringOption(options.home) || os9.homedir());
9295
9937
  const modelMap = await loadQoderModelNames(configDir2, home);
9296
9938
  const qwenworkRoot = isQwenworkConfigRoot(configDir2);
9297
9939
  const isSubagentSession = filePath.includes("subagents");
@@ -9327,7 +9969,7 @@ async function parseQoderSessionFile(filePath, options) {
9327
9969
  sessionId = stringField(raw, "sessionId") || sessionId;
9328
9970
  state.sessionId = sessionId;
9329
9971
  cwd = stringField(raw, "cwd") || cwd;
9330
- project = projectContext.project || (cwd ? path18.basename(cwd) : project || await qoderProjectFromFilePath(filePath, options));
9972
+ project = projectContext.project || (cwd ? path19.basename(cwd) : project || await qoderProjectFromFilePath(filePath, options));
9331
9973
  if (!ts) {
9332
9974
  continue;
9333
9975
  }
@@ -9637,7 +10279,7 @@ async function parseQoderSessionFile(filePath, options) {
9637
10279
  }
9638
10280
  dbModelCalls ??= await loadQoderDbModelCalls("Qoder", parsedPaths.sessionId, modelMap);
9639
10281
  if (dbModelCalls.rootSessionId) {
9640
- const parentPath = path18.join(path18.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
10282
+ const parentPath = path19.join(path19.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
9641
10283
  const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
9642
10284
  return validEvents.map((event) => rebuildEventIdentity4({
9643
10285
  ...event,
@@ -9753,41 +10395,41 @@ function qoderExtractText(value) {
9753
10395
  }
9754
10396
  async function qoderProjectContextFromLines(filePath, lines, options, configDir2) {
9755
10397
  const { projectName: projectDir, sessionId } = parseQoderPaths(filePath);
9756
- const isSubagent = filePath.includes(`${path18.sep}subagents${path18.sep}`);
10398
+ const isSubagent = filePath.includes(`${path19.sep}subagents${path19.sep}`);
9757
10399
  const inherited = isSubagent ? await readPersistedSessionContextFromOptions(options, sessionId) : void 0;
9758
10400
  let cwds = [];
9759
10401
  const workspaceDirs = [];
9760
10402
  for (const line of lines) {
9761
10403
  const raw = parseJsonLine(line);
9762
10404
  const cwd = raw ? stringField(raw, "cwd") : void 0;
9763
- if (cwd && path18.isAbsolute(cwd)) {
10405
+ if (cwd && path19.isAbsolute(cwd)) {
9764
10406
  cwds.push(cwd);
9765
10407
  }
9766
10408
  if (raw && stringField(raw, "type") === "workspace-directories") {
9767
10409
  for (const dir of arrayField5(raw, "directories")) {
9768
- if (typeof dir === "string" && path18.isAbsolute(dir)) {
10410
+ if (typeof dir === "string" && path19.isAbsolute(dir)) {
9769
10411
  workspaceDirs.push(dir);
9770
10412
  }
9771
10413
  }
9772
10414
  }
9773
10415
  }
9774
10416
  if (isSubagent) {
9775
- if (inherited?.cwd && path18.isAbsolute(inherited.cwd)) {
10417
+ if (inherited?.cwd && path19.isAbsolute(inherited.cwd)) {
9776
10418
  cwds = [inherited.cwd];
9777
10419
  } else {
9778
- const parentSessionPath = path18.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
10420
+ const parentSessionPath = path19.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
9779
10421
  try {
9780
- const parentText = await readFile11(parentSessionPath, "utf8");
10422
+ const parentText = await readFile12(parentSessionPath, "utf8");
9781
10423
  const parentCwds = [];
9782
10424
  for (const line of parentText.split("\n").filter(Boolean)) {
9783
10425
  const raw = parseJsonLine(line);
9784
10426
  const cwd = raw ? stringField(raw, "cwd") : void 0;
9785
- if (cwd && path18.isAbsolute(cwd)) {
10427
+ if (cwd && path19.isAbsolute(cwd)) {
9786
10428
  parentCwds.push(cwd);
9787
10429
  }
9788
10430
  if (workspaceDirs.length === 0 && raw && stringField(raw, "type") === "workspace-directories") {
9789
10431
  for (const dir of arrayField5(raw, "directories")) {
9790
- if (typeof dir === "string" && path18.isAbsolute(dir)) {
10432
+ if (typeof dir === "string" && path19.isAbsolute(dir)) {
9791
10433
  workspaceDirs.push(dir);
9792
10434
  }
9793
10435
  }
@@ -9807,29 +10449,29 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
9807
10449
  }
9808
10450
  }
9809
10451
  const root = await gitRootFromCwds3(cwds) || qoderProjectRootFromCwds(projectDir, cwds);
9810
- const project = inherited?.project || (cwds.length > 0 ? path18.basename(cwds[0]) : root ? path18.basename(root) : await qoderProjectFromFilePath(filePath, options));
10452
+ const project = inherited?.project || (cwds.length > 0 ? path19.basename(cwds[0]) : root ? path19.basename(root) : await qoderProjectFromFilePath(filePath, options));
9811
10453
  return {
9812
10454
  project,
9813
10455
  workspaceId: createWorkspaceId({ projectName: project, repoRoot: root })
9814
10456
  };
9815
10457
  }
9816
10458
  function pathInsideDir(candidate, dir) {
9817
- const resolvedDir = path18.resolve(dir);
9818
- const resolved = path18.resolve(candidate);
9819
- return resolved === resolvedDir || resolved.startsWith(`${resolvedDir}${path18.sep}`);
10459
+ const resolvedDir = path19.resolve(dir);
10460
+ const resolved = path19.resolve(candidate);
10461
+ return resolved === resolvedDir || resolved.startsWith(`${resolvedDir}${path19.sep}`);
9820
10462
  }
9821
10463
  async function gitRootFromCwds3(cwds) {
9822
10464
  const seen = /* @__PURE__ */ new Set();
9823
10465
  for (const cwd of cwds) {
9824
- let current = path18.resolve(cwd);
10466
+ let current = path19.resolve(cwd);
9825
10467
  while (!seen.has(current)) {
9826
10468
  seen.add(current);
9827
10469
  try {
9828
- await stat9(path18.join(current, ".git"));
10470
+ await stat9(path19.join(current, ".git"));
9829
10471
  return current;
9830
10472
  } catch {
9831
10473
  }
9832
- const parent = path18.dirname(current);
10474
+ const parent = path19.dirname(current);
9833
10475
  if (parent === current) {
9834
10476
  break;
9835
10477
  }
@@ -9840,12 +10482,12 @@ async function gitRootFromCwds3(cwds) {
9840
10482
  }
9841
10483
  function qoderProjectRootFromCwds(projectDir, cwds) {
9842
10484
  for (const cwd of cwds) {
9843
- let current = path18.resolve(cwd);
10485
+ let current = path19.resolve(cwd);
9844
10486
  while (true) {
9845
10487
  if (qoderEncodedVariants(current).includes(projectDir)) {
9846
10488
  return current;
9847
10489
  }
9848
- const parent = path18.dirname(current);
10490
+ const parent = path19.dirname(current);
9849
10491
  if (parent === current) {
9850
10492
  break;
9851
10493
  }
@@ -9855,7 +10497,7 @@ function qoderProjectRootFromCwds(projectDir, cwds) {
9855
10497
  return void 0;
9856
10498
  }
9857
10499
  function rawQoderProjectPath(value) {
9858
- return path18.resolve(value).split(path18.sep).join("-");
10500
+ return path19.resolve(value).split(path19.sep).join("-");
9859
10501
  }
9860
10502
  function qoderEncodedVariants(value) {
9861
10503
  const raw = rawQoderProjectPath(value);
@@ -9876,11 +10518,11 @@ function qoderEncodedProjectSuffix(projectDir, home) {
9876
10518
  return void 0;
9877
10519
  }
9878
10520
  async function qoderProjectFromFilePath(filePath, options) {
9879
- const projectDir = path18.basename(path18.dirname(filePath));
9880
- const home = options ? path18.resolve(stringOption(options.home) || os9.homedir()) : os9.homedir();
10521
+ const projectDir = path19.basename(path19.dirname(filePath));
10522
+ const home = options ? path19.resolve(stringOption(options.home) || os9.homedir()) : os9.homedir();
9881
10523
  const resolved = await resolveQoderProjectPath(projectDir, home);
9882
10524
  if (resolved) {
9883
- return path18.basename(resolved);
10525
+ return path19.basename(resolved);
9884
10526
  }
9885
10527
  const suffix = qoderEncodedProjectSuffix(projectDir, home);
9886
10528
  if (suffix) {
@@ -9906,7 +10548,7 @@ async function resolveQoderProjectPath(projectDir, home) {
9906
10548
  if (!entry.isDirectory()) {
9907
10549
  continue;
9908
10550
  }
9909
- const candidate = path18.join(current, entry.name);
10551
+ const candidate = path19.join(current, entry.name);
9910
10552
  const candidateVariants = qoderEncodedVariants(candidate);
9911
10553
  if (candidateVariants.includes(projectDir)) {
9912
10554
  return candidate;
@@ -9951,19 +10593,19 @@ function hookConfig7() {
9951
10593
  function qoderConfigDir(home, env) {
9952
10594
  const override = env?.QODER_CONFIG_DIR;
9953
10595
  if (override && override.trim()) {
9954
- return path18.resolve(override);
10596
+ return path19.resolve(override);
9955
10597
  }
9956
- return path18.join(home, ".qoder");
10598
+ return path19.join(home, ".qoder");
9957
10599
  }
9958
10600
  function qwenworkConfigDir(home, env) {
9959
10601
  const override = env?.QWENWORK_CONFIG_DIR;
9960
10602
  if (override && override.trim()) {
9961
- return path18.resolve(override);
10603
+ return path19.resolve(override);
9962
10604
  }
9963
- return path18.join(home, ".qwenworkcn");
10605
+ return path19.join(home, ".qwenworkcn");
9964
10606
  }
9965
10607
  function qoderConfigDirs(home, env) {
9966
- return [...new Set([qoderConfigDir(home, env), qwenworkConfigDir(home, env)].map((dir) => path18.resolve(dir)))];
10608
+ return [...new Set([qoderConfigDir(home, env), qwenworkConfigDir(home, env)].map((dir) => path19.resolve(dir)))];
9967
10609
  }
9968
10610
  function createQoderAdapter() {
9969
10611
  return {
@@ -9975,11 +10617,11 @@ function createQoderAdapter() {
9975
10617
  return qoderConfigDir(home, env);
9976
10618
  },
9977
10619
  installedPath(home, env) {
9978
- return path18.join(qoderConfigDir(home, env), "settings.json");
10620
+ return path19.join(qoderConfigDir(home, env), "settings.json");
9979
10621
  },
9980
10622
  async isInstalled(home, env) {
9981
10623
  return isHooksJsonInstalled(
9982
- path18.join(qoderConfigDir(home, env), "settings.json"),
10624
+ path19.join(qoderConfigDir(home, env), "settings.json"),
9983
10625
  "vibetime hook --agent qoder"
9984
10626
  );
9985
10627
  },
@@ -9988,16 +10630,16 @@ function createQoderAdapter() {
9988
10630
  const targets = [primary, ...variants.filter((dir) => existsSync(dir))];
9989
10631
  return targets.map((base) => ({
9990
10632
  kind: "hooks-json",
9991
- path: path18.join(base, "settings.json"),
10633
+ path: path19.join(base, "settings.json"),
9992
10634
  content: hookConfig7()
9993
10635
  }));
9994
10636
  },
9995
10637
  sourcePaths(home, env) {
9996
- const paths = qoderConfigDirs(home, env).map((base2) => path18.join(base2, "projects"));
10638
+ const paths = qoderConfigDirs(home, env).map((base2) => path19.join(base2, "projects"));
9997
10639
  const base = qoderConfigDir(home, env);
9998
10640
  paths.push(
9999
- path18.join(base, ".qoder.json"),
10000
- path18.join(home, ".qoder.json")
10641
+ path19.join(base, ".qoder.json"),
10642
+ path19.join(home, ".qoder.json")
10001
10643
  );
10002
10644
  return paths;
10003
10645
  },
@@ -10033,27 +10675,27 @@ function normalizeId(id) {
10033
10675
  }
10034
10676
 
10035
10677
  // src/adapters/workbuddy.ts
10036
- import { readdir as readdir9, readFile as readFile12, stat as stat10 } from "node:fs/promises";
10037
- import path19 from "node:path";
10678
+ import { readdir as readdir9, readFile as readFile13, stat as stat10 } from "node:fs/promises";
10679
+ import path20 from "node:path";
10038
10680
  function workbuddyProjectsDir(home, env) {
10039
10681
  const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
10040
10682
  if (override && override.trim()) {
10041
- return path19.resolve(override, override.endsWith("projects") ? "" : "projects");
10683
+ return path20.resolve(override, override.endsWith("projects") ? "" : "projects");
10042
10684
  }
10043
- return path19.join(home, ".workbuddy", "projects");
10685
+ return path20.join(home, ".workbuddy", "projects");
10044
10686
  }
10045
10687
  function workbuddyBaseDir(home, env) {
10046
10688
  const override = env?.WORKBUDDY_HOME;
10047
10689
  if (override && override.trim()) {
10048
- return path19.resolve(override);
10690
+ return path20.resolve(override);
10049
10691
  }
10050
- return path19.join(home, ".workbuddy");
10692
+ return path20.join(home, ".workbuddy");
10051
10693
  }
10052
10694
  function projectFromCwd(cwd, fallback) {
10053
10695
  if (!cwd) {
10054
10696
  return fallback;
10055
10697
  }
10056
- return path19.basename(cwd) || fallback;
10698
+ return path20.basename(cwd) || fallback;
10057
10699
  }
10058
10700
  function sourceHash(filePath) {
10059
10701
  return `sha256:${createStableHash(filePath)}`;
@@ -10171,7 +10813,7 @@ function toolCallFailed(record) {
10171
10813
  return status === "failed" || status === "incomplete" || record.is_error === true || providerData.error != null || providerData.isError === true;
10172
10814
  }
10173
10815
  async function readWorkbuddyLines(filePath) {
10174
- const text = await readFile12(filePath, "utf8");
10816
+ const text = await readFile13(filePath, "utf8");
10175
10817
  return text.split(/\r?\n/).map((line, index) => {
10176
10818
  if (!line.trim()) {
10177
10819
  return void 0;
@@ -10191,8 +10833,8 @@ async function parseWorkbuddySessionFile(filePath, options) {
10191
10833
  }
10192
10834
  const events = [];
10193
10835
  const first = lines[0].record;
10194
- const sessionId = stringField(first, "sessionId") || path19.basename(filePath, ".jsonl");
10195
- const fallbackProject = path19.basename(path19.dirname(filePath));
10836
+ const sessionId = stringField(first, "sessionId") || path20.basename(filePath, ".jsonl");
10837
+ const fallbackProject = path20.basename(path20.dirname(filePath));
10196
10838
  const cwd = lines.map((line) => stringField(line.record, "cwd")).find(Boolean);
10197
10839
  const project = projectFromCwd(cwd, fallbackProject);
10198
10840
  const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
@@ -10481,11 +11123,11 @@ async function workbuddyBackfillFiles(sourceRoot, home, env) {
10481
11123
  if (!project.isDirectory()) {
10482
11124
  continue;
10483
11125
  }
10484
- const projectDir = path19.join(base, project.name);
11126
+ const projectDir = path20.join(base, project.name);
10485
11127
  const entries = await readdir9(projectDir, { withFileTypes: true });
10486
11128
  for (const entry of entries) {
10487
11129
  if (entry.isFile() && entry.name.endsWith(".jsonl")) {
10488
- const filePath = path19.join(projectDir, entry.name);
11130
+ const filePath = path20.join(projectDir, entry.name);
10489
11131
  const info = await stat10(filePath);
10490
11132
  files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
10491
11133
  }
@@ -10523,18 +11165,18 @@ function createWorkbuddyAdapter() {
10523
11165
  return workbuddyProjectsDir(home, env);
10524
11166
  },
10525
11167
  installedPath(home, env) {
10526
- return path19.join(workbuddyBaseDir(home, env), "settings.json");
11168
+ return path20.join(workbuddyBaseDir(home, env), "settings.json");
10527
11169
  },
10528
11170
  async isInstalled(home, env) {
10529
11171
  return isHooksJsonInstalled(
10530
- path19.join(workbuddyBaseDir(home, env), "settings.json"),
11172
+ path20.join(workbuddyBaseDir(home, env), "settings.json"),
10531
11173
  "vibetime hook --agent workbuddy"
10532
11174
  );
10533
11175
  },
10534
11176
  installEntries(home, env) {
10535
11177
  return [{
10536
11178
  kind: "hooks-json",
10537
- path: path19.join(workbuddyBaseDir(home, env), "settings.json"),
11179
+ path: path20.join(workbuddyBaseDir(home, env), "settings.json"),
10538
11180
  content: hookConfig8()
10539
11181
  }];
10540
11182
  },
@@ -10547,20 +11189,20 @@ function createWorkbuddyAdapter() {
10547
11189
 
10548
11190
  // src/adapters/zcode.ts
10549
11191
  import { execFile } from "node:child_process";
10550
- import { readFile as readFile13, stat as stat11 } from "node:fs/promises";
10551
- import path20 from "node:path";
11192
+ import { readFile as readFile14, stat as stat11 } from "node:fs/promises";
11193
+ import path21 from "node:path";
10552
11194
  import { promisify as promisify2 } from "node:util";
10553
11195
  init_fs();
10554
11196
  var execFileAsync = promisify2(execFile);
10555
11197
  function zcodeCliDir(home, env) {
10556
11198
  const override = env?.ZCODE_CLI_DIR || env?.ZCODE_HOME;
10557
11199
  if (override && override.trim()) {
10558
- return path20.resolve(override, override.endsWith("cli") ? "" : "cli");
11200
+ return path21.resolve(override, override.endsWith("cli") ? "" : "cli");
10559
11201
  }
10560
- return path20.join(home, ".zcode", "cli");
11202
+ return path21.join(home, ".zcode", "cli");
10561
11203
  }
10562
11204
  function zcodeDbPath(home, env) {
10563
- return path20.join(zcodeCliDir(home, env), "db", "db.sqlite");
11205
+ return path21.join(zcodeCliDir(home, env), "db", "db.sqlite");
10564
11206
  }
10565
11207
  var providerNameCache = null;
10566
11208
  async function loadProviderNames(configPath2) {
@@ -10576,7 +11218,7 @@ async function loadProviderNames(configPath2) {
10576
11218
  }
10577
11219
  const map = /* @__PURE__ */ new Map();
10578
11220
  try {
10579
- const raw = await readFile13(configPath2, "utf-8");
11221
+ const raw = await readFile14(configPath2, "utf-8");
10580
11222
  const config = JSON.parse(raw);
10581
11223
  const providers = config?.provider;
10582
11224
  if (isPlainObject(providers)) {
@@ -10594,7 +11236,7 @@ function sourceHash2(filePath) {
10594
11236
  return `sha256:${createStableHash(filePath)}`;
10595
11237
  }
10596
11238
  function projectFromDirectory(directory) {
10597
- return directory ? path20.basename(directory) || "zcode" : "zcode";
11239
+ return directory ? path21.basename(directory) || "zcode" : "zcode";
10598
11240
  }
10599
11241
  function isoFromMs(value) {
10600
11242
  return timestampFrom(typeof value === "number" ? value : Number(value));
@@ -10777,16 +11419,16 @@ async function parseZCodeDb(filePath, options) {
10777
11419
  if (rows.length === 0) {
10778
11420
  return [];
10779
11421
  }
10780
- let candidate = path20.resolve(filePath);
11422
+ let candidate = path21.resolve(filePath);
10781
11423
  let configPath2 = "";
10782
11424
  for (let i = 0; i < 12; i++) {
10783
- const probe = path20.join(candidate, ".zcode", "v2", "config.json");
11425
+ const probe = path21.join(candidate, ".zcode", "v2", "config.json");
10784
11426
  try {
10785
11427
  await stat11(probe);
10786
11428
  configPath2 = probe;
10787
11429
  break;
10788
11430
  } catch {
10789
- const parent = path20.dirname(candidate);
11431
+ const parent = path21.dirname(candidate);
10790
11432
  if (parent === candidate) break;
10791
11433
  candidate = parent;
10792
11434
  }
@@ -11002,7 +11644,7 @@ async function parseZCodeDb(filePath, options) {
11002
11644
  }
11003
11645
  async function zcodeBackfillFiles(sourceRoot, home, env) {
11004
11646
  const candidate = sourceRoot || zcodeDbPath(home, env);
11005
- const filePath = candidate.endsWith(".sqlite") ? candidate : path20.join(candidate, "db", "db.sqlite");
11647
+ const filePath = candidate.endsWith(".sqlite") ? candidate : path21.join(candidate, "db", "db.sqlite");
11006
11648
  try {
11007
11649
  const info = await stat11(filePath);
11008
11650
  return [{ path: filePath, modifiedAt: info.mtime.toISOString() }];
@@ -11037,49 +11679,49 @@ function createZCodeAdapter() {
11037
11679
 
11038
11680
  // src/adapters/zed.ts
11039
11681
  import os10 from "node:os";
11040
- import path21 from "node:path";
11682
+ import path22 from "node:path";
11041
11683
  function zedThreadsCandidates(home, env) {
11042
11684
  const candidates = [];
11043
11685
  const platform2 = process.platform;
11044
11686
  if (platform2 === "darwin") {
11045
- candidates.push(path21.join(home, "Library", "Application Support", "Zed", "threads", "threads.db"));
11687
+ candidates.push(path22.join(home, "Library", "Application Support", "Zed", "threads", "threads.db"));
11046
11688
  } else if (platform2 === "win32") {
11047
11689
  const appdata = env?.APPDATA;
11048
11690
  if (appdata && appdata.trim()) {
11049
- candidates.push(path21.join(path21.resolve(appdata), "Zed", "threads", "threads.db"));
11691
+ candidates.push(path22.join(path22.resolve(appdata), "Zed", "threads", "threads.db"));
11050
11692
  }
11051
- candidates.push(path21.join(home, "AppData", "Roaming", "Zed", "threads", "threads.db"));
11693
+ candidates.push(path22.join(home, "AppData", "Roaming", "Zed", "threads", "threads.db"));
11052
11694
  } else {
11053
11695
  const xdgData = env?.XDG_DATA_HOME;
11054
11696
  if (xdgData && xdgData.trim()) {
11055
- candidates.push(path21.join(path21.resolve(xdgData), "zed", "threads", "threads.db"));
11697
+ candidates.push(path22.join(path22.resolve(xdgData), "zed", "threads", "threads.db"));
11056
11698
  }
11057
- candidates.push(path21.join(home, ".local", "share", "zed", "threads", "threads.db"));
11699
+ candidates.push(path22.join(home, ".local", "share", "zed", "threads", "threads.db"));
11058
11700
  const xdgConfig = env?.XDG_CONFIG_HOME;
11059
11701
  if (xdgConfig && xdgConfig.trim()) {
11060
- candidates.push(path21.join(path21.resolve(xdgConfig), "zed", "threads", "threads.db"));
11702
+ candidates.push(path22.join(path22.resolve(xdgConfig), "zed", "threads", "threads.db"));
11061
11703
  }
11062
- candidates.push(path21.join(home, ".config", "zed", "threads", "threads.db"));
11704
+ candidates.push(path22.join(home, ".config", "zed", "threads", "threads.db"));
11063
11705
  }
11064
11706
  return candidates;
11065
11707
  }
11066
11708
  function zedConfigDir(home, env) {
11067
11709
  const platform2 = process.platform;
11068
11710
  if (platform2 === "darwin") {
11069
- return path21.join(home, "Library", "Application Support", "Zed");
11711
+ return path22.join(home, "Library", "Application Support", "Zed");
11070
11712
  }
11071
11713
  if (platform2 === "win32") {
11072
11714
  const appdata = env?.APPDATA;
11073
11715
  if (appdata && appdata.trim()) {
11074
- return path21.join(path21.resolve(appdata), "Zed");
11716
+ return path22.join(path22.resolve(appdata), "Zed");
11075
11717
  }
11076
- return path21.join(home, "AppData", "Roaming", "Zed");
11718
+ return path22.join(home, "AppData", "Roaming", "Zed");
11077
11719
  }
11078
11720
  const xdgConfig = env?.XDG_CONFIG_HOME;
11079
11721
  if (xdgConfig && xdgConfig.trim()) {
11080
- return path21.join(path21.resolve(xdgConfig), "zed");
11722
+ return path22.join(path22.resolve(xdgConfig), "zed");
11081
11723
  }
11082
- return path21.join(home, ".config", "zed");
11724
+ return path22.join(home, ".config", "zed");
11083
11725
  }
11084
11726
  function baseZedEvent(event) {
11085
11727
  return {
@@ -11143,7 +11785,7 @@ async function parseZedSessionFile(dbPath, options) {
11143
11785
  const folderRaw = row.folder_paths || "";
11144
11786
  const folder = folderRaw.split(/[\n,]/).map((s) => s.trim()).find(Boolean);
11145
11787
  const cwd = folder || void 0;
11146
- const project = cwd ? path21.basename(cwd) : row.summary ? row.summary.slice(0, 40) : void 0;
11788
+ const project = cwd ? path22.basename(cwd) : row.summary ? row.summary.slice(0, 40) : void 0;
11147
11789
  let json;
11148
11790
  try {
11149
11791
  const bytes = row.data_type === "zstd" ? decompress2(new Uint8Array(row.data)) : new Uint8Array(row.data);
@@ -11418,7 +12060,7 @@ function createZedAdapter() {
11418
12060
  return zedConfigDir(home, env);
11419
12061
  },
11420
12062
  installedPath(home, env) {
11421
- return path21.join(zedConfigDir(home, env), "vibetime-marker");
12063
+ return path22.join(zedConfigDir(home, env), "vibetime-marker");
11422
12064
  },
11423
12065
  async isInstalled() {
11424
12066
  return false;
@@ -11793,11 +12435,26 @@ async function installEntry(entry, options) {
11793
12435
  await mergeHooksJson(entry.path, entry.content, options);
11794
12436
  return;
11795
12437
  }
12438
+ if (entry.kind === "hooks-toml" && typeof entry.content === "object") {
12439
+ await mergeHooksToml(entry.path, entry.content, options);
12440
+ return;
12441
+ }
11796
12442
  await writeGeneratedFile(entry.path, String(entry.content), {
11797
12443
  ...options,
11798
12444
  onWrite: options.onWrite
11799
12445
  });
11800
12446
  }
12447
+ async function uninstallEntry(entry, options) {
12448
+ if (entry.kind === "hooks-toml" && typeof entry.content === "object") {
12449
+ await uninstallHooksToml(entry.path, entry.content, options);
12450
+ return;
12451
+ }
12452
+ if (entry.kind === "hooks-json" && typeof entry.content === "object") {
12453
+ await uninstallHooksJson(entry.path, entry.content, options);
12454
+ return;
12455
+ }
12456
+ await uninstallGeneratedFile(entry.path, options);
12457
+ }
11801
12458
  async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
11802
12459
  const { mkdir: mkdir6, writeFile: writeFile5 } = await import("node:fs/promises");
11803
12460
  const pathMod = await import("node:path");
@@ -11894,20 +12551,232 @@ function hookCommandFromGroup(group) {
11894
12551
  const hook = group.hooks[0];
11895
12552
  return isPlainObject(hook) && typeof hook.command === "string" ? hook.command : void 0;
11896
12553
  }
12554
+ async function mergeHooksToml(filePath, content, { dryRun, force, onWrite }) {
12555
+ const { mkdir: mkdir6, writeFile: writeFile5 } = await import("node:fs/promises");
12556
+ const pathMod = await import("node:path");
12557
+ if (dryRun) {
12558
+ onWrite(`Would merge ${filePath}`);
12559
+ return;
12560
+ }
12561
+ const existingText = await readTextIfExists(filePath);
12562
+ if (existingText !== null && existingText.trim() !== "" && !looksLikeToml(existingText) && !force) {
12563
+ throw new Error(
12564
+ `Refusing to update non-TOML file: ${filePath}. Re-run with --force if this is intentional.`
12565
+ );
12566
+ }
12567
+ const desired = Array.isArray(content.hooks) ? content.hooks : [];
12568
+ const nextText = mergeTomlHookRules(existingText ?? "", desired);
12569
+ if (existingText === nextText || existingText === null && nextText === "") {
12570
+ onWrite(`Already installed ${filePath}`);
12571
+ return;
12572
+ }
12573
+ if (existingText !== null) {
12574
+ const existingKeys = new Set(
12575
+ parseTomlHookRules(existingText).map((rule) => `${rule.event}\0${rule.command}`)
12576
+ );
12577
+ const allPresent = desired.every((rule) => existingKeys.has(`${rule.event}\0${rule.command}`));
12578
+ if (allPresent && desired.length > 0) {
12579
+ onWrite(`Already installed ${filePath}`);
12580
+ return;
12581
+ }
12582
+ }
12583
+ await mkdir6(pathMod.dirname(filePath), { recursive: true });
12584
+ await writeFile5(filePath, nextText, "utf8");
12585
+ onWrite(`Installed ${filePath}`);
12586
+ }
12587
+ async function uninstallHooksToml(filePath, content, { dryRun, onWrite }) {
12588
+ const existingText = await readTextIfExists(filePath);
12589
+ if (existingText === null) {
12590
+ onWrite(`Already uninstalled ${filePath}`);
12591
+ return;
12592
+ }
12593
+ const commands = Array.from(new Set(
12594
+ (Array.isArray(content.hooks) ? content.hooks : []).map((rule) => rule.command).filter((cmd) => typeof cmd === "string" && cmd.length > 0)
12595
+ ));
12596
+ if (commands.length === 0) {
12597
+ onWrite(`Already uninstalled ${filePath}`);
12598
+ return;
12599
+ }
12600
+ const nextText = removeTomlHookRulesByCommand(existingText, commands);
12601
+ if (nextText === existingText) {
12602
+ onWrite(`Already uninstalled ${filePath}`);
12603
+ return;
12604
+ }
12605
+ if (dryRun) {
12606
+ onWrite(`Would uninstall ${filePath}`);
12607
+ return;
12608
+ }
12609
+ const { writeFile: writeFile5 } = await import("node:fs/promises");
12610
+ await writeFile5(filePath, nextText, "utf8");
12611
+ onWrite(`Uninstalled ${filePath}`);
12612
+ }
12613
+ function collectHookCommandsFromJsonContent(content) {
12614
+ const commands = /* @__PURE__ */ new Set();
12615
+ const walkGroups = (groups) => {
12616
+ if (!Array.isArray(groups)) {
12617
+ return;
12618
+ }
12619
+ for (const group of groups) {
12620
+ if (!isPlainObject(group) || !Array.isArray(group.hooks)) {
12621
+ continue;
12622
+ }
12623
+ for (const hook of group.hooks) {
12624
+ if (isPlainObject(hook) && typeof hook.command === "string" && hook.command) {
12625
+ commands.add(hook.command);
12626
+ }
12627
+ }
12628
+ }
12629
+ };
12630
+ if (isPlainObject(content.hooks)) {
12631
+ for (const groups of Object.values(content.hooks)) {
12632
+ walkGroups(groups);
12633
+ }
12634
+ }
12635
+ for (const [key, value] of Object.entries(content)) {
12636
+ if (key === "hooks" || key === "enable_json_hooks" || !isPlainObject(value)) {
12637
+ continue;
12638
+ }
12639
+ for (const groups of Object.values(value)) {
12640
+ walkGroups(groups);
12641
+ }
12642
+ }
12643
+ return [...commands];
12644
+ }
12645
+ function stripHookCommandsFromGroups(groups, commands) {
12646
+ if (!Array.isArray(groups)) {
12647
+ return groups;
12648
+ }
12649
+ return groups.map((group) => {
12650
+ if (!isPlainObject(group) || !Array.isArray(group.hooks)) {
12651
+ return group;
12652
+ }
12653
+ const nextHooks = group.hooks.filter(
12654
+ (hook) => !(isPlainObject(hook) && typeof hook.command === "string" && commands.has(hook.command))
12655
+ );
12656
+ if (nextHooks.length === 0) {
12657
+ return null;
12658
+ }
12659
+ return { ...group, hooks: nextHooks };
12660
+ }).filter(Boolean);
12661
+ }
12662
+ async function uninstallHooksJson(filePath, content, { dryRun, onWrite }) {
12663
+ const existingText = await readTextIfExists(filePath);
12664
+ if (existingText === null) {
12665
+ onWrite(`Already uninstalled ${filePath}`);
12666
+ return;
12667
+ }
12668
+ let existing;
12669
+ try {
12670
+ existing = JSON.parse(existingText);
12671
+ } catch {
12672
+ onWrite(`Skipped non-JSON file ${filePath}`);
12673
+ return;
12674
+ }
12675
+ if (!isPlainObject(existing)) {
12676
+ onWrite(`Skipped non-object JSON file ${filePath}`);
12677
+ return;
12678
+ }
12679
+ const commands = new Set(collectHookCommandsFromJsonContent(content));
12680
+ if (commands.size === 0) {
12681
+ onWrite(`Already uninstalled ${filePath}`);
12682
+ return;
12683
+ }
12684
+ const next = structuredClone(existing);
12685
+ let changed = false;
12686
+ if (isPlainObject(next.hooks)) {
12687
+ for (const [event, groups] of Object.entries(next.hooks)) {
12688
+ const stripped = stripHookCommandsFromGroups(groups, commands);
12689
+ if (JSON.stringify(stripped) !== JSON.stringify(groups)) {
12690
+ changed = true;
12691
+ if (Array.isArray(stripped) && stripped.length === 0) {
12692
+ delete next.hooks[event];
12693
+ } else {
12694
+ next.hooks[event] = stripped;
12695
+ }
12696
+ }
12697
+ }
12698
+ if (isPlainObject(next.hooks) && Object.keys(next.hooks).length === 0) {
12699
+ delete next.hooks;
12700
+ changed = true;
12701
+ }
12702
+ }
12703
+ for (const [key, value] of Object.entries(next)) {
12704
+ if (key === "hooks" || key === "enable_json_hooks" || !isPlainObject(value)) {
12705
+ continue;
12706
+ }
12707
+ if (!Object.prototype.hasOwnProperty.call(content, key)) {
12708
+ continue;
12709
+ }
12710
+ for (const [event, groups] of Object.entries(value)) {
12711
+ const stripped = stripHookCommandsFromGroups(groups, commands);
12712
+ if (JSON.stringify(stripped) !== JSON.stringify(groups)) {
12713
+ changed = true;
12714
+ if (Array.isArray(stripped) && stripped.length === 0) {
12715
+ delete value[event];
12716
+ } else {
12717
+ value[event] = stripped;
12718
+ }
12719
+ }
12720
+ }
12721
+ if (Object.keys(value).length === 0) {
12722
+ delete next[key];
12723
+ changed = true;
12724
+ }
12725
+ }
12726
+ if (Object.prototype.hasOwnProperty.call(content, "enable_json_hooks") && next.enable_json_hooks === true) {
12727
+ const stillHasNamedHooks = Object.entries(next).some(
12728
+ ([key, value]) => key !== "hooks" && key !== "enable_json_hooks" && isPlainObject(value) && Object.values(value).some((groups) => Array.isArray(groups) && groups.length > 0)
12729
+ );
12730
+ if (!stillHasNamedHooks) {
12731
+ delete next.enable_json_hooks;
12732
+ changed = true;
12733
+ }
12734
+ }
12735
+ if (!changed) {
12736
+ onWrite(`Already uninstalled ${filePath}`);
12737
+ return;
12738
+ }
12739
+ if (dryRun) {
12740
+ onWrite(`Would uninstall ${filePath}`);
12741
+ return;
12742
+ }
12743
+ const { writeFile: writeFile5 } = await import("node:fs/promises");
12744
+ await writeFile5(filePath, `${JSON.stringify(next, null, 2)}
12745
+ `, "utf8");
12746
+ onWrite(`Uninstalled ${filePath}`);
12747
+ }
12748
+ async function uninstallGeneratedFile(filePath, { dryRun, onWrite }) {
12749
+ const existingText = await readTextIfExists(filePath);
12750
+ if (existingText === null) {
12751
+ onWrite(`Already uninstalled ${filePath}`);
12752
+ return;
12753
+ }
12754
+ if (!existingText.includes(GENERATED_MARKER) && !existingText.includes("Generated by codetime.")) {
12755
+ onWrite(`Skipped non-vibetime file ${filePath}`);
12756
+ return;
12757
+ }
12758
+ if (dryRun) {
12759
+ onWrite(`Would uninstall ${filePath}`);
12760
+ return;
12761
+ }
12762
+ const { unlink } = await import("node:fs/promises");
12763
+ await unlink(filePath);
12764
+ onWrite(`Uninstalled ${filePath}`);
12765
+ }
11897
12766
 
11898
12767
  // src/lib/config.ts
11899
12768
  import { randomUUID } from "node:crypto";
11900
12769
  import { existsSync as existsSync2, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
11901
12770
  import { homedir, hostname } from "node:os";
11902
- import path22 from "node:path";
12771
+ import path23 from "node:path";
11903
12772
  function configDir(home = homedir()) {
11904
- return path22.join(home, ".vibetime");
12773
+ return path23.join(home, ".vibetime");
11905
12774
  }
11906
12775
  function configPath(home = homedir()) {
11907
- return path22.join(configDir(home), "config.json");
12776
+ return path23.join(configDir(home), "config.json");
11908
12777
  }
11909
12778
  function machineIdPath(home = homedir()) {
11910
- return path22.join(configDir(home), "machine-id");
12779
+ return path23.join(configDir(home), "machine-id");
11911
12780
  }
11912
12781
  function readConfig(home = homedir()) {
11913
12782
  const file = configPath(home);
@@ -11956,13 +12825,13 @@ init_fs();
11956
12825
  // src/lib/logger.ts
11957
12826
  import { appendFile, mkdir as mkdir4, rename, stat as stat12 } from "node:fs/promises";
11958
12827
  import { homedir as homedir2 } from "node:os";
11959
- import path23 from "node:path";
12828
+ import path24 from "node:path";
11960
12829
  var MAX_BYTES = 1 * 1024 * 1024;
11961
12830
  function logDir(home = homedir2()) {
11962
- return path23.join(home, ".vibetime", "logs");
12831
+ return path24.join(home, ".vibetime", "logs");
11963
12832
  }
11964
12833
  function logPath(home = homedir2(), name = "cli.log") {
11965
- return path23.join(logDir(home), name);
12834
+ return path24.join(logDir(home), name);
11966
12835
  }
11967
12836
  function serializeError(error) {
11968
12837
  if (error instanceof Error) {
@@ -12137,8 +13006,8 @@ function buildHeaders(token, machine) {
12137
13006
  ...machine?.platform ? { "x-machine-platform": machine.platform } : {}
12138
13007
  };
12139
13008
  }
12140
- function joinUrl(base, path25) {
12141
- return new URL(path25, base.endsWith("/") ? base : `${base}/`).toString();
13009
+ function joinUrl(base, path26) {
13010
+ return new URL(path26, base.endsWith("/") ? base : `${base}/`).toString();
12142
13011
  }
12143
13012
  async function postRollupBatch(remote, rollups, options = {}) {
12144
13013
  const response = await remote.fetchImpl(joinUrl(remote.baseUrl, "/v3/agent/ingest"), {
@@ -12228,6 +13097,7 @@ function createRegistry() {
12228
13097
  registry.register(createZCodeAdapter());
12229
13098
  registry.register(createGrokBuildAdapter());
12230
13099
  registry.register(createZedAdapter());
13100
+ registry.register(createKimiCodeAdapter());
12231
13101
  return registry;
12232
13102
  }
12233
13103
  var defaultContext = {
@@ -12285,6 +13155,7 @@ function createCli(ctx, registry) {
12285
13155
  });
12286
13156
  cli.command("detect", "Show supported local targets and install status").action((options) => detectCommand(normalizeOptions(options), ctx, registry).then(() => 0));
12287
13157
  cli.command("install", "Install integration files into detected or requested targets").option("--target <targets>", "Target integrations, comma-separated").option("--targets <targets>", "Target integrations, comma-separated").option("--all", "Install all supported integrations").option("--force", "Overwrite existing non-generated files when needed").action((options) => installCommand(normalizeOptions(options), ctx, registry));
13158
+ cli.command("uninstall", "Remove vibetime integration hooks/files from targets").option("--target <targets>", "Target integrations, comma-separated").option("--targets <targets>", "Target integrations, comma-separated").option("--all", "Uninstall all supported integrations").action((options) => uninstallCommand(normalizeOptions(options), ctx, registry));
12288
13159
  cli.command("upgrade", "Check for updates and upgrade to the latest version").option("--check", "Only check for updates, do not install").action((options) => upgradeCommand(normalizeOptions(options), ctx, registry));
12289
13160
  cli.command("hook", "Read agent hook JSON from stdin and report a throttled event").option("--agent <name>", "Agent name").option("--project <name>", "Project name").option("--min-interval <seconds>", "Minimum seconds between similar hook reports").action((options) => hookCommand(normalizeOptions(options), ctx));
12290
13161
  cli.command("sync-local-trigger", "Trigger one background local sync with throttle and locking").option("--min-interval <seconds>", "Minimum seconds between sync triggers").action((options) => syncLocalTriggerCommand(normalizeOptions(options), ctx, registry));
@@ -12381,6 +13252,44 @@ async function installCommand(options, ctx, registry) {
12381
13252
  }
12382
13253
  return 0;
12383
13254
  }
13255
+ async function uninstallCommand(options, ctx, registry) {
13256
+ const home = resolveHome3(options, ctx);
13257
+ const env = ctx.env;
13258
+ const dryRun = Boolean(options["dry-run"]);
13259
+ const allAdapters = registry.all();
13260
+ const requested = requestedTargets(options);
13261
+ const unknown = requested.filter((id) => !allAdapters.some((a) => a.id === id));
13262
+ if (unknown.length > 0) {
13263
+ throw new Error(`Unknown target(s): ${unknown.join(", ")}`);
13264
+ }
13265
+ const installed = [];
13266
+ for (const adapter of allAdapters) {
13267
+ if (await adapter.isInstalled(home, env)) {
13268
+ installed.push(adapter.id);
13269
+ }
13270
+ }
13271
+ const selectedIds = requested.length > 0 ? requested : options.all ? allAdapters.map((a) => a.id) : installed;
13272
+ if (selectedIds.length === 0) {
13273
+ write(ctx.stderr, "No installed vibetime integrations were found. Use --target <id> or --all.\n");
13274
+ return 1;
13275
+ }
13276
+ for (const adapter of allAdapters.filter((a) => selectedIds.includes(a.id))) {
13277
+ const entries = adapter.installEntries(home, env);
13278
+ if (entries.length === 0) {
13279
+ write(ctx.stdout, `Nothing to uninstall for ${adapter.id}
13280
+ `);
13281
+ continue;
13282
+ }
13283
+ for (const entry of entries) {
13284
+ await uninstallEntry(entry, {
13285
+ dryRun,
13286
+ onWrite: (msg) => write(ctx.stdout, `${msg}
13287
+ `)
13288
+ });
13289
+ }
13290
+ }
13291
+ return 0;
13292
+ }
12384
13293
  var NPM_PACKAGE = "@yhong91/vibetime";
12385
13294
  async function fetchLatestVersion() {
12386
13295
  try {
@@ -13123,13 +14032,13 @@ function selectBackfillFilesForImport(files, watermarkTs) {
13123
14032
  return picked;
13124
14033
  }
13125
14034
  function backfillIncrementalStatePath(home) {
13126
- return path24.join(home, ".vibetime", "backfill-state.json");
14035
+ return path25.join(home, ".vibetime", "backfill-state.json");
13127
14036
  }
13128
14037
  function syncLocalTriggerStatePath(home) {
13129
- return path24.join(home, ".vibetime", "sync-local-trigger.json");
14038
+ return path25.join(home, ".vibetime", "sync-local-trigger.json");
13130
14039
  }
13131
14040
  function syncLocalTriggerLockPath(home) {
13132
- return path24.join(home, ".vibetime", "sync-local-trigger.lock");
14041
+ return path25.join(home, ".vibetime", "sync-local-trigger.lock");
13133
14042
  }
13134
14043
  function backfillRemoteKey(baseUrl) {
13135
14044
  try {
@@ -13191,7 +14100,7 @@ async function readBackfillIncrementalStateFile(home, ctx) {
13191
14100
  }
13192
14101
  async function writeBackfillIncrementalStateFile(home, file) {
13193
14102
  const statePath = backfillIncrementalStatePath(home);
13194
- await mkdir5(path24.dirname(statePath), { recursive: true });
14103
+ await mkdir5(path25.dirname(statePath), { recursive: true });
13195
14104
  await writeFile4(statePath, `${JSON.stringify(file, null, 2)}
13196
14105
  `, "utf8");
13197
14106
  }
@@ -13240,7 +14149,7 @@ async function readSyncLocalTriggerState(statePath) {
13240
14149
  return nextState;
13241
14150
  }
13242
14151
  async function writeSyncLocalTriggerState(statePath, state) {
13243
- await mkdir5(path24.dirname(statePath), { recursive: true });
14152
+ await mkdir5(path25.dirname(statePath), { recursive: true });
13244
14153
  await writeFile4(statePath, `${JSON.stringify(state, null, 2)}
13245
14154
  `, "utf8");
13246
14155
  }
@@ -13255,12 +14164,12 @@ async function readSyncLocalLock(lockPath) {
13255
14164
  return { pid: lock.pid, startedAt: lock.startedAt };
13256
14165
  }
13257
14166
  async function writeSyncLocalLock(lockPath, lock) {
13258
- await mkdir5(path24.dirname(lockPath), { recursive: true });
14167
+ await mkdir5(path25.dirname(lockPath), { recursive: true });
13259
14168
  await writeFile4(lockPath, `${JSON.stringify(lock, null, 2)}
13260
14169
  `, "utf8");
13261
14170
  }
13262
14171
  async function acquireSyncLocalLock(lockPath, lock) {
13263
- await mkdir5(path24.dirname(lockPath), { recursive: true });
14172
+ await mkdir5(path25.dirname(lockPath), { recursive: true });
13264
14173
  try {
13265
14174
  const handle = await open(lockPath, "wx");
13266
14175
  try {
@@ -13340,10 +14249,10 @@ function syncLocalRunnerEntryArgs(cliPath) {
13340
14249
  if (cliPath.endsWith(".ts")) {
13341
14250
  return ["--import", "tsx", cliPath];
13342
14251
  }
13343
- return [path24.resolve(path24.dirname(cliPath), "../bin/vibetime.mjs")];
14252
+ return [path25.resolve(path25.dirname(cliPath), "../bin/vibetime.mjs")];
13344
14253
  }
13345
14254
  function resolveHome3(options, ctx) {
13346
- return path24.resolve(stringOption(options.home) || ctx.env.HOME || os11.homedir());
14255
+ return path25.resolve(stringOption(options.home) || ctx.env.HOME || os11.homedir());
13347
14256
  }
13348
14257
  function requestedTargets(options) {
13349
14258
  const value = options.target || options.targets;
@@ -13514,6 +14423,7 @@ function helpText() {
13514
14423
  Usage:
13515
14424
  vibetime detect [--json] [--home <path>]
13516
14425
  vibetime install [--target codex,claude,opencode,pi] [--all] [--dry-run] [--force] [--home <path>]
14426
+ vibetime uninstall [--target codex,claude,opencode,pi] [--all] [--dry-run] [--home <path>]
13517
14427
  vibetime upgrade [--check]
13518
14428
  vibetime hook --agent <name>
13519
14429
  vibetime backfill discover|plan|import|verify --source codex|claude-code|opencode|pi|all --dry-run [--json] [--batch-size <count>] [--force [--purge-all]]
@@ -13528,6 +14438,7 @@ Setup:
13528
14438
  Commands:
13529
14439
  detect Show supported local targets and install status.
13530
14440
  install Install integration files into detected or requested targets.
14441
+ uninstall Remove vibetime hooks/files from detected or requested targets.
13531
14442
  upgrade Check for updates and upgrade to the latest version.
13532
14443
  hook Read agent hook JSON from stdin and report a throttled event.
13533
14444
  backfill Discover local history and create metadata-only import plans.