@yhong91/vibetime 0.1.40 → 0.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/vibetime.mjs +143 -17
- package/package.json +1 -1
package/bin/vibetime.mjs
CHANGED
|
@@ -1928,7 +1928,7 @@ function countTextLines(text) {
|
|
|
1928
1928
|
}
|
|
1929
1929
|
|
|
1930
1930
|
// src/lib/constants.ts
|
|
1931
|
-
var PACKAGE_VERSION = true ? "0.1.
|
|
1931
|
+
var PACKAGE_VERSION = true ? "0.1.41" : "0.1.1";
|
|
1932
1932
|
var DEFAULT_API_URL = "http://121.196.224.82:3001";
|
|
1933
1933
|
var DEFAULT_BACKFILL_BATCH_SIZE = 50;
|
|
1934
1934
|
var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
|
|
@@ -3308,7 +3308,11 @@ async function isHooksJsonInstalled(filePath, command) {
|
|
|
3308
3308
|
}
|
|
3309
3309
|
|
|
3310
3310
|
// src/adapters/claude-code.ts
|
|
3311
|
+
init_fs();
|
|
3311
3312
|
async function parseClaudeCodeSessionFile(filePath, options) {
|
|
3313
|
+
if (path7.basename(filePath) === "usage.jsonl" && path7.basename(path7.dirname(filePath)) === ".viberouter") {
|
|
3314
|
+
return parseClaudeRouterUsageFile(filePath, options);
|
|
3315
|
+
}
|
|
3312
3316
|
const text = await readFile3(filePath, "utf8");
|
|
3313
3317
|
const lines = text.split("\n").filter(Boolean);
|
|
3314
3318
|
const projectContext = await claudeProjectContextFromLines(filePath, lines, options);
|
|
@@ -3575,6 +3579,86 @@ async function parseClaudeCodeSessionFile(filePath, options) {
|
|
|
3575
3579
|
}
|
|
3576
3580
|
return state.events.filter((event) => validateCanonicalEvent(event).valid);
|
|
3577
3581
|
}
|
|
3582
|
+
async function parseClaudeRouterUsageFile(filePath, options) {
|
|
3583
|
+
const rows = (await readFile3(filePath, "utf8")).split("\n").map(parseJsonLine).filter((row) => Boolean(
|
|
3584
|
+
row && row.surface === "claude" && (numberField(row, "timestamp") ?? 0) > 0 && Object.keys(objectField(row, "usage")).length > 0
|
|
3585
|
+
));
|
|
3586
|
+
if (rows.length === 0) {
|
|
3587
|
+
return [];
|
|
3588
|
+
}
|
|
3589
|
+
const timestamps = rows.map((row) => numberField(row, "timestamp") ?? 0);
|
|
3590
|
+
const firstTs = Math.min(...timestamps) - 6e4;
|
|
3591
|
+
const lastTs = Math.max(...timestamps) + 6e4;
|
|
3592
|
+
const home = path7.resolve(stringOption(options.home) || path7.dirname(path7.dirname(filePath)));
|
|
3593
|
+
const transcriptCounts = /* @__PURE__ */ new Map();
|
|
3594
|
+
for (const transcript of await listJsonlFiles(path7.join(claudeConfigDir(home, process.env), "projects"))) {
|
|
3595
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3596
|
+
for (const line of (await readFile3(transcript, "utf8")).split("\n")) {
|
|
3597
|
+
const raw = parseJsonLine(line);
|
|
3598
|
+
const ts = raw ? Date.parse(timestampFrom(raw.timestamp) || "") : Number.NaN;
|
|
3599
|
+
if (!raw || stringField(raw, "type") !== "assistant" || ts < firstTs || ts > lastTs) {
|
|
3600
|
+
continue;
|
|
3601
|
+
}
|
|
3602
|
+
const message = objectField(raw, "message");
|
|
3603
|
+
const messageId = stringField(message, "id");
|
|
3604
|
+
const usageKey = messageId ? `${messageId}:${stringField(raw, "requestId")}` : null;
|
|
3605
|
+
if (usageKey && seen.has(usageKey)) {
|
|
3606
|
+
continue;
|
|
3607
|
+
}
|
|
3608
|
+
if (usageKey) {
|
|
3609
|
+
seen.add(usageKey);
|
|
3610
|
+
}
|
|
3611
|
+
const usage = claudeUsageFromMessage(message);
|
|
3612
|
+
if (!usage) {
|
|
3613
|
+
continue;
|
|
3614
|
+
}
|
|
3615
|
+
const key = `${usage.tokensInput || 0}:${usage.tokensOutput || 0}`;
|
|
3616
|
+
transcriptCounts.set(key, (transcriptCounts.get(key) || 0) + 1);
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
const sourcePathHash = `sha256:${createStableHash(filePath)}`;
|
|
3620
|
+
const events = [];
|
|
3621
|
+
for (const row of rows) {
|
|
3622
|
+
const usage = objectField(row, "usage");
|
|
3623
|
+
const input = numberField(usage, "inputTokens") || 0;
|
|
3624
|
+
const output = numberField(usage, "outputTokens") || 0;
|
|
3625
|
+
const key = `${input}:${output}`;
|
|
3626
|
+
const matched = transcriptCounts.get(key) || 0;
|
|
3627
|
+
if (matched > 0) {
|
|
3628
|
+
transcriptCounts.set(key, matched - 1);
|
|
3629
|
+
continue;
|
|
3630
|
+
}
|
|
3631
|
+
const timestamp = numberField(row, "timestamp") ?? 0;
|
|
3632
|
+
const requestId = stringField(row, "requestId") || createStableHash(row).slice(0, 24);
|
|
3633
|
+
const cacheCreation = numberField(usage, "cacheCreationInputTokens") || 0;
|
|
3634
|
+
const cacheRead = numberField(usage, "cacheReadInputTokens") || 0;
|
|
3635
|
+
const cached = numberField(usage, "cachedInputTokens") || cacheCreation + cacheRead;
|
|
3636
|
+
const event = baseClaudeEvent({
|
|
3637
|
+
ts: new Date(timestamp).toISOString(),
|
|
3638
|
+
type: "model.usage",
|
|
3639
|
+
sessionId: `viberouter:${new Date(timestamp).toISOString().slice(0, 10)}`,
|
|
3640
|
+
project: "unknown",
|
|
3641
|
+
model: stringField(row, "resolvedModel") || stringField(row, "requestedModel") || stringField(row, "model"),
|
|
3642
|
+
provider: stringField(row, "provider"),
|
|
3643
|
+
confidence: "exact",
|
|
3644
|
+
metrics: {
|
|
3645
|
+
tokensInput: input || void 0,
|
|
3646
|
+
tokensCachedInput: cached || void 0,
|
|
3647
|
+
tokensCacheCreationInput: cacheCreation || void 0,
|
|
3648
|
+
tokensCacheReadInput: cacheRead || void 0,
|
|
3649
|
+
tokensOutput: output || void 0,
|
|
3650
|
+
tokensReasoningOutput: numberField(usage, "reasoningOutputTokens") || void 0,
|
|
3651
|
+
tokensTotal: numberField(usage, "totalTokens") || input + output || void 0,
|
|
3652
|
+
modelCalls: 1
|
|
3653
|
+
},
|
|
3654
|
+
refs: stringRefs({ sourceId: requestId, sourcePathHash, importKey: `claude-code:viberouter:${requestId}` })
|
|
3655
|
+
});
|
|
3656
|
+
if (validateCanonicalEvent(event).valid) {
|
|
3657
|
+
events.push(event);
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
return events;
|
|
3661
|
+
}
|
|
3578
3662
|
function baseClaudeEvent(event) {
|
|
3579
3663
|
return {
|
|
3580
3664
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
@@ -3911,6 +3995,7 @@ function createClaudeCodeAdapter() {
|
|
|
3911
3995
|
const base = claudeConfigDir(home, env);
|
|
3912
3996
|
return [
|
|
3913
3997
|
path7.join(base, "projects"),
|
|
3998
|
+
path7.join(home, ".viberouter", "usage.jsonl"),
|
|
3914
3999
|
path7.join(base, ".claude.json"),
|
|
3915
4000
|
path7.join(home, ".claude.json")
|
|
3916
4001
|
];
|
|
@@ -7491,6 +7576,11 @@ function createPiAdapter() {
|
|
|
7491
7576
|
};
|
|
7492
7577
|
}
|
|
7493
7578
|
|
|
7579
|
+
// src/adapters/qoder-cn.ts
|
|
7580
|
+
import { readdir as readdir7, readFile as readFile10, stat as stat8 } from "node:fs/promises";
|
|
7581
|
+
import os8 from "node:os";
|
|
7582
|
+
import path17 from "node:path";
|
|
7583
|
+
|
|
7494
7584
|
// src/adapters/qoder-local-db.ts
|
|
7495
7585
|
import { access } from "node:fs/promises";
|
|
7496
7586
|
import os7 from "node:os";
|
|
@@ -7537,6 +7627,7 @@ async function loadQoderDbModelCalls(appDirName, sessionId, modelMap) {
|
|
|
7537
7627
|
}
|
|
7538
7628
|
const db = new DatabaseSync(dbPath, { readOnly: true });
|
|
7539
7629
|
try {
|
|
7630
|
+
calls.rootSessionId = resolveRootSessionId(db, sessionId);
|
|
7540
7631
|
const preferredModel = resolveSessionPreferredModel(db, sessionId, modelMap);
|
|
7541
7632
|
const rows = db.prepare(
|
|
7542
7633
|
`select request_id, token_info, model_info from chat_message where session_id = ? and role = 'assistant' and token_info != '' order by gmt_create asc`
|
|
@@ -7570,6 +7661,18 @@ async function loadQoderDbModelCalls(appDirName, sessionId, modelMap) {
|
|
|
7570
7661
|
}
|
|
7571
7662
|
return calls;
|
|
7572
7663
|
}
|
|
7664
|
+
function resolveRootSessionId(db, sessionId) {
|
|
7665
|
+
let current = sessionId;
|
|
7666
|
+
for (let depth = 0; depth < 5; depth += 1) {
|
|
7667
|
+
const row = db.prepare("select parent_session_id from chat_session where session_id = ?").get(current);
|
|
7668
|
+
const parent = row ? stringField(row, "parent_session_id") : void 0;
|
|
7669
|
+
if (!parent) {
|
|
7670
|
+
return current === sessionId ? void 0 : current;
|
|
7671
|
+
}
|
|
7672
|
+
current = parent;
|
|
7673
|
+
}
|
|
7674
|
+
return current === sessionId ? void 0 : current;
|
|
7675
|
+
}
|
|
7573
7676
|
function resolveSessionPreferredModel(db, sessionId, modelMap) {
|
|
7574
7677
|
let current = sessionId;
|
|
7575
7678
|
for (let depth = 0; depth < 5 && current; depth++) {
|
|
@@ -7595,9 +7698,6 @@ function resolveSessionPreferredModel(db, sessionId, modelMap) {
|
|
|
7595
7698
|
}
|
|
7596
7699
|
|
|
7597
7700
|
// src/adapters/qoder-cn.ts
|
|
7598
|
-
import { readdir as readdir7, readFile as readFile10, stat as stat8 } from "node:fs/promises";
|
|
7599
|
-
import os8 from "node:os";
|
|
7600
|
-
import path17 from "node:path";
|
|
7601
7701
|
function parseQoderCnPaths(filePath) {
|
|
7602
7702
|
const parts = filePath.split(path17.sep);
|
|
7603
7703
|
const subagentsIdx = parts.lastIndexOf("subagents");
|
|
@@ -7715,7 +7815,8 @@ async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMa
|
|
|
7715
7815
|
async function parseQoderCnSessionFile(filePath, options) {
|
|
7716
7816
|
const text = await readFile10(filePath, "utf8");
|
|
7717
7817
|
const lines = text.split("\n").filter(Boolean);
|
|
7718
|
-
const
|
|
7818
|
+
const parsedPaths = parseQoderCnPaths(filePath);
|
|
7819
|
+
const { configDir: configDir2 } = parsedPaths;
|
|
7719
7820
|
const projectContext = await qoderCnProjectContextFromLines(filePath, lines, options, configDir2);
|
|
7720
7821
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
7721
7822
|
const seenUsageKeys = /* @__PURE__ */ new Set();
|
|
@@ -8043,6 +8144,16 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8043
8144
|
});
|
|
8044
8145
|
}
|
|
8045
8146
|
}
|
|
8147
|
+
dbModelCalls ??= await loadQoderDbModelCalls("QoderCN", parsedPaths.sessionId, modelMap);
|
|
8148
|
+
if (dbModelCalls.rootSessionId) {
|
|
8149
|
+
const parentPath = path17.join(path17.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
|
|
8150
|
+
const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
|
|
8151
|
+
return validEvents.map((event) => rebuildEventIdentity2({
|
|
8152
|
+
...event,
|
|
8153
|
+
sessionId: dbModelCalls.rootSessionId,
|
|
8154
|
+
refs: { ...event.refs, sourcePathHash: parentSourcePathHash }
|
|
8155
|
+
}));
|
|
8156
|
+
}
|
|
8046
8157
|
return validEvents;
|
|
8047
8158
|
}
|
|
8048
8159
|
function baseQoderCnEvent(event) {
|
|
@@ -8530,7 +8641,8 @@ async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap)
|
|
|
8530
8641
|
async function parseQoderSessionFile(filePath, options) {
|
|
8531
8642
|
const text = await readFile11(filePath, "utf8");
|
|
8532
8643
|
const lines = text.split("\n").filter(Boolean);
|
|
8533
|
-
const
|
|
8644
|
+
const parsedPaths = parseQoderPaths(filePath);
|
|
8645
|
+
const { configDir: configDir2 } = parsedPaths;
|
|
8534
8646
|
const projectContext = await qoderProjectContextFromLines(filePath, lines, options, configDir2);
|
|
8535
8647
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
8536
8648
|
const seenUsageKeys = /* @__PURE__ */ new Set();
|
|
@@ -8858,6 +8970,16 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
8858
8970
|
});
|
|
8859
8971
|
}
|
|
8860
8972
|
}
|
|
8973
|
+
dbModelCalls ??= await loadQoderDbModelCalls("Qoder", parsedPaths.sessionId, modelMap);
|
|
8974
|
+
if (dbModelCalls.rootSessionId) {
|
|
8975
|
+
const parentPath = path18.join(path18.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
|
|
8976
|
+
const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
|
|
8977
|
+
return validEvents.map((event) => rebuildEventIdentity3({
|
|
8978
|
+
...event,
|
|
8979
|
+
sessionId: dbModelCalls.rootSessionId,
|
|
8980
|
+
refs: { ...event.refs, sourcePathHash: parentSourcePathHash }
|
|
8981
|
+
}));
|
|
8982
|
+
}
|
|
8861
8983
|
return validEvents;
|
|
8862
8984
|
}
|
|
8863
8985
|
function baseQoderEvent(event) {
|
|
@@ -9893,7 +10015,7 @@ function modelMetrics(row) {
|
|
|
9893
10015
|
toolCalls: numberField(row, "tool_call_count")
|
|
9894
10016
|
};
|
|
9895
10017
|
}
|
|
9896
|
-
function
|
|
10018
|
+
function resolveRootSessionId2(sessionId, sessions) {
|
|
9897
10019
|
let current = sessionId;
|
|
9898
10020
|
const visited = /* @__PURE__ */ new Set();
|
|
9899
10021
|
while (true) {
|
|
@@ -9915,7 +10037,7 @@ function sessionContext(row, sessions) {
|
|
|
9915
10037
|
const session = sessions.get(sessionId);
|
|
9916
10038
|
const cwd = stringField(session, "directory");
|
|
9917
10039
|
const project = projectFromDirectory(cwd);
|
|
9918
|
-
const rootSessionId =
|
|
10040
|
+
const rootSessionId = resolveRootSessionId2(sessionId, sessions);
|
|
9919
10041
|
return {
|
|
9920
10042
|
rawSessionId: sessionId,
|
|
9921
10043
|
sessionId: `zcode:${rootSessionId}`,
|
|
@@ -11289,9 +11411,10 @@ async function postRollupBatch(remote, rollups, options = {}) {
|
|
|
11289
11411
|
failed: 0
|
|
11290
11412
|
};
|
|
11291
11413
|
}
|
|
11292
|
-
async function deleteRollupsBySource(remote, source, machine) {
|
|
11414
|
+
async function deleteRollupsBySource(remote, source, machine, options = {}) {
|
|
11415
|
+
const query = `source=${encodeURIComponent(source)}${options.preserveTokens ? "&preserveTokens=1" : ""}`;
|
|
11293
11416
|
const response = await remote.fetchImpl(
|
|
11294
|
-
joinUrl(remote.baseUrl, `/v3/agent/sessions
|
|
11417
|
+
joinUrl(remote.baseUrl, `/v3/agent/sessions?${query}`),
|
|
11295
11418
|
{ method: "DELETE", headers: buildHeaders(remote.token, machine) }
|
|
11296
11419
|
);
|
|
11297
11420
|
if (!response.ok) {
|
|
@@ -11414,7 +11537,7 @@ function createCli(ctx, registry) {
|
|
|
11414
11537
|
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));
|
|
11415
11538
|
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));
|
|
11416
11539
|
cli.command("sync-local-runner", "Internal background local sync runner").option("--lock-file <path>", "Lock file for the active sync").option("--state-file <path>", "State file for trigger metadata").action((options) => syncLocalRunnerCommand(normalizeOptions(options), ctx, registry));
|
|
11417
|
-
cli.command("backfill [action]", "Inspect local history import candidates").option("--source <source>", "Backfill source").option("--since <time>", "Only include history after this time").option("--until <time>", "Only include history before this time").option("--project <name>", "Project filter").option("--source-root <path>", "Override source history root").option("--include-source-path", "Include local source paths in output").option("--import-run <id>", "Import run id for verify/resume workflows").option("--limit <count>", "Maximum session files to parse").option("--batch-size <count>", "Max rollups per request (also bounded by --batch-bytes)").option("--batch-bytes <bytes>", "Soft byte cap for the JSON body of a single ingest POST").option("--replace", "Replace conflicting records during import (default)").option("--skip-conflicts", "Skip conflicting records instead of replacing them").option("--force", "Force full re-import: clear watermark and re-process all files").action((action, options) => backfillCommand({ ...normalizeOptions(options), action }, ctx, registry));
|
|
11540
|
+
cli.command("backfill [action]", "Inspect local history import candidates").option("--source <source>", "Backfill source").option("--since <time>", "Only include history after this time").option("--until <time>", "Only include history before this time").option("--project <name>", "Project filter").option("--source-root <path>", "Override source history root").option("--include-source-path", "Include local source paths in output").option("--import-run <id>", "Import run id for verify/resume workflows").option("--limit <count>", "Maximum session files to parse").option("--batch-size <count>", "Max rollups per request (also bounded by --batch-bytes)").option("--batch-bytes <bytes>", "Soft byte cap for the JSON body of a single ingest POST").option("--replace", "Replace conflicting records during import (default)").option("--skip-conflicts", "Skip conflicting records instead of replacing them").option("--force", "Force full re-import: clear watermark and re-process all files (keeps server rollups that already have tokens)").option("--purge-all", "With --force: also delete token-bearing server rollups before re-importing (DANGEROUS \u2014 tokens whose local sources were rotated away are lost forever)").action((action, options) => backfillCommand({ ...normalizeOptions(options), action }, ctx, registry));
|
|
11418
11541
|
cli.command("token [action] [value]", "Set, show, or clear the persisted API token").option("--remote <url>", "Override API base URL when setting a token").action((action, value, options) => tokenCommand(action, value, normalizeOptions(options), ctx));
|
|
11419
11542
|
cli.command("machine [action]", "List or rename machines (requires login)").option("--name <name>", "New display name (used by `machine rename`)").option("--id <id>", "Machine id (defaults to current machine)").action((action, options) => machineCommand(action, normalizeOptions(options), ctx));
|
|
11420
11543
|
return cli;
|
|
@@ -11433,7 +11556,8 @@ function normalizeOptions(options) {
|
|
|
11433
11556
|
importRun: "import-run",
|
|
11434
11557
|
batchSize: "batch-size",
|
|
11435
11558
|
batchBytes: "batch-bytes",
|
|
11436
|
-
skipConflicts: "skip-conflicts"
|
|
11559
|
+
skipConflicts: "skip-conflicts",
|
|
11560
|
+
purgeAll: "purge-all"
|
|
11437
11561
|
};
|
|
11438
11562
|
for (const [camel, dashed] of Object.entries(aliases)) {
|
|
11439
11563
|
if (normalized[camel] !== void 0 && normalized[dashed] === void 0) {
|
|
@@ -12005,11 +12129,13 @@ async function purgeForcedSources(sourceDefs, home, remoteKey, options, ctx) {
|
|
|
12005
12129
|
debug(ctx, `Failed to clear backfill watermark: ${error.message}
|
|
12006
12130
|
`);
|
|
12007
12131
|
}
|
|
12132
|
+
const preserveTokens = !options["purge-all"];
|
|
12008
12133
|
for (const item of sourceDefs) {
|
|
12009
12134
|
try {
|
|
12010
|
-
const deleted = await deleteSessionRollupsBySourceAPI(item.id, options, ctx);
|
|
12135
|
+
const deleted = await deleteSessionRollupsBySourceAPI(item.id, options, ctx, preserveTokens);
|
|
12011
12136
|
if (!options.json) {
|
|
12012
|
-
|
|
12137
|
+
const suffix = preserveTokens ? " (token-bearing rollups kept)" : "";
|
|
12138
|
+
write(ctx.stdout, `purged ${item.id}: ${deleted} old rollups${suffix}
|
|
12013
12139
|
`);
|
|
12014
12140
|
}
|
|
12015
12141
|
} catch (error) {
|
|
@@ -12139,7 +12265,7 @@ async function sendSessionRollupBatch(rollups, options, ctx) {
|
|
|
12139
12265
|
});
|
|
12140
12266
|
return result;
|
|
12141
12267
|
}
|
|
12142
|
-
async function deleteSessionRollupsBySourceAPI(source, options, ctx) {
|
|
12268
|
+
async function deleteSessionRollupsBySourceAPI(source, options, ctx, preserveTokens) {
|
|
12143
12269
|
const remote = resolveRemoteFromOptions(options, ctx);
|
|
12144
12270
|
if (!remote) {
|
|
12145
12271
|
throw new Error("No fetch available for HTTP delete");
|
|
@@ -12149,7 +12275,7 @@ async function deleteSessionRollupsBySourceAPI(source, options, ctx) {
|
|
|
12149
12275
|
id: ensureLocalMachineId(home),
|
|
12150
12276
|
hostname: defaultMachineName(),
|
|
12151
12277
|
platform: process.platform
|
|
12152
|
-
});
|
|
12278
|
+
}, { preserveTokens });
|
|
12153
12279
|
}
|
|
12154
12280
|
function shouldUseIncrementalBackfill(options) {
|
|
12155
12281
|
return !stringOption(options.since) && !stringOption(options.until) && !stringOption(options["source-root"]) && numberOption(options.limit) === void 0;
|
|
@@ -12608,7 +12734,7 @@ Usage:
|
|
|
12608
12734
|
vibetime install [--target codex,claude,opencode,pi] [--all] [--dry-run] [--force] [--home <path>]
|
|
12609
12735
|
vibetime upgrade [--check]
|
|
12610
12736
|
vibetime hook --agent <name>
|
|
12611
|
-
vibetime backfill discover|plan|import|verify --source codex|claude-code|opencode|pi|all --dry-run [--json] [--batch-size <count>]
|
|
12737
|
+
vibetime backfill discover|plan|import|verify --source codex|claude-code|opencode|pi|all --dry-run [--json] [--batch-size <count>] [--force [--purge-all]]
|
|
12612
12738
|
vibetime token set <token>
|
|
12613
12739
|
vibetime token show
|
|
12614
12740
|
vibetime token clear
|
package/package.json
CHANGED