@workscribe/cli 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +295 -92
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1402,6 +1402,42 @@ var init_session_lookup = __esm({
|
|
|
1402
1402
|
}
|
|
1403
1403
|
});
|
|
1404
1404
|
|
|
1405
|
+
// src/db/transaction.ts
|
|
1406
|
+
function isDatabaseLocked(error) {
|
|
1407
|
+
return error?.errcode === SQLITE_BUSY;
|
|
1408
|
+
}
|
|
1409
|
+
function rollbackQuietly(db) {
|
|
1410
|
+
try {
|
|
1411
|
+
db.exec("ROLLBACK");
|
|
1412
|
+
} catch {
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
function withImmediateTransaction(db, work, options = {}) {
|
|
1416
|
+
const { busyTimeoutMs } = options;
|
|
1417
|
+
if (busyTimeoutMs !== void 0) db.exec(`PRAGMA busy_timeout = ${Math.max(0, Math.floor(busyTimeoutMs))}`);
|
|
1418
|
+
try {
|
|
1419
|
+
db.exec("BEGIN IMMEDIATE");
|
|
1420
|
+
try {
|
|
1421
|
+
const result = work();
|
|
1422
|
+
db.exec("COMMIT");
|
|
1423
|
+
return result;
|
|
1424
|
+
} catch (error) {
|
|
1425
|
+
rollbackQuietly(db);
|
|
1426
|
+
throw error;
|
|
1427
|
+
}
|
|
1428
|
+
} finally {
|
|
1429
|
+
if (busyTimeoutMs !== void 0) db.exec(`PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS}`);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
var BUSY_TIMEOUT_MS, SQLITE_BUSY;
|
|
1433
|
+
var init_transaction = __esm({
|
|
1434
|
+
"src/db/transaction.ts"() {
|
|
1435
|
+
"use strict";
|
|
1436
|
+
BUSY_TIMEOUT_MS = 5e3;
|
|
1437
|
+
SQLITE_BUSY = 5;
|
|
1438
|
+
}
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1405
1441
|
// src/db/index.ts
|
|
1406
1442
|
function getDb() {
|
|
1407
1443
|
if (_db) return _db;
|
|
@@ -1419,7 +1455,7 @@ function getDb() {
|
|
|
1419
1455
|
}
|
|
1420
1456
|
db.exec("PRAGMA journal_mode = WAL");
|
|
1421
1457
|
db.exec("PRAGMA foreign_keys = ON");
|
|
1422
|
-
db.exec(
|
|
1458
|
+
db.exec(`PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS}`);
|
|
1423
1459
|
applyMigrations(db, MIGRATIONS);
|
|
1424
1460
|
_db = db;
|
|
1425
1461
|
return _db;
|
|
@@ -1475,10 +1511,30 @@ function getEventsForDate(date) {
|
|
|
1475
1511
|
const stmt = db.prepare(`
|
|
1476
1512
|
SELECT * FROM events
|
|
1477
1513
|
WHERE date(captured_at, 'localtime') = $date
|
|
1478
|
-
ORDER BY captured_at ASC
|
|
1514
|
+
ORDER BY captured_at ASC, id ASC
|
|
1479
1515
|
`);
|
|
1480
1516
|
return stmt.all({ date });
|
|
1481
1517
|
}
|
|
1518
|
+
function countEventsBetween(start, end) {
|
|
1519
|
+
const row = getDb().prepare("SELECT COUNT(*) AS count FROM events WHERE captured_at >= $start AND captured_at < $end").get({ start, end });
|
|
1520
|
+
return row.count;
|
|
1521
|
+
}
|
|
1522
|
+
function sumSessionEventCounts(date) {
|
|
1523
|
+
const row = getDb().prepare("SELECT COALESCE(SUM(event_count), 0) AS total FROM sessions WHERE date = $date").get({ date });
|
|
1524
|
+
return row.total;
|
|
1525
|
+
}
|
|
1526
|
+
function getLatestFileSaveAt(filePath) {
|
|
1527
|
+
const row = getDb().prepare(
|
|
1528
|
+
`SELECT captured_at FROM events
|
|
1529
|
+
WHERE file_path = $file_path AND category = 'code_edit'
|
|
1530
|
+
ORDER BY captured_at DESC
|
|
1531
|
+
LIMIT 1`
|
|
1532
|
+
).get({ file_path: filePath });
|
|
1533
|
+
return row?.captured_at ?? null;
|
|
1534
|
+
}
|
|
1535
|
+
function runInImmediateTransaction(work, options) {
|
|
1536
|
+
return withImmediateTransaction(getDb(), work, options);
|
|
1537
|
+
}
|
|
1482
1538
|
function getEventCount() {
|
|
1483
1539
|
const db = getDb();
|
|
1484
1540
|
const stmt = db.prepare("SELECT COUNT(*) as count FROM events");
|
|
@@ -1507,7 +1563,7 @@ function getEventsBySessionId(sessionId) {
|
|
|
1507
1563
|
SELECT e.* FROM events e
|
|
1508
1564
|
INNER JOIN session_events se ON se.event_id = e.id
|
|
1509
1565
|
WHERE se.session_id = $session_id
|
|
1510
|
-
ORDER BY e.captured_at ASC
|
|
1566
|
+
ORDER BY e.captured_at ASC, e.id ASC
|
|
1511
1567
|
`);
|
|
1512
1568
|
return stmt.all({ session_id: sessionId });
|
|
1513
1569
|
}
|
|
@@ -1600,6 +1656,7 @@ var init_db = __esm({
|
|
|
1600
1656
|
init_migrations();
|
|
1601
1657
|
init_migrate();
|
|
1602
1658
|
init_session_lookup();
|
|
1659
|
+
init_transaction();
|
|
1603
1660
|
_db = null;
|
|
1604
1661
|
}
|
|
1605
1662
|
});
|
|
@@ -3006,10 +3063,10 @@ function installIntoFolder(options) {
|
|
|
3006
3063
|
const outcome = installSkill({
|
|
3007
3064
|
sourceDir: options.sourceDir,
|
|
3008
3065
|
skillsFolder: target.skillsFolder,
|
|
3009
|
-
version: "2.0
|
|
3066
|
+
version: "2.1.0",
|
|
3010
3067
|
force: options.force
|
|
3011
3068
|
});
|
|
3012
|
-
return describeInstallOutcome({ ...target, outcome, version: "2.0
|
|
3069
|
+
return describeInstallOutcome({ ...target, outcome, version: "2.1.0" });
|
|
3013
3070
|
} catch (error) {
|
|
3014
3071
|
return describeInstallFailure({ ...target, error });
|
|
3015
3072
|
}
|
|
@@ -3028,8 +3085,8 @@ function removeSkills(agents, options) {
|
|
|
3028
3085
|
}
|
|
3029
3086
|
function formatAgentSkillLine(agent, homeDir = import_os6.default.homedir()) {
|
|
3030
3087
|
const location = displayPath(import_path14.default.join(homeDir, agent.skillsFolder, SKILL_NAME), homeDir);
|
|
3031
|
-
const installState = readInstallState(import_path14.default.join(homeDir, agent.skillsFolder), "2.0
|
|
3032
|
-
const status = describeSkillStatus(installState, "2.0
|
|
3088
|
+
const installState = readInstallState(import_path14.default.join(homeDir, agent.skillsFolder), "2.1.0");
|
|
3089
|
+
const status = describeSkillStatus(installState, "2.1.0");
|
|
3033
3090
|
return `${agent.name.padEnd(AGENT_NAME_COLUMN_WIDTH)} ${STATUS_COLOURS[status.level](status.text)} ${import_chalk2.default.dim(location)}`;
|
|
3034
3091
|
}
|
|
3035
3092
|
function formatUndetectedAgentLine(agent) {
|
|
@@ -3315,10 +3372,10 @@ async function runAIToolsStep() {
|
|
|
3315
3372
|
}
|
|
3316
3373
|
async function offerSkillInstall(group, sourceDir) {
|
|
3317
3374
|
const target = describeTarget(group);
|
|
3318
|
-
const state = readInstallState(target.skillsFolder, "2.0
|
|
3375
|
+
const state = readInstallState(target.skillsFolder, "2.1.0");
|
|
3319
3376
|
const knownOutcome = outcomeWithoutPrompt(state);
|
|
3320
3377
|
if (knownOutcome) {
|
|
3321
|
-
printMessage(describeInstallOutcome({ ...target, outcome: knownOutcome, version: "2.0
|
|
3378
|
+
printMessage(describeInstallOutcome({ ...target, outcome: knownOutcome, version: "2.1.0" }));
|
|
3322
3379
|
return;
|
|
3323
3380
|
}
|
|
3324
3381
|
const { installSkill: shouldInstall } = await (0, import_prompts2.default)({
|
|
@@ -3478,6 +3535,12 @@ function localDateStr(d) {
|
|
|
3478
3535
|
function localDateOf(isoString) {
|
|
3479
3536
|
return localDateStr(new Date(isoString));
|
|
3480
3537
|
}
|
|
3538
|
+
function localDayBounds(date) {
|
|
3539
|
+
const start = /* @__PURE__ */ new Date(`${date}T00:00:00`);
|
|
3540
|
+
const end = new Date(start);
|
|
3541
|
+
end.setDate(end.getDate() + 1);
|
|
3542
|
+
return { start: start.toISOString(), end: end.toISOString() };
|
|
3543
|
+
}
|
|
3481
3544
|
function startOfWeek(dateStr) {
|
|
3482
3545
|
const d = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
|
|
3483
3546
|
const day = d.getDay();
|
|
@@ -3998,35 +4061,48 @@ var init_grouper = __esm({
|
|
|
3998
4061
|
});
|
|
3999
4062
|
|
|
4000
4063
|
// src/sessions/detector.ts
|
|
4001
|
-
function
|
|
4002
|
-
const
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
if (existing.length > 0 && cachedEventCount === events.length) {
|
|
4007
|
-
return existing.map((sessionRow) => ({
|
|
4008
|
-
id: sessionRow.id,
|
|
4009
|
-
date: sessionRow.date,
|
|
4010
|
-
startedAt: sessionRow.started_at,
|
|
4011
|
-
endedAt: sessionRow.ended_at,
|
|
4012
|
-
repoName: sessionRow.repo_name,
|
|
4013
|
-
durationMinutes: minutesBetween(sessionRow.started_at, sessionRow.ended_at),
|
|
4014
|
-
eventCount: sessionRow.event_count,
|
|
4015
|
-
summary: sessionRow.summary,
|
|
4016
|
-
events: getEventsBySessionId(sessionRow.id)
|
|
4017
|
-
}));
|
|
4018
|
-
}
|
|
4064
|
+
function isDayGrouped(date) {
|
|
4065
|
+
const { start, end } = localDayBounds(date);
|
|
4066
|
+
return countEventsBetween(start, end) === sumSessionEventCounts(date);
|
|
4067
|
+
}
|
|
4068
|
+
function regroupDay(date) {
|
|
4019
4069
|
const savedSummaries = /* @__PURE__ */ new Map();
|
|
4020
|
-
for (const
|
|
4021
|
-
if (
|
|
4070
|
+
for (const sessionRow of getSessionsForDate(date)) {
|
|
4071
|
+
if (sessionRow.summary) savedSummaries.set(`${sessionRow.repo_name}|${sessionRow.started_at}`, sessionRow.summary);
|
|
4022
4072
|
}
|
|
4023
4073
|
clearSessionsForDate(date);
|
|
4024
|
-
|
|
4074
|
+
buildSessions(date, getEventsForDate(date), savedSummaries);
|
|
4075
|
+
}
|
|
4076
|
+
function ensureDayGrouped(date, options = {}) {
|
|
4077
|
+
if (isDayGrouped(date)) return;
|
|
4078
|
+
runInImmediateTransaction(
|
|
4079
|
+
() => {
|
|
4080
|
+
if (isDayGrouped(date)) return;
|
|
4081
|
+
regroupDay(date);
|
|
4082
|
+
},
|
|
4083
|
+
{ busyTimeoutMs: options.lockWaitMs }
|
|
4084
|
+
);
|
|
4025
4085
|
}
|
|
4026
|
-
function
|
|
4086
|
+
function toSession(sessionRow) {
|
|
4087
|
+
return {
|
|
4088
|
+
id: sessionRow.id,
|
|
4089
|
+
date: sessionRow.date,
|
|
4090
|
+
startedAt: sessionRow.started_at,
|
|
4091
|
+
endedAt: sessionRow.ended_at,
|
|
4092
|
+
repoName: sessionRow.repo_name,
|
|
4093
|
+
durationMinutes: minutesBetween(sessionRow.started_at, sessionRow.ended_at),
|
|
4094
|
+
eventCount: sessionRow.event_count,
|
|
4095
|
+
summary: sessionRow.summary,
|
|
4096
|
+
events: getEventsBySessionId(sessionRow.id)
|
|
4097
|
+
};
|
|
4098
|
+
}
|
|
4099
|
+
function getOrBuildSessions(date) {
|
|
4100
|
+
ensureDayGrouped(date);
|
|
4101
|
+
return getSessionsForDate(date).map(toSession);
|
|
4102
|
+
}
|
|
4103
|
+
function buildSessions(date, events, savedSummaries) {
|
|
4027
4104
|
const config = readConfig();
|
|
4028
4105
|
const idleMs = config.session.idleTimeout * 60 * 1e3;
|
|
4029
|
-
const sessions = [];
|
|
4030
4106
|
const timeGroups = splitByIdleGap(events, idleMs);
|
|
4031
4107
|
for (const group of timeGroups) {
|
|
4032
4108
|
if (group.length === 0) continue;
|
|
@@ -4048,26 +4124,15 @@ function buildSessions(date, events, savedSummaries = /* @__PURE__ */ new Map())
|
|
|
4048
4124
|
for (const event of repoGroup) {
|
|
4049
4125
|
insertSessionEvent(sessionId, event.id);
|
|
4050
4126
|
}
|
|
4051
|
-
sessions.push({
|
|
4052
|
-
id: sessionId,
|
|
4053
|
-
date,
|
|
4054
|
-
startedAt: first.captured_at,
|
|
4055
|
-
endedAt: last.captured_at,
|
|
4056
|
-
repoName,
|
|
4057
|
-
durationMinutes: minutesBetween(first.captured_at, last.captured_at),
|
|
4058
|
-
eventCount: repoGroup.length,
|
|
4059
|
-
summary,
|
|
4060
|
-
events: repoGroup
|
|
4061
|
-
});
|
|
4062
4127
|
}
|
|
4063
4128
|
}
|
|
4064
|
-
return sessions;
|
|
4065
4129
|
}
|
|
4066
4130
|
var init_detector = __esm({
|
|
4067
4131
|
"src/sessions/detector.ts"() {
|
|
4068
4132
|
"use strict";
|
|
4069
4133
|
init_db();
|
|
4070
4134
|
init_config();
|
|
4135
|
+
init_format();
|
|
4071
4136
|
init_grouper();
|
|
4072
4137
|
}
|
|
4073
4138
|
});
|
|
@@ -4182,18 +4247,18 @@ var init_session_summary = __esm({
|
|
|
4182
4247
|
});
|
|
4183
4248
|
|
|
4184
4249
|
// src/summary/git-commits.ts
|
|
4185
|
-
function runGit(cwd, args) {
|
|
4250
|
+
function runGit(cwd, args, timeoutMs = GIT_TIMEOUT_MS) {
|
|
4186
4251
|
try {
|
|
4187
|
-
return (0, import_child_process6.execFileSync)("git", args, { cwd, timeout:
|
|
4252
|
+
return (0, import_child_process6.execFileSync)("git", args, { cwd, timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
4188
4253
|
} catch {
|
|
4189
4254
|
return null;
|
|
4190
4255
|
}
|
|
4191
4256
|
}
|
|
4192
|
-
function getRepoRoot(cwd) {
|
|
4193
|
-
return runGit(cwd, ["rev-parse", "--show-toplevel"]) || null;
|
|
4257
|
+
function getRepoRoot(cwd, timeoutMs) {
|
|
4258
|
+
return runGit(cwd, ["rev-parse", "--show-toplevel"], timeoutMs) || null;
|
|
4194
4259
|
}
|
|
4195
|
-
function getUserEmail(repoRoot) {
|
|
4196
|
-
return runGit(repoRoot, ["config", "user.email"]) || null;
|
|
4260
|
+
function getUserEmail(repoRoot, timeoutMs) {
|
|
4261
|
+
return runGit(repoRoot, ["config", "user.email"], timeoutMs) || null;
|
|
4197
4262
|
}
|
|
4198
4263
|
function getOthersSubjects(repoRoot, author) {
|
|
4199
4264
|
if (!author) return [];
|
|
@@ -4211,15 +4276,20 @@ function getOthersSubjects(repoRoot, author) {
|
|
|
4211
4276
|
}
|
|
4212
4277
|
return [...theirs].filter((subject) => !mine.has(subject));
|
|
4213
4278
|
}
|
|
4214
|
-
function getCommitsInWindow({ repoRoot, since, until, author }) {
|
|
4279
|
+
function getCommitsInWindow({ repoRoot, since, until, author, filePath, timeoutMs }) {
|
|
4215
4280
|
const shiftedBy = (iso, milliseconds) => new Date(new Date(iso).getTime() + milliseconds).toISOString();
|
|
4216
|
-
const output = runGit(
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4281
|
+
const output = runGit(
|
|
4282
|
+
repoRoot,
|
|
4283
|
+
[
|
|
4284
|
+
"log",
|
|
4285
|
+
`--since=${shiftedBy(since, -WINDOW_LEAD_MS)}`,
|
|
4286
|
+
`--until=${shiftedBy(until, WINDOW_TRAIL_MS)}`,
|
|
4287
|
+
"--format=%h%x09%ce%x09%s",
|
|
4288
|
+
"--reverse",
|
|
4289
|
+
...filePath ? ["--", filePath] : []
|
|
4290
|
+
],
|
|
4291
|
+
timeoutMs
|
|
4292
|
+
);
|
|
4223
4293
|
if (!output) return [];
|
|
4224
4294
|
const wantedAuthor = author?.toLowerCase() ?? null;
|
|
4225
4295
|
return output.split("\n").flatMap((line) => {
|
|
@@ -4230,11 +4300,38 @@ function getCommitsInWindow({ repoRoot, since, until, author }) {
|
|
|
4230
4300
|
return [{ hash, subject }];
|
|
4231
4301
|
});
|
|
4232
4302
|
}
|
|
4233
|
-
|
|
4303
|
+
function getFileCommits(options) {
|
|
4304
|
+
const now = options.now ?? Date.now;
|
|
4305
|
+
const deadline = now() + options.budgetMs;
|
|
4306
|
+
const timeLeft = () => {
|
|
4307
|
+
const milliseconds = deadline - now();
|
|
4308
|
+
return milliseconds >= 1 ? milliseconds : null;
|
|
4309
|
+
};
|
|
4310
|
+
const rootTimeout = timeLeft();
|
|
4311
|
+
if (rootTimeout === null) return { repoRoot: null, commits: [] };
|
|
4312
|
+
const repoRoot = getRepoRoot(import_path17.default.dirname(options.filePath), rootTimeout);
|
|
4313
|
+
if (!repoRoot) return { repoRoot: null, commits: [] };
|
|
4314
|
+
const emailTimeout = timeLeft();
|
|
4315
|
+
if (emailTimeout === null) return { repoRoot, commits: [] };
|
|
4316
|
+
const author = getUserEmail(repoRoot, emailTimeout);
|
|
4317
|
+
const logTimeout = timeLeft();
|
|
4318
|
+
if (logTimeout === null) return { repoRoot, commits: [] };
|
|
4319
|
+
const commits = getCommitsInWindow({
|
|
4320
|
+
repoRoot,
|
|
4321
|
+
since: options.since,
|
|
4322
|
+
until: options.until,
|
|
4323
|
+
author,
|
|
4324
|
+
filePath: options.filePath,
|
|
4325
|
+
timeoutMs: logTimeout
|
|
4326
|
+
});
|
|
4327
|
+
return { repoRoot, commits };
|
|
4328
|
+
}
|
|
4329
|
+
var import_child_process6, import_path17, GIT_TIMEOUT_MS, WINDOW_LEAD_MS, WINDOW_TRAIL_MS, AUTHORSHIP_HISTORY_DEPTH;
|
|
4234
4330
|
var init_git_commits = __esm({
|
|
4235
4331
|
"src/summary/git-commits.ts"() {
|
|
4236
4332
|
"use strict";
|
|
4237
4333
|
import_child_process6 = require("child_process");
|
|
4334
|
+
import_path17 = __toESM(require("path"));
|
|
4238
4335
|
GIT_TIMEOUT_MS = 3e3;
|
|
4239
4336
|
WINDOW_LEAD_MS = 12e4;
|
|
4240
4337
|
WINDOW_TRAIL_MS = 6e4;
|
|
@@ -4853,7 +4950,7 @@ function registerExport(program) {
|
|
|
4853
4950
|
}
|
|
4854
4951
|
const payload = buildDayPayload(date, sessions, locationContextFrom(config));
|
|
4855
4952
|
const summary = await createSummaryWriter(config).summarize(payload);
|
|
4856
|
-
const outputPath = options.output ??
|
|
4953
|
+
const outputPath = options.output ?? import_path18.default.join(EXPORTS_DIR, `workscribe-${date}.md`);
|
|
4857
4954
|
if (!import_fs21.default.existsSync(EXPORTS_DIR)) import_fs21.default.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 448 });
|
|
4858
4955
|
import_fs21.default.writeFileSync(outputPath, `# Work Summary \u2014 ${date}
|
|
4859
4956
|
|
|
@@ -4862,12 +4959,12 @@ ${summary}
|
|
|
4862
4959
|
log.success(`Saved: ${outputPath}`);
|
|
4863
4960
|
});
|
|
4864
4961
|
}
|
|
4865
|
-
var import_fs21,
|
|
4962
|
+
var import_fs21, import_path18;
|
|
4866
4963
|
var init_export = __esm({
|
|
4867
4964
|
"src/cli/export.ts"() {
|
|
4868
4965
|
"use strict";
|
|
4869
4966
|
import_fs21 = __toESM(require("fs"));
|
|
4870
|
-
|
|
4967
|
+
import_path18 = __toESM(require("path"));
|
|
4871
4968
|
init_detector();
|
|
4872
4969
|
init_config();
|
|
4873
4970
|
init_logger();
|
|
@@ -5081,7 +5178,7 @@ var init_clear = __esm({
|
|
|
5081
5178
|
|
|
5082
5179
|
// src/cli/uninstall.ts
|
|
5083
5180
|
function completionRcFiles() {
|
|
5084
|
-
return [".zshrc", ".bashrc", ".bash_profile"].map((file) =>
|
|
5181
|
+
return [".zshrc", ".bashrc", ".bash_profile"].map((file) => import_path19.default.join(import_os11.default.homedir(), file));
|
|
5085
5182
|
}
|
|
5086
5183
|
function removeCompletionsAndToolHooks() {
|
|
5087
5184
|
for (const rcFile of completionRcFiles()) {
|
|
@@ -5098,10 +5195,10 @@ function removeCompletionsAndToolHooks() {
|
|
|
5098
5195
|
}
|
|
5099
5196
|
function detectShellRcFiles() {
|
|
5100
5197
|
const candidates = [
|
|
5101
|
-
{ name: "zsh", rcFile:
|
|
5102
|
-
{ name: "bash", rcFile:
|
|
5103
|
-
{ name: "bash", rcFile:
|
|
5104
|
-
{ name: "fish", rcFile:
|
|
5198
|
+
{ name: "zsh", rcFile: import_path19.default.join(import_os11.default.homedir(), ".zshrc") },
|
|
5199
|
+
{ name: "bash", rcFile: import_path19.default.join(import_os11.default.homedir(), ".bashrc") },
|
|
5200
|
+
{ name: "bash", rcFile: import_path19.default.join(import_os11.default.homedir(), ".bash_profile") },
|
|
5201
|
+
{ name: "fish", rcFile: import_path19.default.join(import_os11.default.homedir(), ".config", "fish", "conf.d", "workscribe.fish") }
|
|
5105
5202
|
];
|
|
5106
5203
|
return candidates.filter((candidate) => import_fs23.default.existsSync(candidate.rcFile) && isHookInstalled(candidate.rcFile));
|
|
5107
5204
|
}
|
|
@@ -5158,12 +5255,12 @@ function registerUninstall(program) {
|
|
|
5158
5255
|
log.plain("");
|
|
5159
5256
|
});
|
|
5160
5257
|
}
|
|
5161
|
-
var import_fs23,
|
|
5258
|
+
var import_fs23, import_path19, import_os11, import_prompts4, import_chalk10;
|
|
5162
5259
|
var init_uninstall = __esm({
|
|
5163
5260
|
"src/cli/uninstall.ts"() {
|
|
5164
5261
|
"use strict";
|
|
5165
5262
|
import_fs23 = __toESM(require("fs"));
|
|
5166
|
-
|
|
5263
|
+
import_path19 = __toESM(require("path"));
|
|
5167
5264
|
import_os11 = __toESM(require("os"));
|
|
5168
5265
|
import_prompts4 = __toESM(require("prompts"));
|
|
5169
5266
|
import_chalk10 = __toESM(require("chalk"));
|
|
@@ -5220,7 +5317,7 @@ function registerProfile(program) {
|
|
|
5220
5317
|
log.plain(import_chalk11.default.dim(" " + "\u2500".repeat(60)));
|
|
5221
5318
|
for (const entry of entries) {
|
|
5222
5319
|
const badge = activeNames.has(entry.name) ? import_chalk11.default.green(" [active]") : "";
|
|
5223
|
-
const installed = import_fs24.default.existsSync(
|
|
5320
|
+
const installed = import_fs24.default.existsSync(import_path20.default.join(PROFILES_DIR, `${entry.name}.json`)) ? import_chalk11.default.dim(" installed") : "";
|
|
5224
5321
|
log.plain(` ${import_chalk11.default.cyan(entry.name.padEnd(18))} ${entry.displayName.padEnd(28)}${badge}${installed}`);
|
|
5225
5322
|
if (entry.description) log.plain(` ${import_chalk11.default.dim(" ".repeat(18) + entry.description)}`);
|
|
5226
5323
|
}
|
|
@@ -5256,7 +5353,7 @@ function registerProfile(program) {
|
|
|
5256
5353
|
continue;
|
|
5257
5354
|
}
|
|
5258
5355
|
activeNames.delete(name);
|
|
5259
|
-
const cachePath =
|
|
5356
|
+
const cachePath = import_path20.default.join(PROFILES_DIR, `${name}.json`);
|
|
5260
5357
|
if (import_fs24.default.existsSync(cachePath)) import_fs24.default.unlinkSync(cachePath);
|
|
5261
5358
|
log.success(`Profile "${name}" removed`);
|
|
5262
5359
|
}
|
|
@@ -5384,12 +5481,12 @@ function registerProfile(program) {
|
|
|
5384
5481
|
}
|
|
5385
5482
|
});
|
|
5386
5483
|
}
|
|
5387
|
-
var import_fs24,
|
|
5484
|
+
var import_fs24, import_path20, import_chalk11, MIN_SUGGEST_COUNT;
|
|
5388
5485
|
var init_profile = __esm({
|
|
5389
5486
|
"src/cli/profile.ts"() {
|
|
5390
5487
|
"use strict";
|
|
5391
5488
|
import_fs24 = __toESM(require("fs"));
|
|
5392
|
-
|
|
5489
|
+
import_path20 = __toESM(require("path"));
|
|
5393
5490
|
import_chalk11 = __toESM(require("chalk"));
|
|
5394
5491
|
init_config();
|
|
5395
5492
|
init_paths();
|
|
@@ -5409,6 +5506,9 @@ function realPath(target) {
|
|
|
5409
5506
|
return null;
|
|
5410
5507
|
}
|
|
5411
5508
|
}
|
|
5509
|
+
function resolvedPath(target) {
|
|
5510
|
+
return realPath(target) ?? target;
|
|
5511
|
+
}
|
|
5412
5512
|
function currentDirectory(where = { pwd: process.env.PWD, cwd: process.cwd() }) {
|
|
5413
5513
|
if (where.pwd && realPath(where.pwd) === realPath(where.cwd)) return where.pwd;
|
|
5414
5514
|
return where.cwd;
|
|
@@ -5416,15 +5516,15 @@ function currentDirectory(where = { pwd: process.env.PWD, cwd: process.cwd() })
|
|
|
5416
5516
|
function rootForms(cwd, root) {
|
|
5417
5517
|
const resolvedCwd = realPath(cwd);
|
|
5418
5518
|
if (!resolvedCwd) return [root];
|
|
5419
|
-
const reached =
|
|
5519
|
+
const reached = import_path21.default.resolve(cwd, import_path21.default.relative(resolvedCwd, root));
|
|
5420
5520
|
return [.../* @__PURE__ */ new Set([root, reached])];
|
|
5421
5521
|
}
|
|
5422
|
-
var import_fs25,
|
|
5522
|
+
var import_fs25, import_path21;
|
|
5423
5523
|
var init_working_directory = __esm({
|
|
5424
5524
|
"src/utils/working-directory.ts"() {
|
|
5425
5525
|
"use strict";
|
|
5426
5526
|
import_fs25 = __toESM(require("fs"));
|
|
5427
|
-
|
|
5527
|
+
import_path21 = __toESM(require("path"));
|
|
5428
5528
|
}
|
|
5429
5529
|
});
|
|
5430
5530
|
|
|
@@ -5505,9 +5605,9 @@ function redactConfig() {
|
|
|
5505
5605
|
function registerBugReport(program) {
|
|
5506
5606
|
program.command("bug-report").description("Generate a diagnostic report for filing a bug").action(() => {
|
|
5507
5607
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5508
|
-
const reportPath =
|
|
5608
|
+
const reportPath = import_path22.default.join(WORKSCRIBE_DIR, `bug-report-${timestamp}.txt`);
|
|
5509
5609
|
const systemInfo = [
|
|
5510
|
-
`Workscribe version : ${"2.0
|
|
5610
|
+
`Workscribe version : ${"2.1.0"}`,
|
|
5511
5611
|
`Node.js version : ${process.version}`,
|
|
5512
5612
|
`Platform : ${process.platform}`,
|
|
5513
5613
|
`Architecture : ${process.arch}`,
|
|
@@ -5538,8 +5638,8 @@ function registerBugReport(program) {
|
|
|
5538
5638
|
process.exit(1);
|
|
5539
5639
|
}
|
|
5540
5640
|
const params = new URLSearchParams({
|
|
5541
|
-
title: `Bug report \u2014 workscribe ${"2.0
|
|
5542
|
-
body: `<!-- Attach ${
|
|
5641
|
+
title: `Bug report \u2014 workscribe ${"2.1.0"}`,
|
|
5642
|
+
body: `<!-- Attach ${import_path22.default.basename(reportPath)} or paste the relevant sections below -->
|
|
5543
5643
|
|
|
5544
5644
|
**Describe the issue:**
|
|
5545
5645
|
|
|
@@ -5556,13 +5656,13 @@ function registerBugReport(program) {
|
|
|
5556
5656
|
log.plain("");
|
|
5557
5657
|
});
|
|
5558
5658
|
}
|
|
5559
|
-
var import_fs26, import_os12,
|
|
5659
|
+
var import_fs26, import_os12, import_path22, import_chalk12, GITHUB_ISSUES_URL, DEBUG_LOG_TAIL_LINES;
|
|
5560
5660
|
var init_bug_report = __esm({
|
|
5561
5661
|
"src/cli/bug-report.ts"() {
|
|
5562
5662
|
"use strict";
|
|
5563
5663
|
import_fs26 = __toESM(require("fs"));
|
|
5564
5664
|
import_os12 = __toESM(require("os"));
|
|
5565
|
-
|
|
5665
|
+
import_path22 = __toESM(require("path"));
|
|
5566
5666
|
import_chalk12 = __toESM(require("chalk"));
|
|
5567
5667
|
init_paths();
|
|
5568
5668
|
init_config();
|
|
@@ -5629,11 +5729,108 @@ var init_alerts3 = __esm({
|
|
|
5629
5729
|
}
|
|
5630
5730
|
});
|
|
5631
5731
|
|
|
5732
|
+
// src/summary/file-details.ts
|
|
5733
|
+
function displayPath2(absoluteFilePath, repoRoot) {
|
|
5734
|
+
if (repoRoot) {
|
|
5735
|
+
const relative = import_path23.default.relative(repoRoot, absoluteFilePath);
|
|
5736
|
+
if (relative && !relative.startsWith("..") && !import_path23.default.isAbsolute(relative)) return relative;
|
|
5737
|
+
}
|
|
5738
|
+
return import_path23.default.basename(absoluteFilePath);
|
|
5739
|
+
}
|
|
5740
|
+
function otherFilesBySaves(sessionEvents, filePath) {
|
|
5741
|
+
const saves = /* @__PURE__ */ new Map();
|
|
5742
|
+
for (const event of sessionEvents) {
|
|
5743
|
+
if (event.category !== CODE_EDIT || !event.file_path || event.file_path === filePath) continue;
|
|
5744
|
+
const seen = saves.get(event.file_path);
|
|
5745
|
+
saves.set(event.file_path, {
|
|
5746
|
+
count: (seen?.count ?? 0) + 1,
|
|
5747
|
+
lastSavedAt: seen && seen.lastSavedAt > event.captured_at ? seen.lastSavedAt : event.captured_at
|
|
5748
|
+
});
|
|
5749
|
+
}
|
|
5750
|
+
return [...saves.entries()].sort(([, first], [, second]) => second.count - first.count || second.lastSavedAt.localeCompare(first.lastSavedAt)).map(([otherPath]) => otherPath);
|
|
5751
|
+
}
|
|
5752
|
+
function savesOf(sessionEvents, filePath) {
|
|
5753
|
+
return sessionEvents.filter((event) => event.category === CODE_EDIT && event.file_path === filePath);
|
|
5754
|
+
}
|
|
5755
|
+
function buildFileDetails(options) {
|
|
5756
|
+
const { filePath, sessionEvents, displayPathOf } = options;
|
|
5757
|
+
const saves = savesOf(sessionEvents, filePath);
|
|
5758
|
+
if (saves.length === 0) return null;
|
|
5759
|
+
const savedTimes = saves.map((save) => save.captured_at).sort();
|
|
5760
|
+
const latestBranch = [...saves].reverse().find((save) => save.branch)?.branch ?? null;
|
|
5761
|
+
const otherFiles = otherFilesBySaves(sessionEvents, filePath);
|
|
5762
|
+
return {
|
|
5763
|
+
path: filePath,
|
|
5764
|
+
relativePath: displayPathOf(filePath),
|
|
5765
|
+
firstSavedAt: savedTimes[0],
|
|
5766
|
+
lastSavedAt: savedTimes.at(-1) ?? savedTimes[0],
|
|
5767
|
+
saveCount: saves.length,
|
|
5768
|
+
branch: latestBranch ?? options.sessionBranch,
|
|
5769
|
+
commits: options.commits,
|
|
5770
|
+
editedAlongside: {
|
|
5771
|
+
files: otherFiles.slice(0, EDITED_ALONGSIDE_LIMIT).map(displayPathOf),
|
|
5772
|
+
total: otherFiles.length
|
|
5773
|
+
}
|
|
5774
|
+
};
|
|
5775
|
+
}
|
|
5776
|
+
var import_path23, EDITED_ALONGSIDE_LIMIT, CODE_EDIT;
|
|
5777
|
+
var init_file_details = __esm({
|
|
5778
|
+
"src/summary/file-details.ts"() {
|
|
5779
|
+
"use strict";
|
|
5780
|
+
import_path23 = __toESM(require("path"));
|
|
5781
|
+
EDITED_ALONGSIDE_LIMIT = 5;
|
|
5782
|
+
CODE_EDIT = "code_edit";
|
|
5783
|
+
}
|
|
5784
|
+
});
|
|
5785
|
+
|
|
5786
|
+
// src/cli/context-file.ts
|
|
5787
|
+
function groupDayOfLatestSave(filePath) {
|
|
5788
|
+
const latestSave = getLatestFileSaveAt(filePath);
|
|
5789
|
+
if (!latestSave) return;
|
|
5790
|
+
try {
|
|
5791
|
+
ensureDayGrouped(localDateOf(latestSave), { lockWaitMs: LOCK_WAIT_MS });
|
|
5792
|
+
} catch (error) {
|
|
5793
|
+
if (!isDatabaseLocked(error)) throw error;
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
function getFileDetails(filePath, session) {
|
|
5797
|
+
const sessionEvents = getEventsBySessionId(session.id);
|
|
5798
|
+
if (savesOf(sessionEvents, filePath).length === 0) return null;
|
|
5799
|
+
const { repoRoot, commits } = getFileCommits({
|
|
5800
|
+
filePath: resolvedPath(filePath),
|
|
5801
|
+
since: session.started_at,
|
|
5802
|
+
until: session.ended_at,
|
|
5803
|
+
budgetMs: GIT_BUDGET_MS
|
|
5804
|
+
});
|
|
5805
|
+
return buildFileDetails({
|
|
5806
|
+
filePath,
|
|
5807
|
+
sessionEvents,
|
|
5808
|
+
sessionBranch: getSessionBranch(session.id),
|
|
5809
|
+
commits,
|
|
5810
|
+
displayPathOf: (storedPath) => displayPath2(resolvedPath(storedPath), repoRoot)
|
|
5811
|
+
});
|
|
5812
|
+
}
|
|
5813
|
+
var LOCK_WAIT_MS, GIT_BUDGET_MS;
|
|
5814
|
+
var init_context_file = __esm({
|
|
5815
|
+
"src/cli/context-file.ts"() {
|
|
5816
|
+
"use strict";
|
|
5817
|
+
init_db();
|
|
5818
|
+
init_transaction();
|
|
5819
|
+
init_detector();
|
|
5820
|
+
init_file_details();
|
|
5821
|
+
init_git_commits();
|
|
5822
|
+
init_format();
|
|
5823
|
+
init_working_directory();
|
|
5824
|
+
LOCK_WAIT_MS = 1e3;
|
|
5825
|
+
GIT_BUDGET_MS = 2e3;
|
|
5826
|
+
}
|
|
5827
|
+
});
|
|
5828
|
+
|
|
5632
5829
|
// src/cli/context.ts
|
|
5633
5830
|
function sessionMinutes(session) {
|
|
5634
5831
|
return (new Date(session.ended_at).getTime() - new Date(session.started_at).getTime()) / 6e4;
|
|
5635
5832
|
}
|
|
5636
|
-
function
|
|
5833
|
+
function toSession2(row) {
|
|
5637
5834
|
return {
|
|
5638
5835
|
id: row.id,
|
|
5639
5836
|
date: row.date,
|
|
@@ -5648,7 +5845,7 @@ function toSession(row) {
|
|
|
5648
5845
|
}
|
|
5649
5846
|
function renderTemplateSummary(row) {
|
|
5650
5847
|
const config = readConfig();
|
|
5651
|
-
const payload = sessionToPayload(
|
|
5848
|
+
const payload = sessionToPayload(toSession2(row), row.date, locationContextFrom(config));
|
|
5652
5849
|
const bullets = describeSession(payload, config.summary?.includeCategories ?? []);
|
|
5653
5850
|
return `${formatSessionHeading(payload)}
|
|
5654
5851
|
${toBulletList(bullets)}`;
|
|
@@ -5658,6 +5855,7 @@ function resolveContextSummary(row) {
|
|
|
5658
5855
|
}
|
|
5659
5856
|
function toContextJson(session) {
|
|
5660
5857
|
return {
|
|
5858
|
+
repoName: session.repo_name,
|
|
5661
5859
|
date: session.date,
|
|
5662
5860
|
startedAt: session.started_at,
|
|
5663
5861
|
endedAt: session.ended_at,
|
|
@@ -5668,6 +5866,9 @@ function toContextJson(session) {
|
|
|
5668
5866
|
summary: resolveContextSummary(session)
|
|
5669
5867
|
};
|
|
5670
5868
|
}
|
|
5869
|
+
function toFileContextJson(session, filePath) {
|
|
5870
|
+
return { ...toContextJson(session), file: getFileDetails(filePath, session) };
|
|
5871
|
+
}
|
|
5671
5872
|
function printSessionContext(session, heading) {
|
|
5672
5873
|
const ago = timeAgo(session.started_at);
|
|
5673
5874
|
const branch = getSessionBranch(session.id);
|
|
@@ -5688,7 +5889,8 @@ function printSessionContext(session, heading) {
|
|
|
5688
5889
|
function registerContext(program) {
|
|
5689
5890
|
program.command("context").description("Show the last session for the current repo").option("--file <path>", "narrow to last session that touched this file").option("--json", "output as JSON instead of formatted text \u2014 null when no session is found").action((opts) => {
|
|
5690
5891
|
if (opts.file) {
|
|
5691
|
-
const normalizedFile =
|
|
5892
|
+
const normalizedFile = import_path24.default.normalize(opts.file);
|
|
5893
|
+
groupDayOfLatestSave(normalizedFile);
|
|
5692
5894
|
const session = getLastSessionTouchingFile(normalizedFile);
|
|
5693
5895
|
if (!session) {
|
|
5694
5896
|
if (opts.json) log.plain(JSON.stringify(null));
|
|
@@ -5696,10 +5898,10 @@ function registerContext(program) {
|
|
|
5696
5898
|
return;
|
|
5697
5899
|
}
|
|
5698
5900
|
if (opts.json) {
|
|
5699
|
-
log.plain(JSON.stringify(
|
|
5901
|
+
log.plain(JSON.stringify(toFileContextJson(session, normalizedFile)));
|
|
5700
5902
|
return;
|
|
5701
5903
|
}
|
|
5702
|
-
printSessionContext(session, `Last session touching ${
|
|
5904
|
+
printSessionContext(session, `Last session touching ${import_path24.default.basename(normalizedFile)}`);
|
|
5703
5905
|
return;
|
|
5704
5906
|
}
|
|
5705
5907
|
const config = readConfig();
|
|
@@ -5725,11 +5927,11 @@ function registerContext(program) {
|
|
|
5725
5927
|
printSessionContext(sessions[0], `Last session on ${repoName}`);
|
|
5726
5928
|
});
|
|
5727
5929
|
}
|
|
5728
|
-
var
|
|
5930
|
+
var import_path24, import_chalk14;
|
|
5729
5931
|
var init_context = __esm({
|
|
5730
5932
|
"src/cli/context.ts"() {
|
|
5731
5933
|
"use strict";
|
|
5732
|
-
|
|
5934
|
+
import_path24 = __toESM(require("path"));
|
|
5733
5935
|
import_chalk14 = __toESM(require("chalk"));
|
|
5734
5936
|
init_db();
|
|
5735
5937
|
init_git();
|
|
@@ -5738,6 +5940,7 @@ var init_context = __esm({
|
|
|
5738
5940
|
init_config();
|
|
5739
5941
|
init_format();
|
|
5740
5942
|
init_logger();
|
|
5943
|
+
init_context_file();
|
|
5741
5944
|
init_describe();
|
|
5742
5945
|
init_utils3();
|
|
5743
5946
|
init_payload();
|
|
@@ -5843,7 +6046,7 @@ function isNewer(latest, current) {
|
|
|
5843
6046
|
return latestPatch > currentPatch;
|
|
5844
6047
|
}
|
|
5845
6048
|
function checkForUpdate() {
|
|
5846
|
-
const current = "2.0
|
|
6049
|
+
const current = "2.1.0";
|
|
5847
6050
|
const cache = readCache();
|
|
5848
6051
|
if (cache && isNewer(cache.latestVersion, current)) {
|
|
5849
6052
|
process.stderr.write(
|
|
@@ -5866,7 +6069,7 @@ function checkForUpdate() {
|
|
|
5866
6069
|
function buildTelemetryPayload(config) {
|
|
5867
6070
|
return {
|
|
5868
6071
|
installId: config.telemetry.installId,
|
|
5869
|
-
cliVersion: "2.0
|
|
6072
|
+
cliVersion: "2.1.0",
|
|
5870
6073
|
platform: import_os13.default.platform(),
|
|
5871
6074
|
nodeVersion: process.version
|
|
5872
6075
|
};
|
|
@@ -5942,7 +6145,7 @@ var require_main = __commonJS({
|
|
|
5942
6145
|
pingTelemetry();
|
|
5943
6146
|
}
|
|
5944
6147
|
var program = new import_commander.Command();
|
|
5945
|
-
program.name("workscribe").description("Automatic developer work journal").version("2.0
|
|
6148
|
+
program.name("workscribe").description("Automatic developer work journal").version("2.1.0");
|
|
5946
6149
|
registerCapture(program);
|
|
5947
6150
|
registerHook(program);
|
|
5948
6151
|
registerInit(program);
|