@workscribe/cli 1.1.3 → 1.2.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/README.md CHANGED
@@ -27,6 +27,16 @@ A shell hook captures every command you run. At the end of the day, `workscribe
27
27
  shell hook → capture → SQLite → session grouper → AI provider → markdown
28
28
  ```
29
29
 
30
+ ## Requirements
31
+
32
+ | Dependency | Version | Notes |
33
+ |---|---|---|
34
+ | Node.js | **22 or later** | Uses the built-in `node:sqlite` module — not available in Node 20 or earlier |
35
+ | Shell | zsh or bash | Hook is appended to `~/.zshrc` or `~/.bashrc` by `workscribe init` |
36
+ | OS | macOS or Linux | Windows is not supported |
37
+
38
+ > Check your version: `node --version`
39
+
30
40
  ## Install
31
41
 
32
42
  ```bash
@@ -37,8 +47,6 @@ source ~/.zshrc # or restart your terminal
37
47
 
38
48
  `workscribe init` walks you through choosing an AI provider and installs the shell hook. From that point, capture is automatic.
39
49
 
40
- **Requirements:** Node.js 22+, macOS or Linux (zsh or bash)
41
-
42
50
  ## AI providers
43
51
 
44
52
  Workscribe works with any of these — switch at any time via config:
@@ -67,6 +75,9 @@ workscribe config set ai.apiKey <your-key>
67
75
  | `workscribe events` | List captured events |
68
76
  | `workscribe export` | Save today's summary as a markdown file |
69
77
  | `workscribe status` | Check capture is active and show event counts |
78
+ | `workscribe undo` | Remove the last captured event |
79
+ | `workscribe undo -n 5` | Remove the last 5 captured events |
80
+ | `workscribe undo --dry-run` | Preview what would be removed without deleting |
70
81
  | `workscribe pause` | Pause capture temporarily |
71
82
  | `workscribe resume` | Resume capture after a pause |
72
83
  | `workscribe config` | View current configuration |
@@ -86,6 +97,14 @@ workscribe config set ai.apiKey <your-key>
86
97
  workscribe config set session.idleTimeout 20 # minutes before a new session starts (default: 30)
87
98
  workscribe config set ai.model gpt-4o # override the default model for your provider
88
99
  workscribe config set redact.extra "MY_PATTERN" # add a custom redaction regex
100
+
101
+ # Override the display name for a local clone whose folder name differs from the repo
102
+ workscribe config set projects./Users/me/sandbox/myrepo actual-repo-name
103
+
104
+ # Post summaries to Slack or a custom webhook
105
+ workscribe config set integrations.slackWebhook https://hooks.slack.com/services/...
106
+ workscribe summary --slack
107
+ workscribe summary --webhook https://custom.endpoint/hook
89
108
  ```
90
109
 
91
110
  Config is stored at `~/.workscribe/config.json`.
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_commander = require("commander");
28
28
 
29
29
  // src/capture/index.ts
30
- var import_fs3 = __toESM(require("fs"));
30
+ var import_fs4 = __toESM(require("fs"));
31
31
 
32
32
  // src/db/index.ts
33
33
  var import_node_sqlite = require("node:sqlite");
@@ -42,6 +42,7 @@ var CONFIG_PATH = import_path.default.join(WORKSCRIBE_DIR, "config.json");
42
42
  var DEBUG_LOG_PATH = import_path.default.join(WORKSCRIBE_DIR, "debug.log");
43
43
  var EXPORTS_DIR = import_path.default.join(WORKSCRIBE_DIR, "exports");
44
44
  var PAUSE_FLAG_PATH = import_path.default.join(WORKSCRIBE_DIR, "paused");
45
+ var WEEKLY_CACHE_DIR = import_path.default.join(WORKSCRIBE_DIR, "weekly-cache");
45
46
 
46
47
  // src/db/index.ts
47
48
  var _db = null;
@@ -151,6 +152,27 @@ function updateSessionSummary(sessionId, summary) {
151
152
  const stmt = db.prepare("UPDATE sessions SET summary = $summary WHERE id = $id");
152
153
  stmt.run({ summary, id: sessionId });
153
154
  }
155
+ function getLastEventIds(n) {
156
+ const db = getDb();
157
+ const rows = db.prepare("SELECT id FROM events ORDER BY id DESC LIMIT $n").all({ n });
158
+ return rows.map((r) => r.id);
159
+ }
160
+ function getEventsByIds(ids) {
161
+ if (ids.length === 0) return [];
162
+ const db = getDb();
163
+ const placeholders = ids.map(() => "?").join(", ");
164
+ return db.prepare(`SELECT * FROM events WHERE id IN (${placeholders}) ORDER BY id DESC`).all(...ids);
165
+ }
166
+ function deleteEventsByIds(ids) {
167
+ if (ids.length === 0) return;
168
+ const db = getDb();
169
+ const placeholders = ids.map(() => "?").join(", ");
170
+ const affectedDates = db.prepare(`SELECT DISTINCT date(captured_at) as date FROM events WHERE id IN (${placeholders})`).all(...ids);
171
+ db.prepare(`DELETE FROM events WHERE id IN (${placeholders})`).run(...ids);
172
+ for (const { date } of affectedDates) {
173
+ clearSessionsForDate(date);
174
+ }
175
+ }
154
176
  function clearSessionsForDate(date) {
155
177
  const db = getDb();
156
178
  db.prepare(`
@@ -202,14 +224,38 @@ function redact(command, extraPatterns = []) {
202
224
  // src/capture/git.ts
203
225
  var import_child_process = require("child_process");
204
226
  var import_path2 = __toESM(require("path"));
205
- function getGitContext(cwd) {
227
+ var import_fs2 = __toESM(require("fs"));
228
+ function nameFromRemote(root) {
229
+ try {
230
+ const url = (0, import_child_process.execSync)("git remote get-url origin", {
231
+ cwd: root,
232
+ timeout: 2e3,
233
+ stdio: "pipe"
234
+ }).toString().trim();
235
+ const match = url.match(/\/([^/]+?)(?:\.git)?$/);
236
+ return match ? match[1] : null;
237
+ } catch {
238
+ return null;
239
+ }
240
+ }
241
+ function nameFromPackageJson(root) {
242
+ try {
243
+ const raw = import_fs2.default.readFileSync(import_path2.default.join(root, "package.json"), "utf-8");
244
+ const pkg = JSON.parse(raw);
245
+ if (!pkg.name) return null;
246
+ return pkg.name.replace(/^@[^/]+\//, "");
247
+ } catch {
248
+ return null;
249
+ }
250
+ }
251
+ function getGitContext(cwd, nameOverride) {
206
252
  try {
207
253
  const root = (0, import_child_process.execSync)("git rev-parse --show-toplevel", {
208
254
  cwd,
209
255
  timeout: 2e3,
210
256
  stdio: "pipe"
211
257
  }).toString().trim();
212
- const repoName = import_path2.default.basename(root);
258
+ const repoName = nameOverride ?? nameFromRemote(root) ?? nameFromPackageJson(root) ?? import_path2.default.basename(root);
213
259
  let branch = null;
214
260
  try {
215
261
  branch = (0, import_child_process.execSync)("git branch --show-current", {
@@ -379,7 +425,7 @@ function normalizeCommand(command) {
379
425
  }
380
426
 
381
427
  // src/config/index.ts
382
- var import_fs2 = __toESM(require("fs"));
428
+ var import_fs3 = __toESM(require("fs"));
383
429
  var DEFAULTS = {
384
430
  ai: {
385
431
  provider: "",
@@ -401,9 +447,9 @@ var DEFAULTS = {
401
447
  }
402
448
  };
403
449
  function readConfig() {
404
- if (!import_fs2.default.existsSync(CONFIG_PATH)) return { ...DEFAULTS };
450
+ if (!import_fs3.default.existsSync(CONFIG_PATH)) return { ...DEFAULTS };
405
451
  try {
406
- const raw = import_fs2.default.readFileSync(CONFIG_PATH, "utf-8");
452
+ const raw = import_fs3.default.readFileSync(CONFIG_PATH, "utf-8");
407
453
  const parsed = JSON.parse(raw);
408
454
  return deepMerge(DEFAULTS, parsed);
409
455
  } catch {
@@ -411,9 +457,9 @@ function readConfig() {
411
457
  }
412
458
  }
413
459
  function writeConfig(config) {
414
- import_fs2.default.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
460
+ import_fs3.default.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
415
461
  try {
416
- import_fs2.default.chmodSync(CONFIG_PATH, 384);
462
+ import_fs3.default.chmodSync(CONFIG_PATH, 384);
417
463
  } catch {
418
464
  }
419
465
  }
@@ -478,14 +524,15 @@ function isDuplicate(command, cwd, lastEvent, dedupWindowMs = 5e3, now = Date.no
478
524
 
479
525
  // src/capture/index.ts
480
526
  function captureCommand(opts) {
481
- if (import_fs3.default.existsSync(PAUSE_FLAG_PATH)) return;
527
+ if (import_fs4.default.existsSync(PAUSE_FLAG_PATH)) return;
482
528
  const config = readConfig();
483
529
  const { command, cwd, exitCode, durationMs } = opts;
484
530
  if (shouldFilter(command, config.capture.ignore)) return;
485
531
  const last = getLastEvent();
486
532
  if (isDuplicate(command, cwd, last ?? void 0)) return;
487
533
  const safeCommand = redact(command, config.redact.extra);
488
- const { repoName, branch } = getGitContext(cwd);
534
+ const nameOverride = config.projects?.[cwd] ?? null;
535
+ const { repoName, branch } = getGitContext(cwd, nameOverride);
489
536
  const { category, metadata } = normalizeCommand(safeCommand);
490
537
  insertEvent({
491
538
  captured_at: (/* @__PURE__ */ new Date()).toISOString(),
@@ -502,7 +549,7 @@ function captureCommand(opts) {
502
549
 
503
550
  // src/utils/logger.ts
504
551
  var import_chalk = __toESM(require("chalk"));
505
- var import_fs4 = __toESM(require("fs"));
552
+ var import_fs5 = __toESM(require("fs"));
506
553
  var isDebug = process.env.WORKSCRIBE_DEBUG === "1";
507
554
  var log = {
508
555
  info: (msg) => console.log(import_chalk.default.cyan("\u2192"), msg),
@@ -516,7 +563,7 @@ function debugLog(context, error) {
516
563
  const entry = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${context}] ${error instanceof Error ? error.stack : String(error)}
517
564
  `;
518
565
  try {
519
- import_fs4.default.appendFileSync(DEBUG_LOG_PATH, entry, { mode: 384 });
566
+ import_fs5.default.appendFileSync(DEBUG_LOG_PATH, entry, { mode: 384 });
520
567
  } catch {
521
568
  }
522
569
  }
@@ -539,7 +586,7 @@ function registerCapture(program2) {
539
586
  }
540
587
 
541
588
  // src/cli/init.ts
542
- var import_fs5 = __toESM(require("fs"));
589
+ var import_fs6 = __toESM(require("fs"));
543
590
  var import_path3 = __toESM(require("path"));
544
591
  var import_os2 = __toESM(require("os"));
545
592
  var import_prompts = __toESM(require("prompts"));
@@ -552,26 +599,26 @@ function detectShell() {
552
599
  return null;
553
600
  }
554
601
  function isHookInstalled(rcFile) {
555
- if (!import_fs5.default.existsSync(rcFile)) return false;
556
- return import_fs5.default.readFileSync(rcFile, "utf-8").includes(HOOK_START);
602
+ if (!import_fs6.default.existsSync(rcFile)) return false;
603
+ return import_fs6.default.readFileSync(rcFile, "utf-8").includes(HOOK_START);
557
604
  }
558
605
  function installHook(rcFile, hookContent) {
559
606
  const backup = `${rcFile}.workscribe-backup-${Date.now()}`;
560
- import_fs5.default.copyFileSync(rcFile, backup);
607
+ import_fs6.default.copyFileSync(rcFile, backup);
561
608
  log.info(`Backed up ${rcFile} \u2192 ${import_path3.default.basename(backup)}`);
562
- import_fs5.default.appendFileSync(rcFile, `
609
+ import_fs6.default.appendFileSync(rcFile, `
563
610
  ${hookContent}
564
611
  `);
565
612
  }
566
613
  function registerInit(program2) {
567
614
  program2.command("init").description("Install shell hooks and set up Workscribe").action(async () => {
568
615
  log.plain(import_chalk2.default.bold("\n Workscribe \u2014 Developer Work Journal\n"));
569
- if (!import_fs5.default.existsSync(WORKSCRIBE_DIR)) {
570
- import_fs5.default.mkdirSync(WORKSCRIBE_DIR, { recursive: true, mode: 448 });
616
+ if (!import_fs6.default.existsSync(WORKSCRIBE_DIR)) {
617
+ import_fs6.default.mkdirSync(WORKSCRIBE_DIR, { recursive: true, mode: 448 });
571
618
  log.success(`Created ${WORKSCRIBE_DIR}`);
572
619
  } else {
573
620
  try {
574
- import_fs5.default.chmodSync(WORKSCRIBE_DIR, 448);
621
+ import_fs6.default.chmodSync(WORKSCRIBE_DIR, 448);
575
622
  } catch {
576
623
  }
577
624
  }
@@ -586,11 +633,11 @@ function registerInit(program2) {
586
633
  log.info(`Shell hook already installed in ${shell.rcFile}`);
587
634
  } else {
588
635
  const hookFile = import_path3.default.join(__dirname, "..", "shell", `hook.${shell.name}`);
589
- if (!import_fs5.default.existsSync(hookFile)) {
636
+ if (!import_fs6.default.existsSync(hookFile)) {
590
637
  log.warn(`Hook template not found at ${hookFile}`);
591
638
  } else {
592
- const hookContent = import_fs5.default.readFileSync(hookFile, "utf-8");
593
- if (!import_fs5.default.existsSync(shell.rcFile)) import_fs5.default.writeFileSync(shell.rcFile, "");
639
+ const hookContent = import_fs6.default.readFileSync(hookFile, "utf-8");
640
+ if (!import_fs6.default.existsSync(shell.rcFile)) import_fs6.default.writeFileSync(shell.rcFile, "");
594
641
  installHook(shell.rcFile, hookContent);
595
642
  log.success(`Shell hook installed in ${shell.rcFile}`);
596
643
  }
@@ -642,7 +689,7 @@ function registerInit(program2) {
642
689
  log.info("Skipped \u2014 run later: workscribe config set ai.provider <provider>");
643
690
  }
644
691
  }
645
- if (!import_fs5.default.existsSync(CONFIG_PATH)) {
692
+ if (!import_fs6.default.existsSync(CONFIG_PATH)) {
646
693
  writeConfig(config);
647
694
  log.success("Config file created");
648
695
  }
@@ -661,7 +708,7 @@ function registerInit(program2) {
661
708
  }
662
709
 
663
710
  // src/cli/status.ts
664
- var import_fs6 = __toESM(require("fs"));
711
+ var import_fs7 = __toESM(require("fs"));
665
712
  var import_chalk3 = __toESM(require("chalk"));
666
713
 
667
714
  // src/utils/format.ts
@@ -682,6 +729,35 @@ function parseDate(input) {
682
729
  if (isNaN(d.getTime())) throw new Error(`Invalid date: ${input}`);
683
730
  return input;
684
731
  }
732
+ function localDateStr(d) {
733
+ const y = d.getFullYear();
734
+ const m = String(d.getMonth() + 1).padStart(2, "0");
735
+ const day = String(d.getDate()).padStart(2, "0");
736
+ return `${y}-${m}-${day}`;
737
+ }
738
+ function startOfWeek(dateStr) {
739
+ const d = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
740
+ const day = d.getDay();
741
+ const diff = day === 0 ? -6 : 1 - day;
742
+ d.setDate(d.getDate() + diff);
743
+ return localDateStr(d);
744
+ }
745
+ function eachDayOfRange(start, end) {
746
+ const days = [];
747
+ const cur = /* @__PURE__ */ new Date(start + "T00:00:00");
748
+ const last = /* @__PURE__ */ new Date(end + "T00:00:00");
749
+ while (cur <= last) {
750
+ days.push(localDateStr(cur));
751
+ cur.setDate(cur.getDate() + 1);
752
+ }
753
+ return days;
754
+ }
755
+ function isoWeekKey(dateStr) {
756
+ const d = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
757
+ const jan4 = new Date(d.getFullYear(), 0, 4);
758
+ const weekNum = Math.ceil(((d.getTime() - jan4.getTime()) / 864e5 + jan4.getDay() + 1) / 7);
759
+ return `${d.getFullYear()}-W${String(weekNum).padStart(2, "0")}`;
760
+ }
685
761
 
686
762
  // src/cli/status.ts
687
763
  function registerStatus(program2) {
@@ -690,7 +766,7 @@ function registerStatus(program2) {
690
766
  const total = getEventCount();
691
767
  const todayEvents = getEventsForDate(todayDate());
692
768
  const last = getLastEvent();
693
- const isPaused = import_fs6.default.existsSync(PAUSE_FLAG_PATH);
769
+ const isPaused = import_fs7.default.existsSync(PAUSE_FLAG_PATH);
694
770
  log.plain("");
695
771
  log.plain(import_chalk3.default.bold(" Workscribe Status"));
696
772
  log.plain("");
@@ -900,6 +976,8 @@ function registerSessions(program2) {
900
976
 
901
977
  // src/cli/summary.ts
902
978
  var import_chalk6 = __toESM(require("chalk"));
979
+ var import_fs8 = __toESM(require("fs"));
980
+ var import_path4 = __toESM(require("path"));
903
981
 
904
982
  // src/ai/prompt.ts
905
983
  var SYSTEM_PROMPT = `You write short developer work summaries. Here is an example:
@@ -944,6 +1022,62 @@ function buildUserMessage(payload) {
944
1022
  }
945
1023
  return lines.join("\n");
946
1024
  }
1025
+ var WEEKLY_SYSTEM_PROMPT = `You write short weekly developer summaries. Here is an example:
1026
+
1027
+ INPUT:
1028
+ Week: 2026-05-18 to 2026-05-22
1029
+
1030
+ Monday 2026-05-18:
1031
+ Project: api-service (feature/auth) \u2014 2h 10m
1032
+ Events: 3x code_commit, 2x test_run, dependency_install
1033
+
1034
+ Tuesday 2026-05-19:
1035
+ Project: api-service (feature/auth) \u2014 1h 30m
1036
+ Events: 2x code_commit, test_run, build
1037
+
1038
+ Wednesday 2026-05-20:
1039
+ Project: dashboard (main) \u2014 3h 00m
1040
+ Events: 4x code_commit, 2x build, test_run
1041
+
1042
+ Thursday 2026-05-21:
1043
+ Project: api-service (feature/auth) \u2014 1h 00m
1044
+ Events: code_commit {"message":"feat: merge auth"}, branch_switch
1045
+
1046
+ Friday 2026-05-22:
1047
+ Project: dashboard (main) \u2014 2h 00m
1048
+ Events: 3x code_commit, build
1049
+
1050
+ OUTPUT:
1051
+ ## Week of 18 May \u2014 9h 40m across 2 projects
1052
+
1053
+ **api-service** (Mon\u2013Thu): Built and shipped the auth feature \u2014 JWT setup, token validation fixes, and final merge.
1054
+ **dashboard** (Wed, Fri): Active development across 7 commits with builds passing.
1055
+
1056
+ Now summarise the following week. Output ONLY the summary \u2014 a one-line header then one bullet per active project. Maximum 4 bullets.`;
1057
+ var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
1058
+ function buildWeeklyMessage(payload) {
1059
+ const lines = [`Week: ${payload.weekStart} to ${payload.weekEnd}`, ""];
1060
+ for (const { date, sessions } of payload.days) {
1061
+ const dayName = DAY_NAMES[(/* @__PURE__ */ new Date(date + "T00:00:00")).getDay()];
1062
+ lines.push(`${dayName} ${date}:`);
1063
+ for (const s of sessions) {
1064
+ const project = s.project ?? "unknown";
1065
+ const branch = s.branch ? ` (${s.branch})` : "";
1066
+ lines.push(` Project: ${project}${branch} \u2014 ${formatDuration(s.durationMinutes)}`);
1067
+ const counts = /* @__PURE__ */ new Map();
1068
+ for (const e of s.events) {
1069
+ const meta = Object.keys(e.metadata).length > 0 ? ` ${JSON.stringify(e.metadata)}` : "";
1070
+ const failed = e.exitCode !== null && e.exitCode !== 0 ? " [failed]" : "";
1071
+ const key = `${e.category}${meta}${failed}`;
1072
+ counts.set(key, (counts.get(key) ?? 0) + 1);
1073
+ }
1074
+ const eventLine = [...counts.entries()].map(([k, n]) => `${n > 1 ? `${n}x ` : ""}${k}`).join(", ");
1075
+ lines.push(` Events: ${eventLine}`);
1076
+ }
1077
+ lines.push("");
1078
+ }
1079
+ return lines.join("\n");
1080
+ }
947
1081
 
948
1082
  // src/ai/fetch.ts
949
1083
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -992,6 +1126,28 @@ var AnthropicProvider = class {
992
1126
  const data = await response.json();
993
1127
  return data.content.find((c) => c.type === "text")?.text ?? "";
994
1128
  }
1129
+ async summarizeWeek(payload) {
1130
+ const response = await fetchWithTimeout("https://api.anthropic.com/v1/messages", {
1131
+ method: "POST",
1132
+ headers: {
1133
+ "Content-Type": "application/json",
1134
+ "x-api-key": this.apiKey,
1135
+ "anthropic-version": "2023-06-01"
1136
+ },
1137
+ body: JSON.stringify({
1138
+ model: this.model,
1139
+ max_tokens: 1024,
1140
+ system: WEEKLY_SYSTEM_PROMPT,
1141
+ messages: [{ role: "user", content: buildWeeklyMessage(payload) }]
1142
+ })
1143
+ });
1144
+ if (!response.ok) {
1145
+ const body = await response.text();
1146
+ throw new Error(`Anthropic API error ${response.status}: ${body}`);
1147
+ }
1148
+ const data = await response.json();
1149
+ return data.content.find((c) => c.type === "text")?.text ?? "";
1150
+ }
995
1151
  };
996
1152
 
997
1153
  // src/ai/providers/openai.ts
@@ -1027,6 +1183,29 @@ var OpenAIProvider = class {
1027
1183
  const data = await response.json();
1028
1184
  return data.choices[0]?.message?.content ?? "";
1029
1185
  }
1186
+ async summarizeWeek(payload) {
1187
+ const response = await fetchWithTimeout(`${this.baseUrl}/chat/completions`, {
1188
+ method: "POST",
1189
+ headers: {
1190
+ "Content-Type": "application/json",
1191
+ Authorization: `Bearer ${this.apiKey}`
1192
+ },
1193
+ body: JSON.stringify({
1194
+ model: this.model,
1195
+ messages: [
1196
+ { role: "system", content: WEEKLY_SYSTEM_PROMPT },
1197
+ { role: "user", content: buildWeeklyMessage(payload) }
1198
+ ],
1199
+ max_tokens: 1024
1200
+ })
1201
+ });
1202
+ if (!response.ok) {
1203
+ const body = await response.text();
1204
+ throw new Error(`OpenAI API error ${response.status}: ${body}`);
1205
+ }
1206
+ const data = await response.json();
1207
+ return data.choices[0]?.message?.content ?? "";
1208
+ }
1030
1209
  };
1031
1210
 
1032
1211
  // src/ai/providers/ollama.ts
@@ -1063,6 +1242,30 @@ var OllamaProvider = class {
1063
1242
  const data = await response.json();
1064
1243
  return data.message?.content ?? "";
1065
1244
  }
1245
+ async summarizeWeek(payload) {
1246
+ const response = await fetchWithTimeout(`${this.baseUrl}/api/chat`, {
1247
+ method: "POST",
1248
+ headers: { "Content-Type": "application/json" },
1249
+ body: JSON.stringify({
1250
+ model: this.model,
1251
+ stream: false,
1252
+ messages: [
1253
+ { role: "system", content: WEEKLY_SYSTEM_PROMPT },
1254
+ { role: "user", content: buildWeeklyMessage(payload) }
1255
+ ],
1256
+ options: {
1257
+ num_predict: 400,
1258
+ temperature: 0.3
1259
+ }
1260
+ })
1261
+ });
1262
+ if (!response.ok) {
1263
+ const body = await response.text();
1264
+ throw new Error(`Ollama error ${response.status}: ${body}. Is Ollama running? Try: ollama serve`);
1265
+ }
1266
+ const data = await response.json();
1267
+ return data.message?.content ?? "";
1268
+ }
1066
1269
  };
1067
1270
 
1068
1271
  // src/ai/router.ts
@@ -1098,6 +1301,23 @@ Supported providers: anthropic, openai, ollama, openai-compatible`
1098
1301
  }
1099
1302
  }
1100
1303
 
1304
+ // src/integrations/webhook.ts
1305
+ async function postToWebhook(url, text) {
1306
+ const response = await fetchWithTimeout(
1307
+ url,
1308
+ {
1309
+ method: "POST",
1310
+ headers: { "Content-Type": "application/json" },
1311
+ body: JSON.stringify({ text })
1312
+ },
1313
+ 1e4
1314
+ );
1315
+ if (!response.ok) {
1316
+ const body = await response.text();
1317
+ throw new Error(`Webhook POST failed ${response.status}: ${body}`);
1318
+ }
1319
+ }
1320
+
1101
1321
  // src/cli/summary.ts
1102
1322
  function eventRowToPayload(row) {
1103
1323
  let metadata = {};
@@ -1112,8 +1332,84 @@ function eventRowToPayload(row) {
1112
1332
  durationMs: row.duration_ms
1113
1333
  };
1114
1334
  }
1335
+ function sessionToPayload(s, date) {
1336
+ return {
1337
+ date,
1338
+ project: s.repoName,
1339
+ branch: s.events[0]?.branch ?? null,
1340
+ startTime: formatDate(s.startedAt),
1341
+ endTime: formatDate(s.endedAt),
1342
+ durationMinutes: s.durationMinutes,
1343
+ events: s.events.map(eventRowToPayload)
1344
+ };
1345
+ }
1346
+ async function handleWebhook(opts, config, summary) {
1347
+ const webhookUrl = opts.webhook ?? (opts.slack ? config.integrations?.slackWebhook : void 0);
1348
+ if (webhookUrl) {
1349
+ try {
1350
+ log.info("Posting to webhook\u2026");
1351
+ await postToWebhook(webhookUrl, summary);
1352
+ log.success("Posted.");
1353
+ } catch (e) {
1354
+ log.error(`Webhook failed: ${e.message}`);
1355
+ }
1356
+ } else if (opts.slack && !config.integrations?.slackWebhook) {
1357
+ log.warn("--slack requires integrations.slackWebhook to be set in config");
1358
+ }
1359
+ }
1115
1360
  function registerSummary(program2) {
1116
- program2.command("summary").description("Generate an AI work summary").option("--date <YYYY-MM-DD>", "date to summarize (default: today)").option("--week", "summarize the current week").option("--fresh", "bypass cache and regenerate").action(async (opts) => {
1361
+ program2.command("summary").description("Generate an AI work summary").option("--date <YYYY-MM-DD>", "date to summarize (default: today)").option("--week", "summarize the current week").option("--fresh", "bypass cache and regenerate").option("--slack", "post summary to Slack webhook from config").option("--webhook <url>", "post summary to a custom webhook URL").action(async (opts) => {
1362
+ const config = readConfig();
1363
+ let provider;
1364
+ try {
1365
+ provider = getProvider(config);
1366
+ } catch (e) {
1367
+ log.error(e.message);
1368
+ process.exit(1);
1369
+ }
1370
+ if (opts.week) {
1371
+ const today = todayDate();
1372
+ const weekStart = startOfWeek(today);
1373
+ const days = eachDayOfRange(weekStart, today);
1374
+ const weeklyDays = [];
1375
+ for (const day of days) {
1376
+ const sessions2 = getOrBuildSessions(day);
1377
+ if (sessions2.length > 0) {
1378
+ weeklyDays.push({ date: day, sessions: sessions2.map((s) => sessionToPayload(s, day)) });
1379
+ }
1380
+ }
1381
+ if (weeklyDays.length === 0) {
1382
+ log.plain("No activity captured this week.");
1383
+ return;
1384
+ }
1385
+ const cacheKey = isoWeekKey(today);
1386
+ const cacheFile = import_path4.default.join(WEEKLY_CACHE_DIR, `${cacheKey}.md`);
1387
+ if (!opts.fresh && import_fs8.default.existsSync(cacheFile)) {
1388
+ const cached = import_fs8.default.readFileSync(cacheFile, "utf-8");
1389
+ log.plain("");
1390
+ log.plain(cached);
1391
+ log.plain("");
1392
+ log.plain(import_chalk6.default.dim(" (cached \u2014 use --fresh to regenerate)"));
1393
+ await handleWebhook(opts, config, cached);
1394
+ return;
1395
+ }
1396
+ const payload2 = { weekStart, weekEnd: today, days: weeklyDays };
1397
+ log.plain("");
1398
+ log.info(`Generating weekly summary via ${import_chalk6.default.cyan(config.ai.provider)}\u2026`);
1399
+ try {
1400
+ const summary = provider.summarizeWeek ? await provider.summarizeWeek(payload2) : await provider.summarize({ date: today, sessions: weeklyDays.flatMap((d) => d.sessions) });
1401
+ log.plain("");
1402
+ log.plain(summary);
1403
+ log.plain("");
1404
+ import_fs8.default.mkdirSync(WEEKLY_CACHE_DIR, { recursive: true });
1405
+ import_fs8.default.writeFileSync(cacheFile, summary, { encoding: "utf-8", mode: 384 });
1406
+ await handleWebhook(opts, config, summary);
1407
+ } catch (e) {
1408
+ log.error(`Weekly summary failed [${config.ai.provider}]: ${e.message}`);
1409
+ process.exit(1);
1410
+ }
1411
+ return;
1412
+ }
1117
1413
  let date = todayDate();
1118
1414
  if (opts.date) {
1119
1415
  try {
@@ -1140,23 +1436,7 @@ function registerSummary(program2) {
1140
1436
  return;
1141
1437
  }
1142
1438
  }
1143
- const config = readConfig();
1144
- let provider;
1145
- try {
1146
- provider = getProvider(config);
1147
- } catch (e) {
1148
- log.error(e.message);
1149
- process.exit(1);
1150
- }
1151
- const sessionPayloads = sessions.map((s) => ({
1152
- date,
1153
- project: s.repoName,
1154
- branch: s.events[0]?.branch ?? null,
1155
- startTime: formatDate(s.startedAt),
1156
- endTime: formatDate(s.endedAt),
1157
- durationMinutes: s.durationMinutes,
1158
- events: s.events.map(eventRowToPayload)
1159
- }));
1439
+ const sessionPayloads = sessions.map((s) => sessionToPayload(s, date));
1160
1440
  const payload = { date, sessions: sessionPayloads };
1161
1441
  log.plain("");
1162
1442
  log.info(`Generating summary via ${import_chalk6.default.cyan(config.ai.provider)}\u2026`);
@@ -1168,6 +1448,7 @@ function registerSummary(program2) {
1168
1448
  if (sessions[0]) {
1169
1449
  updateSessionSummary(sessions[0].id, summary);
1170
1450
  }
1451
+ await handleWebhook(opts, config, summary);
1171
1452
  } catch (e) {
1172
1453
  log.error(`Summary failed [${config.ai.provider}]: ${e.message}`);
1173
1454
  process.exit(1);
@@ -1176,8 +1457,8 @@ function registerSummary(program2) {
1176
1457
  }
1177
1458
 
1178
1459
  // src/cli/export.ts
1179
- var import_fs7 = __toESM(require("fs"));
1180
- var import_path4 = __toESM(require("path"));
1460
+ var import_fs9 = __toESM(require("fs"));
1461
+ var import_path5 = __toESM(require("path"));
1181
1462
  function eventRowToPayload2(row) {
1182
1463
  let metadata = {};
1183
1464
  try {
@@ -1222,9 +1503,9 @@ function registerExport(program2) {
1222
1503
  const payload = { date, sessions: sessionPayloads };
1223
1504
  log.info(`Generating summary via ${config.ai.provider}\u2026`);
1224
1505
  const summary = await provider.summarize(payload);
1225
- const outputPath = opts.output ?? import_path4.default.join(EXPORTS_DIR, `workscribe-${date}.md`);
1226
- if (!import_fs7.default.existsSync(EXPORTS_DIR)) import_fs7.default.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 448 });
1227
- import_fs7.default.writeFileSync(outputPath, `# Work Summary \u2014 ${date}
1506
+ const outputPath = opts.output ?? import_path5.default.join(EXPORTS_DIR, `workscribe-${date}.md`);
1507
+ if (!import_fs9.default.existsSync(EXPORTS_DIR)) import_fs9.default.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 448 });
1508
+ import_fs9.default.writeFileSync(outputPath, `# Work Summary \u2014 ${date}
1228
1509
 
1229
1510
  ${summary}
1230
1511
  `, { encoding: "utf-8", mode: 384 });
@@ -1261,7 +1542,7 @@ function registerConfig(program2) {
1261
1542
  }
1262
1543
  });
1263
1544
  }
1264
- var SENSITIVE_KEYS = /* @__PURE__ */ new Set(["apiKey", "api_key", "secret", "token", "password", "passwd"]);
1545
+ var SENSITIVE_KEYS = /* @__PURE__ */ new Set(["apiKey", "api_key", "secret", "token", "password", "passwd", "webhook", "slackWebhook"]);
1265
1546
  function maskValue(key, value) {
1266
1547
  if (SENSITIVE_KEYS.has(key) && typeof value === "string" && value.length > 0) {
1267
1548
  return import_chalk7.default.dim(`${value.slice(0, 4)}${"*".repeat(8)} (${value.length} chars)`);
@@ -1282,29 +1563,58 @@ function printConfig(obj, indent) {
1282
1563
  }
1283
1564
 
1284
1565
  // src/cli/pause.ts
1285
- var import_fs8 = __toESM(require("fs"));
1566
+ var import_fs10 = __toESM(require("fs"));
1286
1567
  function registerPause(program2) {
1287
1568
  program2.command("pause").description("Pause capture temporarily").action(() => {
1288
- if (import_fs8.default.existsSync(PAUSE_FLAG_PATH)) {
1569
+ if (import_fs10.default.existsSync(PAUSE_FLAG_PATH)) {
1289
1570
  log.info("Capture is already paused. Run workscribe resume to restart.");
1290
1571
  return;
1291
1572
  }
1292
- import_fs8.default.writeFileSync(PAUSE_FLAG_PATH, "", { mode: 384 });
1573
+ import_fs10.default.writeFileSync(PAUSE_FLAG_PATH, "", { mode: 384 });
1293
1574
  log.success("Capture paused. Run workscribe resume to restart.");
1294
1575
  });
1295
1576
  program2.command("resume").description("Resume capture after a pause").action(() => {
1296
- if (!import_fs8.default.existsSync(PAUSE_FLAG_PATH)) {
1577
+ if (!import_fs10.default.existsSync(PAUSE_FLAG_PATH)) {
1297
1578
  log.info("Capture is not paused.");
1298
1579
  return;
1299
1580
  }
1300
- import_fs8.default.rmSync(PAUSE_FLAG_PATH);
1581
+ import_fs10.default.rmSync(PAUSE_FLAG_PATH);
1301
1582
  log.success("Capture resumed.");
1302
1583
  });
1303
1584
  }
1304
1585
 
1586
+ // src/cli/undo.ts
1587
+ function registerUndo(program2) {
1588
+ program2.command("undo").description("Remove the last N captured events").option("-n, --count <number>", "number of events to remove", "1").option("--dry-run", "preview what would be deleted without deleting").action((opts) => {
1589
+ const count = parseInt(opts.count, 10);
1590
+ if (isNaN(count) || count < 1) {
1591
+ log.error("--count must be a positive integer");
1592
+ process.exit(1);
1593
+ }
1594
+ const ids = getLastEventIds(count);
1595
+ if (ids.length === 0) {
1596
+ log.plain("No events to undo.");
1597
+ return;
1598
+ }
1599
+ const events = getEventsByIds(ids);
1600
+ log.plain("");
1601
+ log.plain(`Events to remove (${events.length}):`);
1602
+ for (const e of events) {
1603
+ log.plain(` ${e.captured_at} ${e.category} ${e.command}`);
1604
+ }
1605
+ log.plain("");
1606
+ if (opts.dryRun) {
1607
+ log.plain("Dry run \u2014 nothing deleted.");
1608
+ return;
1609
+ }
1610
+ deleteEventsByIds(ids);
1611
+ log.success(`Removed ${ids.length} event${ids.length > 1 ? "s" : ""}.`);
1612
+ });
1613
+ }
1614
+
1305
1615
  // src/index.ts
1306
1616
  var program = new import_commander.Command();
1307
- program.name("workscribe").description("Automatic developer work journal").version("1.1.3");
1617
+ program.name("workscribe").description("Automatic developer work journal").version("1.2.0");
1308
1618
  registerCapture(program);
1309
1619
  registerInit(program);
1310
1620
  registerStatus(program);
@@ -1314,5 +1624,6 @@ registerSummary(program);
1314
1624
  registerExport(program);
1315
1625
  registerConfig(program);
1316
1626
  registerPause(program);
1627
+ registerUndo(program);
1317
1628
  program.parse();
1318
1629
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/capture/index.ts","../src/db/index.ts","../src/utils/paths.ts","../src/capture/redact.ts","../src/capture/git.ts","../src/normalize/patterns.ts","../src/config/index.ts","../src/capture/filter.ts","../src/utils/logger.ts","../src/cli/capture.ts","../src/cli/init.ts","../src/cli/status.ts","../src/utils/format.ts","../src/cli/events.ts","../src/cli/sessions.ts","../src/sessions/grouper.ts","../src/sessions/detector.ts","../src/cli/summary.ts","../src/ai/prompt.ts","../src/ai/fetch.ts","../src/ai/providers/anthropic.ts","../src/ai/providers/openai.ts","../src/ai/providers/ollama.ts","../src/ai/router.ts","../src/cli/export.ts","../src/cli/config.ts","../src/cli/pause.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { registerCapture } from './cli/capture'\n\ndeclare const __WORKSCRIBE_VERSION__: string\nimport { registerInit } from './cli/init'\nimport { registerStatus } from './cli/status'\nimport { registerEvents } from './cli/events'\nimport { registerSessions } from './cli/sessions'\nimport { registerSummary } from './cli/summary'\nimport { registerExport } from './cli/export'\nimport { registerConfig } from './cli/config'\nimport { registerPause } from './cli/pause'\n\nconst program = new Command()\n\nprogram\n .name('workscribe')\n .description('Automatic developer work journal')\n .version(__WORKSCRIBE_VERSION__)\n\nregisterCapture(program)\nregisterInit(program)\nregisterStatus(program)\nregisterEvents(program)\nregisterSessions(program)\nregisterSummary(program)\nregisterExport(program)\nregisterConfig(program)\nregisterPause(program)\n\nprogram.parse()\n","import fs from 'fs'\nimport { insertEvent, getLastEvent } from '../db'\nimport { redact } from './redact'\nimport { getGitContext } from './git'\nimport { normalizeCommand } from '../normalize/patterns'\nimport { readConfig } from '../config'\nimport { shouldFilter, isDuplicate } from './filter'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\n\nexport function captureCommand(opts: {\n command: string\n cwd: string\n exitCode: number | null\n durationMs: number | null\n}): void {\n // Check pause flag before any I/O\n if (fs.existsSync(PAUSE_FLAG_PATH)) return\n\n const config = readConfig()\n const { command, cwd, exitCode, durationMs } = opts\n\n // 1. Filter noise commands\n if (shouldFilter(command, config.capture.ignore)) return\n\n // 2. Deduplicate: same command + same cwd within 5 seconds\n const last = getLastEvent()\n if (isDuplicate(command, cwd, last ?? undefined)) return\n\n // 3. Redact sensitive patterns\n const safeCommand = redact(command, config.redact.extra)\n\n // 4. Extract git context\n const { repoName, branch } = getGitContext(cwd)\n\n // 5. Normalize to typed event\n const { category, metadata } = normalizeCommand(safeCommand)\n\n // 6. Persist\n insertEvent({\n captured_at: new Date().toISOString(),\n command: safeCommand,\n cwd,\n exit_code: exitCode,\n duration_ms: durationMs,\n repo_name: repoName,\n branch,\n category,\n metadata: Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null,\n })\n}\n","import { DatabaseSync } from 'node:sqlite'\nimport fs from 'fs'\nimport { WORKSCRIBE_DIR, DB_PATH } from '../utils/paths'\nimport type { EventRow, SessionRow } from './types'\nexport type { EventRow, SessionRow } from './types'\n\nlet _db: DatabaseSync | null = null\n\nexport function getDb(): DatabaseSync {\n if (_db) return _db\n\n // Ensure ~/.workscribe/ exists\n if (!fs.existsSync(WORKSCRIBE_DIR)) {\n fs.mkdirSync(WORKSCRIBE_DIR, { recursive: true })\n }\n\n _db = new DatabaseSync(DB_PATH)\n // Restrict DB file to owner-only after creation\n try { fs.chmodSync(DB_PATH, 0o600) } catch { /* ignore if already correct */ }\n _db.exec('PRAGMA journal_mode = WAL')\n _db.exec('PRAGMA foreign_keys = ON')\n runMigrations(_db)\n return _db\n}\n\nfunction runMigrations(db: DatabaseSync): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n captured_at TEXT NOT NULL,\n command TEXT NOT NULL,\n cwd TEXT NOT NULL,\n exit_code INTEGER,\n duration_ms INTEGER,\n repo_name TEXT,\n branch TEXT,\n category TEXT NOT NULL DEFAULT 'other',\n metadata TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_events_captured_at ON events(captured_at);\n CREATE INDEX IF NOT EXISTS idx_events_repo_name ON events(repo_name);\n\n CREATE TABLE IF NOT EXISTS sessions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n date TEXT NOT NULL,\n started_at TEXT NOT NULL,\n ended_at TEXT NOT NULL,\n repo_name TEXT,\n event_count INTEGER NOT NULL DEFAULT 0,\n summary TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_sessions_date ON sessions(date);\n\n CREATE TABLE IF NOT EXISTS session_events (\n session_id INTEGER NOT NULL REFERENCES sessions(id),\n event_id INTEGER NOT NULL REFERENCES events(id),\n PRIMARY KEY (session_id, event_id)\n );\n `)\n}\n\n// ─── Typed query helpers ──────────────────────────────────────────────────────\n\nexport function insertEvent(params: {\n captured_at: string\n command: string\n cwd: string\n exit_code: number | null\n duration_ms: number | null\n repo_name: string | null\n branch: string | null\n category: string\n metadata: string | null\n}): void {\n const db = getDb()\n const stmt = db.prepare(`\n INSERT INTO events (captured_at, command, cwd, exit_code, duration_ms, repo_name, branch, category, metadata)\n VALUES ($captured_at, $command, $cwd, $exit_code, $duration_ms, $repo_name, $branch, $category, $metadata)\n `)\n stmt.run(params)\n}\n\nexport function getLastEvent(): EventRow | undefined {\n const db = getDb()\n const stmt = db.prepare('SELECT * FROM events ORDER BY id DESC LIMIT 1')\n return stmt.get() as EventRow | undefined\n}\n\nexport function getEventsForDate(date: string): EventRow[] {\n const db = getDb()\n const stmt = db.prepare(`\n SELECT * FROM events\n WHERE date(captured_at) = $date\n ORDER BY captured_at ASC\n `)\n return stmt.all({ date }) as unknown as EventRow[]\n}\n\nexport function getRecentEvents(limit = 20): EventRow[] {\n const db = getDb()\n const stmt = db.prepare('SELECT * FROM events ORDER BY captured_at DESC LIMIT $limit')\n return stmt.all({ limit }) as unknown as EventRow[]\n}\n\nexport function getEventCount(): number {\n const db = getDb()\n const stmt = db.prepare('SELECT COUNT(*) as count FROM events')\n const row = stmt.get() as { count: number }\n return row.count\n}\n\nexport function insertSession(params: {\n date: string\n started_at: string\n ended_at: string\n repo_name: string | null\n event_count: number\n summary: string | null\n}): number {\n const db = getDb()\n const stmt = db.prepare(`\n INSERT INTO sessions (date, started_at, ended_at, repo_name, event_count, summary)\n VALUES ($date, $started_at, $ended_at, $repo_name, $event_count, $summary)\n `)\n const result = stmt.run(params)\n return Number(result.lastInsertRowid)\n}\n\nexport function insertSessionEvent(sessionId: number, eventId: number): void {\n const db = getDb()\n const stmt = db.prepare(`\n INSERT OR IGNORE INTO session_events (session_id, event_id) VALUES ($session_id, $event_id)\n `)\n stmt.run({ session_id: sessionId, event_id: eventId })\n}\n\nexport function getSessionsForDate(date: string): SessionRow[] {\n const db = getDb()\n const stmt = db.prepare('SELECT * FROM sessions WHERE date = $date ORDER BY started_at ASC')\n return stmt.all({ date }) as unknown as SessionRow[]\n}\n\nexport function updateSessionSummary(sessionId: number, summary: string): void {\n const db = getDb()\n const stmt = db.prepare('UPDATE sessions SET summary = $summary WHERE id = $id')\n stmt.run({ summary, id: sessionId })\n}\n\nexport function clearSessionsForDate(date: string): void {\n const db = getDb()\n // Remove session_events first due to FK constraint\n db.prepare(`\n DELETE FROM session_events WHERE session_id IN (\n SELECT id FROM sessions WHERE date = $date\n )\n `).run({ date })\n db.prepare('DELETE FROM sessions WHERE date = $date').run({ date })\n}\n","import path from 'path'\nimport os from 'os'\n\nexport const WORKSCRIBE_DIR = path.join(os.homedir(), '.workscribe')\nexport const DB_PATH = path.join(WORKSCRIBE_DIR, 'workscribe.db')\nexport const CONFIG_PATH = path.join(WORKSCRIBE_DIR, 'config.json')\nexport const DEBUG_LOG_PATH = path.join(WORKSCRIBE_DIR, 'debug.log')\nexport const EXPORTS_DIR = path.join(WORKSCRIBE_DIR, 'exports')\nexport const PAUSE_FLAG_PATH = path.join(WORKSCRIBE_DIR, 'paused')\n","// Patterns that match known sensitive values\nconst BUILTIN_PATTERNS: RegExp[] = [\n // AWS access keys\n /AKIA[0-9A-Z]{16}/g,\n // AWS secret access keys (40 char base64-ish after common env vars)\n /(?<=AWS_SECRET_ACCESS_KEY[=\\s]+)\\S+/gi,\n // GitHub tokens\n /ghp_[a-zA-Z0-9]{36}/g,\n /github_pat_[a-zA-Z0-9_]{82}/g,\n // GitLab tokens\n /glpat-[a-zA-Z0-9\\-]{20}/g,\n // Generic JWTs (three base64url segments)\n /eyJ[a-zA-Z0-9_-]+\\.eyJ[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_-]+/g,\n // Slack tokens\n /xox[baprs]-[a-zA-Z0-9\\-]+/g,\n // Stripe keys\n /sk_(live|test)_[a-zA-Z0-9]{24,}/g,\n // Values after long-form secret flags (single-char flags like -t/-p/-k are too ambiguous)\n /(?<=--(password|passwd|secret|token|key|api[-_]?key|auth[-_]?token)\\s+)\\S+/gi,\n // export VAR=VALUE where VAR looks sensitive\n /(?<=export\\s+(?:[A-Z_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL)[A-Z_]*)=)\\S+/gi,\n]\n\nexport function redact(command: string, extraPatterns: string[] = []): string {\n let result = command\n\n const allPatterns = [...BUILTIN_PATTERNS]\n\n for (const pattern of extraPatterns) {\n try {\n allPatterns.push(new RegExp(pattern, 'g'))\n } catch {\n // Invalid regex — skip silently\n }\n }\n\n for (const pattern of allPatterns) {\n // Reset lastIndex for global regexes\n pattern.lastIndex = 0\n result = result.replace(pattern, '[REDACTED]')\n }\n\n return result\n}\n","import { execSync } from 'child_process'\nimport path from 'path'\n\nexport interface GitContext {\n repoName: string | null\n branch: string | null\n}\n\nexport function getGitContext(cwd: string): GitContext {\n try {\n const root = execSync('git rev-parse --show-toplevel', {\n cwd,\n timeout: 2000,\n stdio: 'pipe',\n })\n .toString()\n .trim()\n\n const repoName = path.basename(root)\n\n let branch: string | null = null\n try {\n branch = execSync('git branch --show-current', {\n cwd,\n timeout: 2000,\n stdio: 'pipe',\n })\n .toString()\n .trim() || null\n } catch {\n // Detached HEAD or not a git repo\n }\n\n return { repoName, branch }\n } catch {\n return { repoName: null, branch: null }\n }\n}\n","export interface NormalizedEvent {\n category: string\n metadata: Record<string, unknown>\n}\n\ninterface Rule {\n // Returns metadata if command matches, null otherwise\n match(cmd: string): NormalizedEvent | null\n}\n\nfunction makeRule(\n pattern: RegExp,\n category: string,\n extract: (match: RegExpMatchArray, cmd: string) => Record<string, unknown> = () => ({})\n): Rule {\n return {\n match(cmd) {\n const m = cmd.match(pattern)\n if (!m) return null\n return { category, metadata: extract(m, cmd) }\n },\n }\n}\n\n// Extract package names from install commands like: npm install foo bar\nfunction extractPackages(cmd: string, afterKeyword: string): string[] {\n const idx = cmd.indexOf(afterKeyword)\n if (idx === -1) return []\n return cmd\n .slice(idx + afterKeyword.length)\n .trim()\n .split(/\\s+/)\n .filter((p) => p && !p.startsWith('-'))\n}\n\nexport const RULES: Rule[] = [\n // ─── Git operations ───────────────────────────────────────────────────────\n makeRule(/^git\\s+commit\\b/, 'code_commit', (_m, cmd) => {\n const msgMatch = cmd.match(/-m\\s+[\"']?([^\"']+)[\"']?/)\n return { message: msgMatch ? msgMatch[1].trim() : null }\n }),\n\n makeRule(/^git\\s+push\\b/, 'code_push', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { remote: parts[2] ?? 'origin', branch: parts[3] ?? null }\n }),\n\n makeRule(/^git\\s+(pull|fetch)\\b/, 'code_pull', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], remote: parts[2] ?? 'origin' }\n }),\n\n makeRule(/^git\\s+(checkout|switch)\\b/, 'branch_switch', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n const branch = parts.find((p) => !p.startsWith('-') && p !== 'git' && p !== parts[1])\n return { branch: branch ?? null }\n }),\n\n makeRule(/^git\\s+(merge|rebase)\\b/, 'code_merge', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], target: parts[2] ?? null }\n }),\n\n makeRule(/^git\\s+stash\\b/, 'code_stash', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[2] ?? 'push' }\n }),\n\n makeRule(/^git\\s+/, 'git_other', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1] ?? null }\n }),\n\n // ─── Package managers — install ───────────────────────────────────────────\n makeRule(/^npm\\s+(install|i|add)\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'npm',\n packages: extractPackages(cmd, cmd.includes('install') ? 'install' : cmd.includes(' i ') ? ' i ' : 'add'),\n })),\n\n makeRule(/^(yarn|pnpm)\\s+(install|add)\\b/, 'dependency_install', (m, cmd) => ({\n tool: m[1],\n packages: extractPackages(cmd, 'add'),\n })),\n\n makeRule(/^pip\\s+(install)\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'pip',\n packages: extractPackages(cmd, 'install'),\n })),\n\n makeRule(/^cargo\\s+add\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'cargo',\n packages: extractPackages(cmd, 'add'),\n })),\n\n makeRule(/^(brew)\\s+install\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'brew',\n packages: extractPackages(cmd, 'install'),\n })),\n\n makeRule(/^go\\s+get\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'go',\n packages: extractPackages(cmd, 'get'),\n })),\n\n // ─── Package managers — remove ────────────────────────────────────────────\n makeRule(/^npm\\s+(uninstall|remove|rm)\\b/, 'dependency_remove', (_m, cmd) => ({\n tool: 'npm',\n packages: extractPackages(cmd, cmd.split(/\\s+/)[1]),\n })),\n\n makeRule(/^(yarn|pnpm)\\s+remove\\b/, 'dependency_remove', (m, cmd) => ({\n tool: m[1],\n packages: extractPackages(cmd, 'remove'),\n })),\n\n makeRule(/^pip\\s+uninstall\\b/, 'dependency_remove', (_m, cmd) => ({\n tool: 'pip',\n packages: extractPackages(cmd, 'uninstall'),\n })),\n\n // ─── Tests ────────────────────────────────────────────────────────────────\n makeRule(/^(npm\\s+(test|run\\s+test)|yarn\\s+test|pnpm\\s+test)\\b/, 'test_run', () => ({ tool: 'npm' })),\n makeRule(/^(jest|vitest)\\b/, 'test_run', (m) => ({ tool: m[1] })),\n makeRule(/^pytest\\b/, 'test_run', () => ({ tool: 'pytest' })),\n makeRule(/^go\\s+test\\b/, 'test_run', () => ({ tool: 'go' })),\n makeRule(/^cargo\\s+test\\b/, 'test_run', () => ({ tool: 'cargo' })),\n makeRule(/^rspec\\b/, 'test_run', () => ({ tool: 'rspec' })),\n makeRule(/^(php\\s+)?phpunit\\b/, 'test_run', () => ({ tool: 'phpunit' })),\n\n // ─── Build ────────────────────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+build\\b/, 'build', () => ({ tool: 'npm' })),\n makeRule(/^(yarn|pnpm)\\s+build\\b/, 'build', (m) => ({ tool: m[1] })),\n makeRule(/^(make|gmake)\\b/, 'build', (m) => ({ tool: m[1] })),\n makeRule(/^cargo\\s+build\\b/, 'build', () => ({ tool: 'cargo' })),\n makeRule(/^go\\s+build\\b/, 'build', () => ({ tool: 'go' })),\n makeRule(/^docker\\s+build\\b/, 'build', () => ({ tool: 'docker' })),\n makeRule(/^(mvn|gradle)\\b/, 'build', (m) => ({ tool: m[1] })),\n\n // ─── Dev server ───────────────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+(dev|start|serve)\\b/, 'server_run', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n makeRule(/^(yarn|pnpm)\\s+(dev|start)\\b/, 'server_run', (m) => ({ tool: m[1], script: m[2] })),\n makeRule(/^python\\s+.*manage\\.py\\s+runserver\\b/, 'server_run', () => ({ tool: 'django' })),\n\n // ─── Infrastructure ───────────────────────────────────────────────────────\n makeRule(/^ssh\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'ssh', target: parts[parts.length - 1] }\n }),\n\n makeRule(/^kubectl\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'kubectl', subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^(helm|terraform|pulumi)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^docker\\s+(run|compose|ps|stop|start|rm|exec)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'docker', subcommand: parts[1] }\n }),\n\n makeRule(/^(aws|gcloud|az)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n // ─── Scripts ──────────────────────────────────────────────────────────────\n makeRule(/^(bash|sh|zsh|fish)\\s+\\S+/, 'script_run', (m) => ({ shell: m[1] })),\n makeRule(/^node\\s+\\S+\\.(?:js|mjs|cjs)\\b/, 'script_run', () => ({ runtime: 'node' })),\n makeRule(/^python3?\\s+\\S+\\.py\\b/, 'script_run', () => ({ runtime: 'python' })),\n\n // ─── Environment ──────────────────────────────────────────────────────────\n makeRule(/^export\\s+\\w+=/, 'environment', (_m, cmd) => {\n const varName = cmd.match(/^export\\s+(\\w+)=/)?.[1] ?? null\n return { variable: varName }\n }),\n makeRule(/^source\\s+/, 'environment', () => ({})),\n\n // ─── npm scripts (generic) ────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+\\S+/, 'script_run', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n]\n\nexport function normalizeCommand(command: string): NormalizedEvent {\n const trimmed = command.trim().replace(/\\s+/g, ' ')\n\n for (const rule of RULES) {\n const result = rule.match(trimmed)\n if (result) return result\n }\n\n return { category: 'other', metadata: {} }\n}\n","import fs from 'fs'\nimport { CONFIG_PATH } from '../utils/paths'\n\nexport interface WorkscribeConfig {\n ai: {\n provider: string // 'anthropic' | 'openai' | 'ollama' | 'openai-compatible'\n apiKey: string\n model: string\n baseUrl: string\n }\n session: {\n idleTimeout: number // minutes\n }\n capture: {\n ignore: string[]\n }\n redact: {\n extra: string[]\n }\n summary: {\n format: 'markdown' | 'json'\n }\n}\n\nconst DEFAULTS: WorkscribeConfig = {\n ai: {\n provider: '',\n apiKey: '',\n model: '',\n baseUrl: '',\n },\n session: {\n idleTimeout: 30,\n },\n capture: {\n ignore: ['cd', 'ls', 'll', 'la', 'ls -la', 'ls -l', 'ls -a', 'pwd', 'clear', 'exit', 'history', 'man'],\n },\n redact: {\n extra: [],\n },\n summary: {\n format: 'markdown',\n },\n}\n\nexport function readConfig(): WorkscribeConfig {\n if (!fs.existsSync(CONFIG_PATH)) return { ...DEFAULTS }\n try {\n const raw = fs.readFileSync(CONFIG_PATH, 'utf-8')\n const parsed = JSON.parse(raw)\n return deepMerge(DEFAULTS, parsed)\n } catch {\n return { ...DEFAULTS }\n }\n}\n\nexport function writeConfig(config: WorkscribeConfig): void {\n fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 })\n // Enforce permissions on existing files (mode only applies on creation above)\n try { fs.chmodSync(CONFIG_PATH, 0o600) } catch { /* ignore */ }\n}\n\nexport function getConfigValue(config: WorkscribeConfig, key: string): unknown {\n const parts = key.split('.')\n let cur: unknown = config\n for (const part of parts) {\n if (cur == null || typeof cur !== 'object') return undefined\n cur = (cur as Record<string, unknown>)[part]\n }\n return cur\n}\n\nexport function setConfigValue(config: WorkscribeConfig, key: string, value: string): WorkscribeConfig {\n const parts = key.split('.')\n const result = JSON.parse(JSON.stringify(config)) as WorkscribeConfig\n let cur = result as unknown as Record<string, unknown>\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]\n if (cur[part] == null || typeof cur[part] !== 'object') {\n cur[part] = {}\n }\n cur = cur[part] as Record<string, unknown>\n }\n const lastKey = parts[parts.length - 1]\n // Coerce value type\n if (value === 'true') cur[lastKey] = true\n else if (value === 'false') cur[lastKey] = false\n else if (!isNaN(Number(value))) cur[lastKey] = Number(value)\n else cur[lastKey] = value\n return result\n}\n\nfunction deepMerge<T extends object>(defaults: T, overrides: Partial<T>): T {\n const result = { ...defaults }\n for (const key of Object.keys(overrides) as (keyof T)[]) {\n const val = overrides[key]\n if (val !== null && typeof val === 'object' && !Array.isArray(val) && typeof defaults[key] === 'object') {\n result[key] = deepMerge(defaults[key] as object, val as object) as T[keyof T]\n } else if (val !== undefined) {\n result[key] = val as T[keyof T]\n }\n }\n return result\n}\n","import type { EventRow } from '../db/types'\n\nfunction normalizeForFilter(cmd: string): string {\n return cmd.trim().replace(/\\s+/g, ' ')\n}\n\n/**\n * Returns true if the command should be dropped (noise / ignore list).\n */\nexport function shouldFilter(command: string, ignoreList: string[]): boolean {\n const normalized = normalizeForFilter(command)\n if (!normalized) return true\n\n const normalizedIgnore = ignoreList.map(normalizeForFilter)\n if (normalizedIgnore.includes(normalized)) return true\n\n // Also drop if just the base command (no args) is in the ignore list\n const baseCmd = normalized.split(' ')[0]\n if (normalizedIgnore.includes(baseCmd)) return true\n\n return false\n}\n\n/**\n * Returns true if this command is a duplicate of the last event\n * (same command + same cwd within the dedup window).\n */\nexport function isDuplicate(\n command: string,\n cwd: string,\n lastEvent: Pick<EventRow, 'command' | 'cwd' | 'captured_at'> | undefined,\n dedupWindowMs = 5000,\n now = Date.now()\n): boolean {\n if (!lastEvent) return false\n const lastTime = new Date(lastEvent.captured_at).getTime()\n return (\n lastEvent.command === normalizeForFilter(command) &&\n lastEvent.cwd === cwd &&\n now - lastTime < dedupWindowMs\n )\n}\n","import chalk from 'chalk'\nimport fs from 'fs'\nimport { DEBUG_LOG_PATH } from './paths'\n\nconst isDebug = process.env.WORKSCRIBE_DEBUG === '1'\n\nexport const log = {\n info: (msg: string) => console.log(chalk.cyan('→'), msg),\n success: (msg: string) => console.log(chalk.green('✓'), msg),\n warn: (msg: string) => console.log(chalk.yellow('⚠'), msg),\n error: (msg: string) => console.error(chalk.red('✗'), msg),\n plain: (msg: string) => console.log(msg),\n}\n\nexport function debugLog(context: string, error: unknown): void {\n if (!isDebug) return\n const entry = `[${new Date().toISOString()}] [${context}] ${error instanceof Error ? error.stack : String(error)}\\n`\n try {\n fs.appendFileSync(DEBUG_LOG_PATH, entry, { mode: 0o600 })\n } catch {\n // If we can't write to the debug log, silently ignore\n }\n}\n","import { Command } from 'commander'\nimport { captureCommand } from '../capture'\nimport { debugLog } from '../utils/logger'\n\nexport function registerCapture(program: Command): void {\n // Internal command — called by the shell hook, not by the user\n program\n .command('_capture')\n .description('Internal: capture a shell command event (called by shell hook)')\n .option('--cmd <command>', 'command that was executed')\n .option('--cwd <path>', 'working directory')\n .option('--exit <code>', 'exit code', parseInt)\n .option('--duration <ms>', 'duration in milliseconds', parseInt)\n .action((opts) => {\n // Never surface errors to the terminal — this runs silently in the background\n try {\n if (!opts.cmd || !opts.cwd) return\n captureCommand({\n command: opts.cmd,\n cwd: opts.cwd,\n exitCode: opts.exit ?? null,\n durationMs: opts.duration ?? null,\n })\n } catch (err) {\n debugLog('_capture', err)\n }\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport prompts from 'prompts'\nimport chalk from 'chalk'\nimport { WORKSCRIBE_DIR, CONFIG_PATH } from '../utils/paths'\nimport { readConfig, writeConfig } from '../config'\nimport { getDb } from '../db'\nimport { log } from '../utils/logger'\n\nconst HOOK_START = '# workscribe:hook:start'\nconst HOOK_END = '# workscribe:hook:end'\n\nfunction detectShell(): { name: string; rcFile: string } | null {\n const shell = process.env.SHELL ?? ''\n if (shell.includes('zsh')) return { name: 'zsh', rcFile: path.join(os.homedir(), '.zshrc') }\n if (shell.includes('bash')) return { name: 'bash', rcFile: path.join(os.homedir(), '.bashrc') }\n return null\n}\n\nfunction isHookInstalled(rcFile: string): boolean {\n if (!fs.existsSync(rcFile)) return false\n return fs.readFileSync(rcFile, 'utf-8').includes(HOOK_START)\n}\n\nfunction installHook(rcFile: string, hookContent: string): void {\n // Back up the rc file\n const backup = `${rcFile}.workscribe-backup-${Date.now()}`\n fs.copyFileSync(rcFile, backup)\n log.info(`Backed up ${rcFile} → ${path.basename(backup)}`)\n\n // Append hook\n fs.appendFileSync(rcFile, `\\n${hookContent}\\n`)\n}\n\nexport function registerInit(program: Command): void {\n program\n .command('init')\n .description('Install shell hooks and set up Workscribe')\n .action(async () => {\n log.plain(chalk.bold('\\n Workscribe — Developer Work Journal\\n'))\n\n // 1. Create ~/.workscribe/ with owner-only permissions\n if (!fs.existsSync(WORKSCRIBE_DIR)) {\n fs.mkdirSync(WORKSCRIBE_DIR, { recursive: true, mode: 0o700 })\n log.success(`Created ${WORKSCRIBE_DIR}`)\n } else {\n // Secure the directory for existing installs\n try { fs.chmodSync(WORKSCRIBE_DIR, 0o700) } catch { /* ignore */ }\n }\n\n // 2. Initialize database (runs migrations)\n getDb()\n log.success('Database initialized')\n\n // 3. Detect shell\n const shell = detectShell()\n if (!shell) {\n log.warn('Could not detect shell. Supported: zsh, bash')\n log.plain(`Manually add the hook from: ${chalk.cyan('shell/hook.zsh')} or ${chalk.cyan('shell/hook.bash')}`)\n } else {\n if (isHookInstalled(shell.rcFile)) {\n log.info(`Shell hook already installed in ${shell.rcFile}`)\n } else {\n // Load hook template from package\n const hookFile = path.join(__dirname, '..', 'shell', `hook.${shell.name}`)\n if (!fs.existsSync(hookFile)) {\n log.warn(`Hook template not found at ${hookFile}`)\n } else {\n const hookContent = fs.readFileSync(hookFile, 'utf-8')\n if (!fs.existsSync(shell.rcFile)) fs.writeFileSync(shell.rcFile, '')\n installHook(shell.rcFile, hookContent)\n log.success(`Shell hook installed in ${shell.rcFile}`)\n }\n }\n }\n\n // 4. AI provider setup\n const config = readConfig()\n\n if (config.ai.provider) {\n log.info(`AI provider already configured: ${chalk.cyan(config.ai.provider)}`)\n } else {\n log.plain('')\n const { provider } = await prompts({\n type: 'select',\n name: 'provider',\n message: 'Which AI provider do you want to use?',\n choices: [\n { title: 'Anthropic (Claude) — requires API key', value: 'anthropic' },\n { title: 'OpenAI — requires API key', value: 'openai' },\n { title: 'Ollama (local) — free, runs on your machine', value: 'ollama' },\n { title: 'Other (OpenAI-compatible) — Groq, Together, Mistral…', value: 'openai-compatible' },\n { title: 'Skip — configure later', value: 'skip' },\n ],\n })\n\n if (provider && provider !== 'skip') {\n config.ai.provider = provider\n\n if (provider === 'ollama') {\n log.plain(`\\n ${chalk.dim('Make sure Ollama is running: ollama serve')}`)\n log.plain(` ${chalk.dim('Default model: llama3 (change with: workscribe config set ai.model <model>)')}\\n`)\n } else {\n const { apiKey } = await prompts({\n type: 'password',\n name: 'apiKey',\n message: `Enter your ${provider === 'openai-compatible' ? '' : provider + ' '}API key:`,\n })\n if (apiKey) config.ai.apiKey = apiKey\n\n if (provider === 'openai-compatible') {\n const { baseUrl } = await prompts({\n type: 'text',\n name: 'baseUrl',\n message: 'Enter the base URL (e.g. https://api.groq.com/openai/v1):',\n })\n if (baseUrl) config.ai.baseUrl = baseUrl\n }\n }\n\n writeConfig(config)\n log.success(`AI provider set to ${chalk.cyan(provider)}`)\n } else if (provider === 'skip' || !provider) {\n log.info('Skipped — run later: workscribe config set ai.provider <provider>')\n }\n }\n\n // 5. Write config if not exists\n if (!fs.existsSync(CONFIG_PATH)) {\n writeConfig(config)\n log.success('Config file created')\n }\n\n log.plain('')\n log.plain(chalk.bold(' Setup complete!'))\n if (shell) {\n log.plain('')\n log.plain(` Activate capture now: ${chalk.cyan(`source ${shell.rcFile}`)}`)\n log.plain(` Or restart your terminal.`)\n }\n log.plain('')\n log.plain(` ${chalk.dim('workscribe summary')} — generate today\\'s work summary`)\n log.plain(` ${chalk.dim('workscribe status')} — check capture is working`)\n log.plain('')\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport chalk from 'chalk'\nimport { getEventCount, getLastEvent } from '../db'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { formatDate, todayDate } from '../utils/format'\nimport { getEventsForDate } from '../db'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\n\nexport function registerStatus(program: Command): void {\n program\n .command('status')\n .description('Show capture status and event counts')\n .action(() => {\n const config = readConfig()\n const total = getEventCount()\n const todayEvents = getEventsForDate(todayDate())\n const last = getLastEvent()\n const isPaused = fs.existsSync(PAUSE_FLAG_PATH)\n\n log.plain('')\n log.plain(chalk.bold(' Workscribe Status'))\n log.plain('')\n\n if (isPaused) {\n log.plain(` Capture ${chalk.yellow('paused')} ${chalk.dim('(run: workscribe resume)')}`)\n } else {\n log.plain(` Capture ${chalk.green('active')}`)\n }\n\n log.plain(` Events today ${chalk.cyan(String(todayEvents.length))}`)\n log.plain(` Events total ${chalk.cyan(String(total))}`)\n\n if (last) {\n log.plain(` Last event ${formatDate(last.captured_at)} ${chalk.dim(last.command.slice(0, 60))}`)\n }\n\n log.plain('')\n log.plain(` AI provider ${config.ai.provider ? chalk.cyan(config.ai.provider) : chalk.yellow('not configured')}`)\n if (config.ai.model) log.plain(` Model ${chalk.dim(config.ai.model)}`)\n log.plain('')\n })\n}\n","export function formatDuration(minutes: number): string {\n if (minutes < 60) return `${Math.round(minutes)}m`\n const h = Math.floor(minutes / 60)\n const m = Math.round(minutes % 60)\n return m > 0 ? `${h}h ${m}m` : `${h}h`\n}\n\nexport function formatDate(isoString: string): string {\n return new Date(isoString).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\nexport function todayDate(): string {\n return new Date().toISOString().split('T')[0]\n}\n\nexport function parseDate(input: string): string {\n // Accepts YYYY-MM-DD, returns YYYY-MM-DD. Throws on invalid.\n const d = new Date(input + 'T00:00:00')\n if (isNaN(d.getTime())) throw new Error(`Invalid date: ${input}`)\n return input\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getEventsForDate } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\n\nconst CATEGORY_COLORS: Record<string, (s: string) => string> = {\n code_commit: chalk.green,\n code_push: chalk.green,\n code_pull: chalk.blue,\n branch_switch: chalk.blue,\n dependency_install: chalk.yellow,\n dependency_remove: chalk.yellow,\n test_run: chalk.magenta,\n build: chalk.cyan,\n server_run: chalk.cyan,\n infrastructure: chalk.red,\n script_run: chalk.white,\n environment: chalk.gray,\n git_other: chalk.blue,\n code_merge: chalk.green,\n code_stash: chalk.blue,\n other: chalk.gray,\n}\n\nexport function registerEvents(program: Command): void {\n program\n .command('events')\n .description('List captured events')\n .option('--date <YYYY-MM-DD>', 'date to list events for (default: today)')\n .option('--limit <n>', 'max events to show', parseInt)\n .action((opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const events = getEventsForDate(date)\n const limit = opts.limit ?? events.length\n\n if (events.length === 0) {\n log.plain(`No events captured for ${date}`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Events for ${date} (${events.length} total)`))\n log.plain('')\n\n for (const event of events.slice(0, limit)) {\n const colorFn = CATEGORY_COLORS[event.category] ?? chalk.gray\n const time = formatDate(event.captured_at)\n const category = colorFn(event.category.padEnd(20))\n const cmd = chalk.dim(event.command.slice(0, 55))\n const repo = event.repo_name ? chalk.dim(` [${event.repo_name}]`) : ''\n const exitBadge = event.exit_code && event.exit_code !== 0 ? chalk.red(` ✗${event.exit_code}`) : ''\n log.plain(` ${chalk.dim(time)} ${category} ${cmd}${repo}${exitBadge}`)\n }\n\n if (limit < events.length) {\n log.plain('')\n log.plain(chalk.dim(` … and ${events.length - limit} more (remove --limit to see all)`))\n }\n\n log.plain('')\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate, formatDuration } from '../utils/format'\n\nexport function registerSessions(program: Command): void {\n program\n .command('sessions')\n .description('List detected work sessions')\n .option('--date <YYYY-MM-DD>', 'date to show sessions for (default: today)')\n .action((opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const sessions = getOrBuildSessions(date)\n\n if (sessions.length === 0) {\n log.plain(`No sessions detected for ${date}`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Sessions for ${date} (${sessions.length} session${sessions.length > 1 ? 's' : ''})`))\n log.plain('')\n\n for (const session of sessions) {\n const start = formatDate(session.startedAt)\n const end = formatDate(session.endedAt)\n const duration = formatDuration(session.durationMinutes)\n const repo = session.repoName ? chalk.cyan(session.repoName) : chalk.gray('no repo')\n log.plain(` ${chalk.dim(start)} – ${chalk.dim(end)} ${repo} ${chalk.dim(duration)} ${chalk.dim(session.eventCount + ' events')}`)\n }\n\n log.plain('')\n })\n}\n","import type { EventRow } from '../db/types'\n\n/**\n * Split a sorted list of events into groups separated by idle gaps.\n */\nexport function splitByIdleGap(events: EventRow[], idleMs: number): EventRow[][] {\n if (events.length === 0) return []\n const groups: EventRow[][] = [[events[0]]]\n\n for (let i = 1; i < events.length; i++) {\n const prev = new Date(events[i - 1].captured_at).getTime()\n const curr = new Date(events[i].captured_at).getTime()\n if (curr - prev >= idleMs) {\n groups.push([events[i]])\n } else {\n groups[groups.length - 1].push(events[i])\n }\n }\n\n return groups\n}\n\n/**\n * Split a list of events into groups by consecutive repo_name.\n * A repo change mid-stream creates a new group.\n */\nexport function splitByRepo(events: EventRow[]): EventRow[][] {\n if (events.length === 0) return []\n const groups: EventRow[][] = [[events[0]]]\n\n for (let i = 1; i < events.length; i++) {\n const prevRepo = events[i - 1].repo_name\n const currRepo = events[i].repo_name\n if (currRepo !== prevRepo) {\n groups.push([events[i]])\n } else {\n groups[groups.length - 1].push(events[i])\n }\n }\n\n return groups\n}\n\nexport function minutesBetween(start: string, end: string): number {\n return (new Date(end).getTime() - new Date(start).getTime()) / 60000\n}\n","import type { EventRow } from '../db/types'\nimport {\n insertSession,\n insertSessionEvent,\n clearSessionsForDate,\n getSessionsForDate,\n getEventsForDate,\n} from '../db'\nimport { readConfig } from '../config'\nimport { splitByIdleGap, splitByRepo, minutesBetween } from './grouper'\n\nexport interface Session {\n id: number\n date: string\n startedAt: string\n endedAt: string\n repoName: string | null\n durationMinutes: number\n eventCount: number\n summary: string | null\n events: EventRow[]\n}\n\nexport function getOrBuildSessions(date: string): Session[] {\n // Check if sessions already exist for this date\n const existing = getSessionsForDate(date)\n const events = getEventsForDate(date)\n\n if (events.length === 0) return []\n\n // If sessions exist and event count matches, use cached\n const cachedEventCount = existing.reduce((sum, s) => sum + s.event_count, 0)\n if (existing.length > 0 && cachedEventCount === events.length) {\n return existing.map((s) => ({\n id: s.id,\n date: s.date,\n startedAt: s.started_at,\n endedAt: s.ended_at,\n repoName: s.repo_name,\n durationMinutes: minutesBetween(s.started_at, s.ended_at),\n eventCount: s.event_count,\n summary: s.summary,\n events: [], // not needed when using cached\n }))\n }\n\n // Rebuild sessions\n clearSessionsForDate(date)\n return buildSessions(date, events)\n}\n\nfunction buildSessions(date: string, events: EventRow[]): Session[] {\n const config = readConfig()\n const idleMs = config.session.idleTimeout * 60 * 1000\n\n // Group events by repo, then split by idle gap within each repo group\n const sessions: Session[] = []\n\n // Split by idle gaps first (regardless of repo)\n const timeGroups = splitByIdleGap(events, idleMs)\n\n for (const group of timeGroups) {\n if (group.length === 0) continue\n\n // Within each time group, further split by repo change\n const repoGroups = splitByRepo(group)\n\n for (const repoGroup of repoGroups) {\n if (repoGroup.length === 0) continue\n\n const first = repoGroup[0]\n const last = repoGroup[repoGroup.length - 1]\n const repoName = last.repo_name ?? first.repo_name ?? null\n\n const sessionId = insertSession({\n date,\n started_at: first.captured_at,\n ended_at: last.captured_at,\n repo_name: repoName,\n event_count: repoGroup.length,\n summary: null,\n })\n\n for (const event of repoGroup) {\n insertSessionEvent(sessionId, event.id)\n }\n\n sessions.push({\n id: sessionId,\n date,\n startedAt: first.captured_at,\n endedAt: last.captured_at,\n repoName,\n durationMinutes: minutesBetween(first.captured_at, last.captured_at),\n eventCount: repoGroup.length,\n summary: null,\n events: repoGroup,\n })\n }\n }\n\n return sessions\n}\n\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { getProvider } from '../ai/router'\nimport { readConfig } from '../config'\nimport { updateSessionSummary } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\nimport type { DaySummaryPayload, SessionPayload, EventPayload } from '../ai/types'\nimport type { EventRow } from '../db'\n\nfunction eventRowToPayload(row: EventRow): EventPayload {\n let metadata: Record<string, unknown> = {}\n try {\n if (row.metadata) metadata = JSON.parse(row.metadata)\n } catch { /* ignore */ }\n return {\n category: row.category,\n metadata,\n exitCode: row.exit_code,\n durationMs: row.duration_ms,\n }\n}\n\nexport function registerSummary(program: Command): void {\n program\n .command('summary')\n .description('Generate an AI work summary')\n .option('--date <YYYY-MM-DD>', 'date to summarize (default: today)')\n .option('--week', 'summarize the current week')\n .option('--fresh', 'bypass cache and regenerate')\n .action(async (opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const sessions = getOrBuildSessions(date)\n\n if (sessions.length === 0) {\n log.plain(`No activity captured for ${date}`)\n return\n }\n\n // Check cache (unless --fresh)\n if (!opts.fresh) {\n const allCached = sessions.every((s) => s.summary)\n if (allCached) {\n log.plain('')\n for (const s of sessions) {\n log.plain(s.summary!)\n }\n log.plain('')\n log.plain(chalk.dim(' (cached — use --fresh to regenerate)'))\n return\n }\n }\n\n // Build payload\n const config = readConfig()\n let provider\n try {\n provider = getProvider(config)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n\n const sessionPayloads: SessionPayload[] = sessions.map((s) => ({\n date,\n project: s.repoName,\n branch: s.events[0]?.branch ?? null,\n startTime: formatDate(s.startedAt),\n endTime: formatDate(s.endedAt),\n durationMinutes: s.durationMinutes,\n events: s.events.map(eventRowToPayload),\n }))\n\n const payload: DaySummaryPayload = { date, sessions: sessionPayloads }\n\n log.plain('')\n log.info(`Generating summary via ${chalk.cyan(config.ai.provider)}…`)\n\n try {\n const summary = await provider.summarize(payload)\n log.plain('')\n log.plain(summary)\n log.plain('')\n\n // Cache per session (store full summary on first session for simplicity)\n if (sessions[0]) {\n updateSessionSummary(sessions[0].id, summary)\n }\n } catch (e) {\n log.error(`Summary failed [${config.ai.provider}]: ${(e as Error).message}`)\n process.exit(1)\n }\n })\n}\n","import type { DaySummaryPayload } from './types'\nimport { formatDuration } from '../utils/format'\n\nexport const SYSTEM_PROMPT = `You write short developer work summaries. Here is an example:\n\nINPUT:\nProject: api-service\nBranch: feature/auth\nDuration: 1h 30m\nEvents:\n - branch_switch {\"branch\":\"feature/auth\"}\n - 2x dependency_install {\"tool\":\"npm\",\"packages\":[\"bcrypt\",\"jsonwebtoken\"]}\n - 2x test_run {\"tool\":\"jest\"} [failed]\n - test_run {\"tool\":\"jest\"}\n - code_commit {\"message\":\"fix: auth token validation\"}\n\nOUTPUT:\n## api-service (feature/auth) — 1h 30m\n- Switched to the authentication feature branch\n- Added JWT and bcrypt dependencies\n- Resolved failing auth tests\n- Committed authentication fixes\n\nNow summarise the following. Output ONLY the summary, nothing else. Maximum 5 bullets.`\n\nexport function buildUserMessage(payload: DaySummaryPayload): string {\n const lines: string[] = [`Date: ${payload.date}`, '']\n\n for (const session of payload.sessions) {\n lines.push(`Project: ${session.project ?? 'unknown'}`)\n if (session.branch) lines.push(`Branch: ${session.branch}`)\n lines.push(`Duration: ${formatDuration(session.durationMinutes)}`)\n lines.push('Events:')\n\n // Aggregate repeated events so the model sees \"3x build\" not 3 separate build lines\n const counts = new Map<string, number>()\n for (const event of session.events) {\n const meta = Object.keys(event.metadata).length > 0 ? ` ${JSON.stringify(event.metadata)}` : ''\n const failed = event.exitCode !== null && event.exitCode !== 0 ? ' [failed]' : ''\n const key = `${event.category}${meta}${failed}`\n counts.set(key, (counts.get(key) ?? 0) + 1)\n }\n\n for (const [key, count] of counts) {\n lines.push(` - ${count > 1 ? `${count}x ` : ''}${key}`)\n }\n\n lines.push('')\n }\n\n return lines.join('\\n')\n}\n","const DEFAULT_TIMEOUT_MS = 30_000\n\nexport async function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs = DEFAULT_TIMEOUT_MS\n): Promise<Response> {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await fetch(url, { ...init, signal: controller.signal })\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Request timed out after ${timeoutMs / 1000}s`)\n }\n throw err\n } finally {\n clearTimeout(timer)\n }\n}\n","import type { AIProvider, DaySummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage } from '../prompt'\nimport { fetchWithTimeout } from '../fetch'\n\nexport class AnthropicProvider implements AIProvider {\n private apiKey: string\n private model: string\n\n constructor(apiKey: string, model?: string) {\n this.apiKey = apiKey\n this.model = model || 'claude-haiku-4-5-20251001'\n }\n\n async summarize(payload: DaySummaryPayload): Promise<string> {\n const response = await fetchWithTimeout('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: this.model,\n max_tokens: 1024,\n system: SYSTEM_PROMPT,\n messages: [{ role: 'user', content: buildUserMessage(payload) }],\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Anthropic API error ${response.status}: ${body}`)\n }\n\n const data = (await response.json()) as { content: Array<{ type: string; text: string }> }\n return data.content.find((c) => c.type === 'text')?.text ?? ''\n }\n}\n","import type { AIProvider, DaySummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage } from '../prompt'\nimport { fetchWithTimeout } from '../fetch'\n\nexport class OpenAIProvider implements AIProvider {\n private apiKey: string\n private model: string\n private baseUrl: string\n\n constructor(apiKey: string, model?: string, baseUrl?: string) {\n this.apiKey = apiKey\n this.model = model || 'gpt-4o-mini'\n this.baseUrl = baseUrl || 'https://api.openai.com/v1'\n }\n\n async summarize(payload: DaySummaryPayload): Promise<string> {\n const response = await fetchWithTimeout(`${this.baseUrl}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n model: this.model,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserMessage(payload) },\n ],\n max_tokens: 1024,\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`OpenAI API error ${response.status}: ${body}`)\n }\n\n const data = (await response.json()) as {\n choices: Array<{ message: { content: string } }>\n }\n return data.choices[0]?.message?.content ?? ''\n }\n}\n","import type { AIProvider, DaySummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage } from '../prompt'\nimport { fetchWithTimeout } from '../fetch'\n\nexport class OllamaProvider implements AIProvider {\n private model: string\n private baseUrl: string\n\n constructor(model?: string, baseUrl?: string) {\n this.model = model || 'llama3'\n this.baseUrl = baseUrl || 'http://localhost:11434'\n }\n\n async summarize(payload: DaySummaryPayload): Promise<string> {\n const response = await fetchWithTimeout(`${this.baseUrl}/api/chat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.model,\n stream: false,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserMessage(payload) },\n ],\n options: {\n num_predict: 300, // hard cap on output tokens — forces conciseness\n temperature: 0.3, // lower temperature = more focused, less wandering\n },\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Ollama error ${response.status}: ${body}. Is Ollama running? Try: ollama serve`)\n }\n\n const data = (await response.json()) as { message: { content: string } }\n return data.message?.content ?? ''\n }\n}\n","import type { AIProvider } from './types'\nimport type { WorkscribeConfig } from '../config'\nimport { AnthropicProvider } from './providers/anthropic'\nimport { OpenAIProvider } from './providers/openai'\nimport { OllamaProvider } from './providers/ollama'\n\nexport function getProvider(config: WorkscribeConfig): AIProvider {\n const { provider, apiKey, model, baseUrl } = config.ai\n\n if (!provider) {\n throw new Error(\n 'No AI provider configured.\\nRun: workscribe config set ai.provider <anthropic|openai|ollama|openai-compatible>'\n )\n }\n\n switch (provider) {\n case 'anthropic': {\n if (!apiKey) throw new Error('Anthropic requires an API key.\\nRun: workscribe config set ai.apiKey <your-key>')\n return new AnthropicProvider(apiKey, model || undefined)\n }\n\n case 'openai': {\n if (!apiKey) throw new Error('OpenAI requires an API key.\\nRun: workscribe config set ai.apiKey <your-key>')\n return new OpenAIProvider(apiKey, model || undefined)\n }\n\n case 'openai-compatible': {\n if (!apiKey) throw new Error('This provider requires an API key.\\nRun: workscribe config set ai.apiKey <your-key>')\n if (!baseUrl) throw new Error('openai-compatible requires a base URL.\\nRun: workscribe config set ai.baseUrl <url>')\n return new OpenAIProvider(apiKey, model || undefined, baseUrl)\n }\n\n case 'ollama': {\n return new OllamaProvider(model || undefined, baseUrl || undefined)\n }\n\n default:\n throw new Error(\n `Unknown provider: \"${provider}\"\\nSupported providers: anthropic, openai, ollama, openai-compatible`\n )\n }\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { getProvider } from '../ai/router'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\nimport { EXPORTS_DIR } from '../utils/paths'\nimport type { DaySummaryPayload, SessionPayload, EventPayload } from '../ai/types'\nimport type { EventRow } from '../db'\n\nfunction eventRowToPayload(row: EventRow): EventPayload {\n let metadata: Record<string, unknown> = {}\n try {\n if (row.metadata) metadata = JSON.parse(row.metadata)\n } catch { /* ignore */ }\n return { category: row.category, metadata, exitCode: row.exit_code, durationMs: row.duration_ms }\n}\n\nexport function registerExport(program: Command): void {\n program\n .command('export')\n .description('Export work summary as a markdown file')\n .option('--date <YYYY-MM-DD>', 'date to export (default: today)')\n .option('--output <path>', 'output file path (default: ~/.workscribe/exports/)')\n .action(async (opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const sessions = getOrBuildSessions(date)\n if (sessions.length === 0) {\n log.plain(`No activity captured for ${date}`)\n return\n }\n\n const config = readConfig()\n let provider\n try {\n provider = getProvider(config)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n\n const sessionPayloads: SessionPayload[] = sessions.map((s) => ({\n date,\n project: s.repoName,\n branch: s.events[0]?.branch ?? null,\n startTime: formatDate(s.startedAt),\n endTime: formatDate(s.endedAt),\n durationMinutes: s.durationMinutes,\n events: s.events.map(eventRowToPayload),\n }))\n\n const payload: DaySummaryPayload = { date, sessions: sessionPayloads }\n\n log.info(`Generating summary via ${config.ai.provider}…`)\n const summary = await provider.summarize(payload)\n\n const outputPath = opts.output ?? path.join(EXPORTS_DIR, `workscribe-${date}.md`)\n\n if (!fs.existsSync(EXPORTS_DIR)) fs.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 0o700 })\n\n fs.writeFileSync(outputPath, `# Work Summary — ${date}\\n\\n${summary}\\n`, { encoding: 'utf-8', mode: 0o600 })\n log.success(`Saved: ${outputPath}`)\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { readConfig, writeConfig, getConfigValue, setConfigValue } from '../config'\nimport { log } from '../utils/logger'\n\nexport function registerConfig(program: Command): void {\n const configCmd = program\n .command('config')\n .description('View or update Workscribe configuration')\n .action(() => {\n const config = readConfig()\n log.plain('')\n log.plain(chalk.bold(' Workscribe Config'))\n log.plain('')\n printConfig(config, ' ')\n log.plain('')\n })\n\n configCmd\n .command('set <key> <value>')\n .description('Set a config value (e.g. workscribe config set ai.provider anthropic)')\n .action((key: string, value: string) => {\n const config = readConfig()\n const updated = setConfigValue(config, key, value)\n writeConfig(updated)\n const isSensitive = key === 'ai.apiKey' || key.toLowerCase().includes('key') || key.toLowerCase().includes('secret') || key.toLowerCase().includes('token')\n const displayValue = isSensitive ? '[set]' : chalk.yellow(value)\n log.success(`Set ${chalk.cyan(key)} = ${displayValue}`)\n })\n\n configCmd\n .command('get <key>')\n .description('Get a config value')\n .action((key: string) => {\n const config = readConfig()\n const value = getConfigValue(config, key)\n if (value === undefined) {\n log.warn(`Key not found: ${key}`)\n } else {\n log.plain(String(value))\n }\n })\n}\n\nconst SENSITIVE_KEYS = new Set(['apiKey', 'api_key', 'secret', 'token', 'password', 'passwd'])\n\nfunction maskValue(key: string, value: unknown): string {\n if (SENSITIVE_KEYS.has(key) && typeof value === 'string' && value.length > 0) {\n return chalk.dim(`${value.slice(0, 4)}${'*'.repeat(8)} (${value.length} chars)`)\n }\n if (Array.isArray(value)) return `[${(value as unknown[]).join(', ')}]`\n return String(value) || chalk.dim('(not set)')\n}\n\nfunction printConfig(obj: unknown, indent: string): void {\n if (typeof obj !== 'object' || obj === null) return\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n log.plain(`${indent}${chalk.bold(key)}:`)\n printConfig(value, indent + ' ')\n } else {\n log.plain(`${indent}${chalk.dim(key)}: ${chalk.yellow(maskValue(key, value))}`)\n }\n }\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\nimport { log } from '../utils/logger'\n\nexport function registerPause(program: Command): void {\n program\n .command('pause')\n .description('Pause capture temporarily')\n .action(() => {\n if (fs.existsSync(PAUSE_FLAG_PATH)) {\n log.info('Capture is already paused. Run workscribe resume to restart.')\n return\n }\n fs.writeFileSync(PAUSE_FLAG_PATH, '', { mode: 0o600 })\n log.success('Capture paused. Run workscribe resume to restart.')\n })\n\n program\n .command('resume')\n .description('Resume capture after a pause')\n .action(() => {\n if (!fs.existsSync(PAUSE_FLAG_PATH)) {\n log.info('Capture is not paused.')\n return\n }\n fs.rmSync(PAUSE_FLAG_PATH)\n log.success('Capture resumed.')\n })\n}\n"],"mappings":"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","names":["import_fs","path","os","fs","import_path","path","import_fs","fs","fs","import_fs","chalk","fs","program","import_fs","import_path","import_os","import_chalk","path","os","fs","program","chalk","prompts","import_fs","import_chalk","program","fs","chalk","import_chalk","chalk","program","import_chalk","program","chalk","import_chalk","program","chalk","import_fs","import_path","eventRowToPayload","program","path","fs","import_chalk","program","chalk","import_fs","program","fs"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/capture/index.ts","../src/db/index.ts","../src/utils/paths.ts","../src/capture/redact.ts","../src/capture/git.ts","../src/normalize/patterns.ts","../src/config/index.ts","../src/capture/filter.ts","../src/utils/logger.ts","../src/cli/capture.ts","../src/cli/init.ts","../src/cli/status.ts","../src/utils/format.ts","../src/cli/events.ts","../src/cli/sessions.ts","../src/sessions/grouper.ts","../src/sessions/detector.ts","../src/cli/summary.ts","../src/ai/prompt.ts","../src/ai/fetch.ts","../src/ai/providers/anthropic.ts","../src/ai/providers/openai.ts","../src/ai/providers/ollama.ts","../src/ai/router.ts","../src/integrations/webhook.ts","../src/cli/export.ts","../src/cli/config.ts","../src/cli/pause.ts","../src/cli/undo.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { registerCapture } from './cli/capture'\n\ndeclare const __WORKSCRIBE_VERSION__: string\nimport { registerInit } from './cli/init'\nimport { registerStatus } from './cli/status'\nimport { registerEvents } from './cli/events'\nimport { registerSessions } from './cli/sessions'\nimport { registerSummary } from './cli/summary'\nimport { registerExport } from './cli/export'\nimport { registerConfig } from './cli/config'\nimport { registerPause } from './cli/pause'\nimport { registerUndo } from './cli/undo'\n\nconst program = new Command()\n\nprogram\n .name('workscribe')\n .description('Automatic developer work journal')\n .version(__WORKSCRIBE_VERSION__)\n\nregisterCapture(program)\nregisterInit(program)\nregisterStatus(program)\nregisterEvents(program)\nregisterSessions(program)\nregisterSummary(program)\nregisterExport(program)\nregisterConfig(program)\nregisterPause(program)\nregisterUndo(program)\n\nprogram.parse()\n","import fs from 'fs'\nimport { insertEvent, getLastEvent } from '../db'\nimport { redact } from './redact'\nimport { getGitContext } from './git'\nimport { normalizeCommand } from '../normalize/patterns'\nimport { readConfig } from '../config'\nimport { shouldFilter, isDuplicate } from './filter'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\n\nexport function captureCommand(opts: {\n command: string\n cwd: string\n exitCode: number | null\n durationMs: number | null\n}): void {\n // Check pause flag before any I/O\n if (fs.existsSync(PAUSE_FLAG_PATH)) return\n\n const config = readConfig()\n const { command, cwd, exitCode, durationMs } = opts\n\n // 1. Filter noise commands\n if (shouldFilter(command, config.capture.ignore)) return\n\n // 2. Deduplicate: same command + same cwd within 5 seconds\n const last = getLastEvent()\n if (isDuplicate(command, cwd, last ?? undefined)) return\n\n // 3. Redact sensitive patterns\n const safeCommand = redact(command, config.redact.extra)\n\n // 4. Extract git context (with optional per-project name override)\n const nameOverride = config.projects?.[cwd] ?? null\n const { repoName, branch } = getGitContext(cwd, nameOverride)\n\n // 5. Normalize to typed event\n const { category, metadata } = normalizeCommand(safeCommand)\n\n // 6. Persist\n insertEvent({\n captured_at: new Date().toISOString(),\n command: safeCommand,\n cwd,\n exit_code: exitCode,\n duration_ms: durationMs,\n repo_name: repoName,\n branch,\n category,\n metadata: Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null,\n })\n}\n","import { DatabaseSync } from 'node:sqlite'\nimport fs from 'fs'\nimport { WORKSCRIBE_DIR, DB_PATH } from '../utils/paths'\nimport type { EventRow, SessionRow } from './types'\nexport type { EventRow, SessionRow } from './types'\n\nlet _db: DatabaseSync | null = null\n\nexport function getDb(): DatabaseSync {\n if (_db) return _db\n\n // Ensure ~/.workscribe/ exists\n if (!fs.existsSync(WORKSCRIBE_DIR)) {\n fs.mkdirSync(WORKSCRIBE_DIR, { recursive: true })\n }\n\n _db = new DatabaseSync(DB_PATH)\n // Restrict DB file to owner-only after creation\n try { fs.chmodSync(DB_PATH, 0o600) } catch { /* ignore if already correct */ }\n _db.exec('PRAGMA journal_mode = WAL')\n _db.exec('PRAGMA foreign_keys = ON')\n runMigrations(_db)\n return _db\n}\n\nfunction runMigrations(db: DatabaseSync): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n captured_at TEXT NOT NULL,\n command TEXT NOT NULL,\n cwd TEXT NOT NULL,\n exit_code INTEGER,\n duration_ms INTEGER,\n repo_name TEXT,\n branch TEXT,\n category TEXT NOT NULL DEFAULT 'other',\n metadata TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_events_captured_at ON events(captured_at);\n CREATE INDEX IF NOT EXISTS idx_events_repo_name ON events(repo_name);\n\n CREATE TABLE IF NOT EXISTS sessions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n date TEXT NOT NULL,\n started_at TEXT NOT NULL,\n ended_at TEXT NOT NULL,\n repo_name TEXT,\n event_count INTEGER NOT NULL DEFAULT 0,\n summary TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_sessions_date ON sessions(date);\n\n CREATE TABLE IF NOT EXISTS session_events (\n session_id INTEGER NOT NULL REFERENCES sessions(id),\n event_id INTEGER NOT NULL REFERENCES events(id),\n PRIMARY KEY (session_id, event_id)\n );\n `)\n}\n\n// ─── Typed query helpers ──────────────────────────────────────────────────────\n\nexport function insertEvent(params: {\n captured_at: string\n command: string\n cwd: string\n exit_code: number | null\n duration_ms: number | null\n repo_name: string | null\n branch: string | null\n category: string\n metadata: string | null\n}): void {\n const db = getDb()\n const stmt = db.prepare(`\n INSERT INTO events (captured_at, command, cwd, exit_code, duration_ms, repo_name, branch, category, metadata)\n VALUES ($captured_at, $command, $cwd, $exit_code, $duration_ms, $repo_name, $branch, $category, $metadata)\n `)\n stmt.run(params)\n}\n\nexport function getLastEvent(): EventRow | undefined {\n const db = getDb()\n const stmt = db.prepare('SELECT * FROM events ORDER BY id DESC LIMIT 1')\n return stmt.get() as EventRow | undefined\n}\n\nexport function getEventsForDate(date: string): EventRow[] {\n const db = getDb()\n const stmt = db.prepare(`\n SELECT * FROM events\n WHERE date(captured_at) = $date\n ORDER BY captured_at ASC\n `)\n return stmt.all({ date }) as unknown as EventRow[]\n}\n\nexport function getRecentEvents(limit = 20): EventRow[] {\n const db = getDb()\n const stmt = db.prepare('SELECT * FROM events ORDER BY captured_at DESC LIMIT $limit')\n return stmt.all({ limit }) as unknown as EventRow[]\n}\n\nexport function getEventCount(): number {\n const db = getDb()\n const stmt = db.prepare('SELECT COUNT(*) as count FROM events')\n const row = stmt.get() as { count: number }\n return row.count\n}\n\nexport function insertSession(params: {\n date: string\n started_at: string\n ended_at: string\n repo_name: string | null\n event_count: number\n summary: string | null\n}): number {\n const db = getDb()\n const stmt = db.prepare(`\n INSERT INTO sessions (date, started_at, ended_at, repo_name, event_count, summary)\n VALUES ($date, $started_at, $ended_at, $repo_name, $event_count, $summary)\n `)\n const result = stmt.run(params)\n return Number(result.lastInsertRowid)\n}\n\nexport function insertSessionEvent(sessionId: number, eventId: number): void {\n const db = getDb()\n const stmt = db.prepare(`\n INSERT OR IGNORE INTO session_events (session_id, event_id) VALUES ($session_id, $event_id)\n `)\n stmt.run({ session_id: sessionId, event_id: eventId })\n}\n\nexport function getSessionsForDate(date: string): SessionRow[] {\n const db = getDb()\n const stmt = db.prepare('SELECT * FROM sessions WHERE date = $date ORDER BY started_at ASC')\n return stmt.all({ date }) as unknown as SessionRow[]\n}\n\nexport function updateSessionSummary(sessionId: number, summary: string): void {\n const db = getDb()\n const stmt = db.prepare('UPDATE sessions SET summary = $summary WHERE id = $id')\n stmt.run({ summary, id: sessionId })\n}\n\nexport function getLastEventIds(n: number): number[] {\n const db = getDb()\n const rows = db.prepare('SELECT id FROM events ORDER BY id DESC LIMIT $n').all({ n }) as { id: number }[]\n return rows.map((r) => r.id)\n}\n\nexport function getEventsByIds(ids: number[]): EventRow[] {\n if (ids.length === 0) return []\n const db = getDb()\n const placeholders = ids.map(() => '?').join(', ')\n return db.prepare(`SELECT * FROM events WHERE id IN (${placeholders}) ORDER BY id DESC`).all(...ids) as unknown as EventRow[]\n}\n\nexport function deleteEventsByIds(ids: number[]): void {\n if (ids.length === 0) return\n const db = getDb()\n const placeholders = ids.map(() => '?').join(', ')\n\n const affectedDates = db\n .prepare(`SELECT DISTINCT date(captured_at) as date FROM events WHERE id IN (${placeholders})`)\n .all(...ids) as { date: string }[]\n\n db.prepare(`DELETE FROM events WHERE id IN (${placeholders})`).run(...ids)\n\n for (const { date } of affectedDates) {\n clearSessionsForDate(date)\n }\n}\n\nexport function clearSessionsForDate(date: string): void {\n const db = getDb()\n // Remove session_events first due to FK constraint\n db.prepare(`\n DELETE FROM session_events WHERE session_id IN (\n SELECT id FROM sessions WHERE date = $date\n )\n `).run({ date })\n db.prepare('DELETE FROM sessions WHERE date = $date').run({ date })\n}\n","import path from 'path'\nimport os from 'os'\n\nexport const WORKSCRIBE_DIR = path.join(os.homedir(), '.workscribe')\nexport const DB_PATH = path.join(WORKSCRIBE_DIR, 'workscribe.db')\nexport const CONFIG_PATH = path.join(WORKSCRIBE_DIR, 'config.json')\nexport const DEBUG_LOG_PATH = path.join(WORKSCRIBE_DIR, 'debug.log')\nexport const EXPORTS_DIR = path.join(WORKSCRIBE_DIR, 'exports')\nexport const PAUSE_FLAG_PATH = path.join(WORKSCRIBE_DIR, 'paused')\nexport const WEEKLY_CACHE_DIR = path.join(WORKSCRIBE_DIR, 'weekly-cache')\n","// Patterns that match known sensitive values\nconst BUILTIN_PATTERNS: RegExp[] = [\n // AWS access keys\n /AKIA[0-9A-Z]{16}/g,\n // AWS secret access keys (40 char base64-ish after common env vars)\n /(?<=AWS_SECRET_ACCESS_KEY[=\\s]+)\\S+/gi,\n // GitHub tokens\n /ghp_[a-zA-Z0-9]{36}/g,\n /github_pat_[a-zA-Z0-9_]{82}/g,\n // GitLab tokens\n /glpat-[a-zA-Z0-9\\-]{20}/g,\n // Generic JWTs (three base64url segments)\n /eyJ[a-zA-Z0-9_-]+\\.eyJ[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_-]+/g,\n // Slack tokens\n /xox[baprs]-[a-zA-Z0-9\\-]+/g,\n // Stripe keys\n /sk_(live|test)_[a-zA-Z0-9]{24,}/g,\n // Values after long-form secret flags (single-char flags like -t/-p/-k are too ambiguous)\n /(?<=--(password|passwd|secret|token|key|api[-_]?key|auth[-_]?token)\\s+)\\S+/gi,\n // export VAR=VALUE where VAR looks sensitive\n /(?<=export\\s+(?:[A-Z_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL)[A-Z_]*)=)\\S+/gi,\n]\n\nexport function redact(command: string, extraPatterns: string[] = []): string {\n let result = command\n\n const allPatterns = [...BUILTIN_PATTERNS]\n\n for (const pattern of extraPatterns) {\n try {\n allPatterns.push(new RegExp(pattern, 'g'))\n } catch {\n // Invalid regex — skip silently\n }\n }\n\n for (const pattern of allPatterns) {\n // Reset lastIndex for global regexes\n pattern.lastIndex = 0\n result = result.replace(pattern, '[REDACTED]')\n }\n\n return result\n}\n","import { execSync } from 'child_process'\nimport path from 'path'\nimport fs from 'fs'\n\nexport interface GitContext {\n repoName: string | null\n branch: string | null\n}\n\nfunction nameFromRemote(root: string): string | null {\n try {\n const url = execSync('git remote get-url origin', {\n cwd: root,\n timeout: 2000,\n stdio: 'pipe',\n }).toString().trim()\n const match = url.match(/\\/([^/]+?)(?:\\.git)?$/)\n return match ? match[1] : null\n } catch {\n return null\n }\n}\n\nfunction nameFromPackageJson(root: string): string | null {\n try {\n const raw = fs.readFileSync(path.join(root, 'package.json'), 'utf-8')\n const pkg = JSON.parse(raw) as { name?: string }\n if (!pkg.name) return null\n return pkg.name.replace(/^@[^/]+\\//, '')\n } catch {\n return null\n }\n}\n\nexport function getGitContext(cwd: string, nameOverride?: string | null): GitContext {\n try {\n const root = execSync('git rev-parse --show-toplevel', {\n cwd,\n timeout: 2000,\n stdio: 'pipe',\n })\n .toString()\n .trim()\n\n const repoName =\n nameOverride ??\n nameFromRemote(root) ??\n nameFromPackageJson(root) ??\n path.basename(root)\n\n let branch: string | null = null\n try {\n branch = execSync('git branch --show-current', {\n cwd,\n timeout: 2000,\n stdio: 'pipe',\n })\n .toString()\n .trim() || null\n } catch {\n // Detached HEAD or not a git repo\n }\n\n return { repoName, branch }\n } catch {\n return { repoName: null, branch: null }\n }\n}\n","export interface NormalizedEvent {\n category: string\n metadata: Record<string, unknown>\n}\n\ninterface Rule {\n // Returns metadata if command matches, null otherwise\n match(cmd: string): NormalizedEvent | null\n}\n\nfunction makeRule(\n pattern: RegExp,\n category: string,\n extract: (match: RegExpMatchArray, cmd: string) => Record<string, unknown> = () => ({})\n): Rule {\n return {\n match(cmd) {\n const m = cmd.match(pattern)\n if (!m) return null\n return { category, metadata: extract(m, cmd) }\n },\n }\n}\n\n// Extract package names from install commands like: npm install foo bar\nfunction extractPackages(cmd: string, afterKeyword: string): string[] {\n const idx = cmd.indexOf(afterKeyword)\n if (idx === -1) return []\n return cmd\n .slice(idx + afterKeyword.length)\n .trim()\n .split(/\\s+/)\n .filter((p) => p && !p.startsWith('-'))\n}\n\nexport const RULES: Rule[] = [\n // ─── Git operations ───────────────────────────────────────────────────────\n makeRule(/^git\\s+commit\\b/, 'code_commit', (_m, cmd) => {\n const msgMatch = cmd.match(/-m\\s+[\"']?([^\"']+)[\"']?/)\n return { message: msgMatch ? msgMatch[1].trim() : null }\n }),\n\n makeRule(/^git\\s+push\\b/, 'code_push', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { remote: parts[2] ?? 'origin', branch: parts[3] ?? null }\n }),\n\n makeRule(/^git\\s+(pull|fetch)\\b/, 'code_pull', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], remote: parts[2] ?? 'origin' }\n }),\n\n makeRule(/^git\\s+(checkout|switch)\\b/, 'branch_switch', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n const branch = parts.find((p) => !p.startsWith('-') && p !== 'git' && p !== parts[1])\n return { branch: branch ?? null }\n }),\n\n makeRule(/^git\\s+(merge|rebase)\\b/, 'code_merge', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], target: parts[2] ?? null }\n }),\n\n makeRule(/^git\\s+stash\\b/, 'code_stash', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[2] ?? 'push' }\n }),\n\n makeRule(/^git\\s+/, 'git_other', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1] ?? null }\n }),\n\n // ─── Package managers — install ───────────────────────────────────────────\n makeRule(/^npm\\s+(install|i|add)\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'npm',\n packages: extractPackages(cmd, cmd.includes('install') ? 'install' : cmd.includes(' i ') ? ' i ' : 'add'),\n })),\n\n makeRule(/^(yarn|pnpm)\\s+(install|add)\\b/, 'dependency_install', (m, cmd) => ({\n tool: m[1],\n packages: extractPackages(cmd, 'add'),\n })),\n\n makeRule(/^pip\\s+(install)\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'pip',\n packages: extractPackages(cmd, 'install'),\n })),\n\n makeRule(/^cargo\\s+add\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'cargo',\n packages: extractPackages(cmd, 'add'),\n })),\n\n makeRule(/^(brew)\\s+install\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'brew',\n packages: extractPackages(cmd, 'install'),\n })),\n\n makeRule(/^go\\s+get\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'go',\n packages: extractPackages(cmd, 'get'),\n })),\n\n // ─── Package managers — remove ────────────────────────────────────────────\n makeRule(/^npm\\s+(uninstall|remove|rm)\\b/, 'dependency_remove', (_m, cmd) => ({\n tool: 'npm',\n packages: extractPackages(cmd, cmd.split(/\\s+/)[1]),\n })),\n\n makeRule(/^(yarn|pnpm)\\s+remove\\b/, 'dependency_remove', (m, cmd) => ({\n tool: m[1],\n packages: extractPackages(cmd, 'remove'),\n })),\n\n makeRule(/^pip\\s+uninstall\\b/, 'dependency_remove', (_m, cmd) => ({\n tool: 'pip',\n packages: extractPackages(cmd, 'uninstall'),\n })),\n\n // ─── Tests ────────────────────────────────────────────────────────────────\n makeRule(/^(npm\\s+(test|run\\s+test)|yarn\\s+test|pnpm\\s+test)\\b/, 'test_run', () => ({ tool: 'npm' })),\n makeRule(/^(jest|vitest)\\b/, 'test_run', (m) => ({ tool: m[1] })),\n makeRule(/^pytest\\b/, 'test_run', () => ({ tool: 'pytest' })),\n makeRule(/^go\\s+test\\b/, 'test_run', () => ({ tool: 'go' })),\n makeRule(/^cargo\\s+test\\b/, 'test_run', () => ({ tool: 'cargo' })),\n makeRule(/^rspec\\b/, 'test_run', () => ({ tool: 'rspec' })),\n makeRule(/^(php\\s+)?phpunit\\b/, 'test_run', () => ({ tool: 'phpunit' })),\n\n // ─── Build ────────────────────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+build\\b/, 'build', () => ({ tool: 'npm' })),\n makeRule(/^(yarn|pnpm)\\s+build\\b/, 'build', (m) => ({ tool: m[1] })),\n makeRule(/^(make|gmake)\\b/, 'build', (m) => ({ tool: m[1] })),\n makeRule(/^cargo\\s+build\\b/, 'build', () => ({ tool: 'cargo' })),\n makeRule(/^go\\s+build\\b/, 'build', () => ({ tool: 'go' })),\n makeRule(/^docker\\s+build\\b/, 'build', () => ({ tool: 'docker' })),\n makeRule(/^(mvn|gradle)\\b/, 'build', (m) => ({ tool: m[1] })),\n\n // ─── Dev server ───────────────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+(dev|start|serve)\\b/, 'server_run', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n makeRule(/^(yarn|pnpm)\\s+(dev|start)\\b/, 'server_run', (m) => ({ tool: m[1], script: m[2] })),\n makeRule(/^python\\s+.*manage\\.py\\s+runserver\\b/, 'server_run', () => ({ tool: 'django' })),\n\n // ─── Infrastructure ───────────────────────────────────────────────────────\n makeRule(/^ssh\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'ssh', target: parts[parts.length - 1] }\n }),\n\n makeRule(/^kubectl\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'kubectl', subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^(helm|terraform|pulumi)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^docker\\s+(run|compose|ps|stop|start|rm|exec)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'docker', subcommand: parts[1] }\n }),\n\n makeRule(/^(aws|gcloud|az)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n // ─── Scripts ──────────────────────────────────────────────────────────────\n makeRule(/^(bash|sh|zsh|fish)\\s+\\S+/, 'script_run', (m) => ({ shell: m[1] })),\n makeRule(/^node\\s+\\S+\\.(?:js|mjs|cjs)\\b/, 'script_run', () => ({ runtime: 'node' })),\n makeRule(/^python3?\\s+\\S+\\.py\\b/, 'script_run', () => ({ runtime: 'python' })),\n\n // ─── Environment ──────────────────────────────────────────────────────────\n makeRule(/^export\\s+\\w+=/, 'environment', (_m, cmd) => {\n const varName = cmd.match(/^export\\s+(\\w+)=/)?.[1] ?? null\n return { variable: varName }\n }),\n makeRule(/^source\\s+/, 'environment', () => ({})),\n\n // ─── npm scripts (generic) ────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+\\S+/, 'script_run', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n]\n\nexport function normalizeCommand(command: string): NormalizedEvent {\n const trimmed = command.trim().replace(/\\s+/g, ' ')\n\n for (const rule of RULES) {\n const result = rule.match(trimmed)\n if (result) return result\n }\n\n return { category: 'other', metadata: {} }\n}\n","import fs from 'fs'\nimport { CONFIG_PATH } from '../utils/paths'\n\nexport interface WorkscribeConfig {\n ai: {\n provider: string // 'anthropic' | 'openai' | 'ollama' | 'openai-compatible'\n apiKey: string\n model: string\n baseUrl: string\n }\n session: {\n idleTimeout: number // minutes\n }\n capture: {\n ignore: string[]\n }\n redact: {\n extra: string[]\n }\n summary: {\n format: 'markdown' | 'json'\n }\n projects?: Record<string, string> // absolute path → display name override\n integrations?: {\n slackWebhook?: string\n webhook?: string\n }\n}\n\nconst DEFAULTS: WorkscribeConfig = {\n ai: {\n provider: '',\n apiKey: '',\n model: '',\n baseUrl: '',\n },\n session: {\n idleTimeout: 30,\n },\n capture: {\n ignore: ['cd', 'ls', 'll', 'la', 'ls -la', 'ls -l', 'ls -a', 'pwd', 'clear', 'exit', 'history', 'man'],\n },\n redact: {\n extra: [],\n },\n summary: {\n format: 'markdown',\n },\n}\n\nexport function readConfig(): WorkscribeConfig {\n if (!fs.existsSync(CONFIG_PATH)) return { ...DEFAULTS }\n try {\n const raw = fs.readFileSync(CONFIG_PATH, 'utf-8')\n const parsed = JSON.parse(raw)\n return deepMerge(DEFAULTS, parsed)\n } catch {\n return { ...DEFAULTS }\n }\n}\n\nexport function writeConfig(config: WorkscribeConfig): void {\n fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 })\n // Enforce permissions on existing files (mode only applies on creation above)\n try { fs.chmodSync(CONFIG_PATH, 0o600) } catch { /* ignore */ }\n}\n\nexport function getConfigValue(config: WorkscribeConfig, key: string): unknown {\n const parts = key.split('.')\n let cur: unknown = config\n for (const part of parts) {\n if (cur == null || typeof cur !== 'object') return undefined\n cur = (cur as Record<string, unknown>)[part]\n }\n return cur\n}\n\nexport function setConfigValue(config: WorkscribeConfig, key: string, value: string): WorkscribeConfig {\n const parts = key.split('.')\n const result = JSON.parse(JSON.stringify(config)) as WorkscribeConfig\n let cur = result as unknown as Record<string, unknown>\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]\n if (cur[part] == null || typeof cur[part] !== 'object') {\n cur[part] = {}\n }\n cur = cur[part] as Record<string, unknown>\n }\n const lastKey = parts[parts.length - 1]\n // Coerce value type\n if (value === 'true') cur[lastKey] = true\n else if (value === 'false') cur[lastKey] = false\n else if (!isNaN(Number(value))) cur[lastKey] = Number(value)\n else cur[lastKey] = value\n return result\n}\n\nfunction deepMerge<T extends object>(defaults: T, overrides: Partial<T>): T {\n const result = { ...defaults }\n for (const key of Object.keys(overrides) as (keyof T)[]) {\n const val = overrides[key]\n if (val !== null && typeof val === 'object' && !Array.isArray(val) && typeof defaults[key] === 'object') {\n result[key] = deepMerge(defaults[key] as object, val as object) as T[keyof T]\n } else if (val !== undefined) {\n result[key] = val as T[keyof T]\n }\n }\n return result\n}\n","import type { EventRow } from '../db/types'\n\nfunction normalizeForFilter(cmd: string): string {\n return cmd.trim().replace(/\\s+/g, ' ')\n}\n\n/**\n * Returns true if the command should be dropped (noise / ignore list).\n */\nexport function shouldFilter(command: string, ignoreList: string[]): boolean {\n const normalized = normalizeForFilter(command)\n if (!normalized) return true\n\n const normalizedIgnore = ignoreList.map(normalizeForFilter)\n if (normalizedIgnore.includes(normalized)) return true\n\n // Also drop if just the base command (no args) is in the ignore list\n const baseCmd = normalized.split(' ')[0]\n if (normalizedIgnore.includes(baseCmd)) return true\n\n return false\n}\n\n/**\n * Returns true if this command is a duplicate of the last event\n * (same command + same cwd within the dedup window).\n */\nexport function isDuplicate(\n command: string,\n cwd: string,\n lastEvent: Pick<EventRow, 'command' | 'cwd' | 'captured_at'> | undefined,\n dedupWindowMs = 5000,\n now = Date.now()\n): boolean {\n if (!lastEvent) return false\n const lastTime = new Date(lastEvent.captured_at).getTime()\n return (\n lastEvent.command === normalizeForFilter(command) &&\n lastEvent.cwd === cwd &&\n now - lastTime < dedupWindowMs\n )\n}\n","import chalk from 'chalk'\nimport fs from 'fs'\nimport { DEBUG_LOG_PATH } from './paths'\n\nconst isDebug = process.env.WORKSCRIBE_DEBUG === '1'\n\nexport const log = {\n info: (msg: string) => console.log(chalk.cyan('→'), msg),\n success: (msg: string) => console.log(chalk.green('✓'), msg),\n warn: (msg: string) => console.log(chalk.yellow('⚠'), msg),\n error: (msg: string) => console.error(chalk.red('✗'), msg),\n plain: (msg: string) => console.log(msg),\n}\n\nexport function debugLog(context: string, error: unknown): void {\n if (!isDebug) return\n const entry = `[${new Date().toISOString()}] [${context}] ${error instanceof Error ? error.stack : String(error)}\\n`\n try {\n fs.appendFileSync(DEBUG_LOG_PATH, entry, { mode: 0o600 })\n } catch {\n // If we can't write to the debug log, silently ignore\n }\n}\n","import { Command } from 'commander'\nimport { captureCommand } from '../capture'\nimport { debugLog } from '../utils/logger'\n\nexport function registerCapture(program: Command): void {\n // Internal command — called by the shell hook, not by the user\n program\n .command('_capture')\n .description('Internal: capture a shell command event (called by shell hook)')\n .option('--cmd <command>', 'command that was executed')\n .option('--cwd <path>', 'working directory')\n .option('--exit <code>', 'exit code', parseInt)\n .option('--duration <ms>', 'duration in milliseconds', parseInt)\n .action((opts) => {\n // Never surface errors to the terminal — this runs silently in the background\n try {\n if (!opts.cmd || !opts.cwd) return\n captureCommand({\n command: opts.cmd,\n cwd: opts.cwd,\n exitCode: opts.exit ?? null,\n durationMs: opts.duration ?? null,\n })\n } catch (err) {\n debugLog('_capture', err)\n }\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport prompts from 'prompts'\nimport chalk from 'chalk'\nimport { WORKSCRIBE_DIR, CONFIG_PATH } from '../utils/paths'\nimport { readConfig, writeConfig } from '../config'\nimport { getDb } from '../db'\nimport { log } from '../utils/logger'\n\nconst HOOK_START = '# workscribe:hook:start'\nconst HOOK_END = '# workscribe:hook:end'\n\nfunction detectShell(): { name: string; rcFile: string } | null {\n const shell = process.env.SHELL ?? ''\n if (shell.includes('zsh')) return { name: 'zsh', rcFile: path.join(os.homedir(), '.zshrc') }\n if (shell.includes('bash')) return { name: 'bash', rcFile: path.join(os.homedir(), '.bashrc') }\n return null\n}\n\nfunction isHookInstalled(rcFile: string): boolean {\n if (!fs.existsSync(rcFile)) return false\n return fs.readFileSync(rcFile, 'utf-8').includes(HOOK_START)\n}\n\nfunction installHook(rcFile: string, hookContent: string): void {\n // Back up the rc file\n const backup = `${rcFile}.workscribe-backup-${Date.now()}`\n fs.copyFileSync(rcFile, backup)\n log.info(`Backed up ${rcFile} → ${path.basename(backup)}`)\n\n // Append hook\n fs.appendFileSync(rcFile, `\\n${hookContent}\\n`)\n}\n\nexport function registerInit(program: Command): void {\n program\n .command('init')\n .description('Install shell hooks and set up Workscribe')\n .action(async () => {\n log.plain(chalk.bold('\\n Workscribe — Developer Work Journal\\n'))\n\n // 1. Create ~/.workscribe/ with owner-only permissions\n if (!fs.existsSync(WORKSCRIBE_DIR)) {\n fs.mkdirSync(WORKSCRIBE_DIR, { recursive: true, mode: 0o700 })\n log.success(`Created ${WORKSCRIBE_DIR}`)\n } else {\n // Secure the directory for existing installs\n try { fs.chmodSync(WORKSCRIBE_DIR, 0o700) } catch { /* ignore */ }\n }\n\n // 2. Initialize database (runs migrations)\n getDb()\n log.success('Database initialized')\n\n // 3. Detect shell\n const shell = detectShell()\n if (!shell) {\n log.warn('Could not detect shell. Supported: zsh, bash')\n log.plain(`Manually add the hook from: ${chalk.cyan('shell/hook.zsh')} or ${chalk.cyan('shell/hook.bash')}`)\n } else {\n if (isHookInstalled(shell.rcFile)) {\n log.info(`Shell hook already installed in ${shell.rcFile}`)\n } else {\n // Load hook template from package\n const hookFile = path.join(__dirname, '..', 'shell', `hook.${shell.name}`)\n if (!fs.existsSync(hookFile)) {\n log.warn(`Hook template not found at ${hookFile}`)\n } else {\n const hookContent = fs.readFileSync(hookFile, 'utf-8')\n if (!fs.existsSync(shell.rcFile)) fs.writeFileSync(shell.rcFile, '')\n installHook(shell.rcFile, hookContent)\n log.success(`Shell hook installed in ${shell.rcFile}`)\n }\n }\n }\n\n // 4. AI provider setup\n const config = readConfig()\n\n if (config.ai.provider) {\n log.info(`AI provider already configured: ${chalk.cyan(config.ai.provider)}`)\n } else {\n log.plain('')\n const { provider } = await prompts({\n type: 'select',\n name: 'provider',\n message: 'Which AI provider do you want to use?',\n choices: [\n { title: 'Anthropic (Claude) — requires API key', value: 'anthropic' },\n { title: 'OpenAI — requires API key', value: 'openai' },\n { title: 'Ollama (local) — free, runs on your machine', value: 'ollama' },\n { title: 'Other (OpenAI-compatible) — Groq, Together, Mistral…', value: 'openai-compatible' },\n { title: 'Skip — configure later', value: 'skip' },\n ],\n })\n\n if (provider && provider !== 'skip') {\n config.ai.provider = provider\n\n if (provider === 'ollama') {\n log.plain(`\\n ${chalk.dim('Make sure Ollama is running: ollama serve')}`)\n log.plain(` ${chalk.dim('Default model: llama3 (change with: workscribe config set ai.model <model>)')}\\n`)\n } else {\n const { apiKey } = await prompts({\n type: 'password',\n name: 'apiKey',\n message: `Enter your ${provider === 'openai-compatible' ? '' : provider + ' '}API key:`,\n })\n if (apiKey) config.ai.apiKey = apiKey\n\n if (provider === 'openai-compatible') {\n const { baseUrl } = await prompts({\n type: 'text',\n name: 'baseUrl',\n message: 'Enter the base URL (e.g. https://api.groq.com/openai/v1):',\n })\n if (baseUrl) config.ai.baseUrl = baseUrl\n }\n }\n\n writeConfig(config)\n log.success(`AI provider set to ${chalk.cyan(provider)}`)\n } else if (provider === 'skip' || !provider) {\n log.info('Skipped — run later: workscribe config set ai.provider <provider>')\n }\n }\n\n // 5. Write config if not exists\n if (!fs.existsSync(CONFIG_PATH)) {\n writeConfig(config)\n log.success('Config file created')\n }\n\n log.plain('')\n log.plain(chalk.bold(' Setup complete!'))\n if (shell) {\n log.plain('')\n log.plain(` Activate capture now: ${chalk.cyan(`source ${shell.rcFile}`)}`)\n log.plain(` Or restart your terminal.`)\n }\n log.plain('')\n log.plain(` ${chalk.dim('workscribe summary')} — generate today\\'s work summary`)\n log.plain(` ${chalk.dim('workscribe status')} — check capture is working`)\n log.plain('')\n })\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport chalk from 'chalk'\nimport { getEventCount, getLastEvent } from '../db'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { formatDate, todayDate } from '../utils/format'\nimport { getEventsForDate } from '../db'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\n\nexport function registerStatus(program: Command): void {\n program\n .command('status')\n .description('Show capture status and event counts')\n .action(() => {\n const config = readConfig()\n const total = getEventCount()\n const todayEvents = getEventsForDate(todayDate())\n const last = getLastEvent()\n const isPaused = fs.existsSync(PAUSE_FLAG_PATH)\n\n log.plain('')\n log.plain(chalk.bold(' Workscribe Status'))\n log.plain('')\n\n if (isPaused) {\n log.plain(` Capture ${chalk.yellow('paused')} ${chalk.dim('(run: workscribe resume)')}`)\n } else {\n log.plain(` Capture ${chalk.green('active')}`)\n }\n\n log.plain(` Events today ${chalk.cyan(String(todayEvents.length))}`)\n log.plain(` Events total ${chalk.cyan(String(total))}`)\n\n if (last) {\n log.plain(` Last event ${formatDate(last.captured_at)} ${chalk.dim(last.command.slice(0, 60))}`)\n }\n\n log.plain('')\n log.plain(` AI provider ${config.ai.provider ? chalk.cyan(config.ai.provider) : chalk.yellow('not configured')}`)\n if (config.ai.model) log.plain(` Model ${chalk.dim(config.ai.model)}`)\n log.plain('')\n })\n}\n","export function formatDuration(minutes: number): string {\n if (minutes < 60) return `${Math.round(minutes)}m`\n const h = Math.floor(minutes / 60)\n const m = Math.round(minutes % 60)\n return m > 0 ? `${h}h ${m}m` : `${h}h`\n}\n\nexport function formatDate(isoString: string): string {\n return new Date(isoString).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\nexport function todayDate(): string {\n return new Date().toISOString().split('T')[0]\n}\n\nexport function parseDate(input: string): string {\n // Accepts YYYY-MM-DD, returns YYYY-MM-DD. Throws on invalid.\n const d = new Date(input + 'T00:00:00')\n if (isNaN(d.getTime())) throw new Error(`Invalid date: ${input}`)\n return input\n}\n\nfunction localDateStr(d: Date): string {\n const y = d.getFullYear()\n const m = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n return `${y}-${m}-${day}`\n}\n\n// Returns Monday of the ISO week containing dateStr (YYYY-MM-DD)\nexport function startOfWeek(dateStr: string): string {\n const d = new Date(dateStr + 'T00:00:00')\n const day = d.getDay() // 0=Sun\n const diff = day === 0 ? -6 : 1 - day\n d.setDate(d.getDate() + diff)\n return localDateStr(d)\n}\n\n// Returns all YYYY-MM-DD strings from start to end inclusive\nexport function eachDayOfRange(start: string, end: string): string[] {\n const days: string[] = []\n const cur = new Date(start + 'T00:00:00')\n const last = new Date(end + 'T00:00:00')\n while (cur <= last) {\n days.push(localDateStr(cur))\n cur.setDate(cur.getDate() + 1)\n }\n return days\n}\n\n// Returns ISO week string YYYY-WW for a date\nexport function isoWeekKey(dateStr: string): string {\n const d = new Date(dateStr + 'T00:00:00')\n const jan4 = new Date(d.getFullYear(), 0, 4)\n const weekNum = Math.ceil(((d.getTime() - jan4.getTime()) / 86400000 + jan4.getDay() + 1) / 7)\n return `${d.getFullYear()}-W${String(weekNum).padStart(2, '0')}`\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getEventsForDate } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\n\nconst CATEGORY_COLORS: Record<string, (s: string) => string> = {\n code_commit: chalk.green,\n code_push: chalk.green,\n code_pull: chalk.blue,\n branch_switch: chalk.blue,\n dependency_install: chalk.yellow,\n dependency_remove: chalk.yellow,\n test_run: chalk.magenta,\n build: chalk.cyan,\n server_run: chalk.cyan,\n infrastructure: chalk.red,\n script_run: chalk.white,\n environment: chalk.gray,\n git_other: chalk.blue,\n code_merge: chalk.green,\n code_stash: chalk.blue,\n other: chalk.gray,\n}\n\nexport function registerEvents(program: Command): void {\n program\n .command('events')\n .description('List captured events')\n .option('--date <YYYY-MM-DD>', 'date to list events for (default: today)')\n .option('--limit <n>', 'max events to show', parseInt)\n .action((opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const events = getEventsForDate(date)\n const limit = opts.limit ?? events.length\n\n if (events.length === 0) {\n log.plain(`No events captured for ${date}`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Events for ${date} (${events.length} total)`))\n log.plain('')\n\n for (const event of events.slice(0, limit)) {\n const colorFn = CATEGORY_COLORS[event.category] ?? chalk.gray\n const time = formatDate(event.captured_at)\n const category = colorFn(event.category.padEnd(20))\n const cmd = chalk.dim(event.command.slice(0, 55))\n const repo = event.repo_name ? chalk.dim(` [${event.repo_name}]`) : ''\n const exitBadge = event.exit_code && event.exit_code !== 0 ? chalk.red(` ✗${event.exit_code}`) : ''\n log.plain(` ${chalk.dim(time)} ${category} ${cmd}${repo}${exitBadge}`)\n }\n\n if (limit < events.length) {\n log.plain('')\n log.plain(chalk.dim(` … and ${events.length - limit} more (remove --limit to see all)`))\n }\n\n log.plain('')\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate, formatDuration } from '../utils/format'\n\nexport function registerSessions(program: Command): void {\n program\n .command('sessions')\n .description('List detected work sessions')\n .option('--date <YYYY-MM-DD>', 'date to show sessions for (default: today)')\n .action((opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const sessions = getOrBuildSessions(date)\n\n if (sessions.length === 0) {\n log.plain(`No sessions detected for ${date}`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Sessions for ${date} (${sessions.length} session${sessions.length > 1 ? 's' : ''})`))\n log.plain('')\n\n for (const session of sessions) {\n const start = formatDate(session.startedAt)\n const end = formatDate(session.endedAt)\n const duration = formatDuration(session.durationMinutes)\n const repo = session.repoName ? chalk.cyan(session.repoName) : chalk.gray('no repo')\n log.plain(` ${chalk.dim(start)} – ${chalk.dim(end)} ${repo} ${chalk.dim(duration)} ${chalk.dim(session.eventCount + ' events')}`)\n }\n\n log.plain('')\n })\n}\n","import type { EventRow } from '../db/types'\n\n/**\n * Split a sorted list of events into groups separated by idle gaps.\n */\nexport function splitByIdleGap(events: EventRow[], idleMs: number): EventRow[][] {\n if (events.length === 0) return []\n const groups: EventRow[][] = [[events[0]]]\n\n for (let i = 1; i < events.length; i++) {\n const prev = new Date(events[i - 1].captured_at).getTime()\n const curr = new Date(events[i].captured_at).getTime()\n if (curr - prev >= idleMs) {\n groups.push([events[i]])\n } else {\n groups[groups.length - 1].push(events[i])\n }\n }\n\n return groups\n}\n\n/**\n * Split a list of events into groups by consecutive repo_name.\n * A repo change mid-stream creates a new group.\n */\nexport function splitByRepo(events: EventRow[]): EventRow[][] {\n if (events.length === 0) return []\n const groups: EventRow[][] = [[events[0]]]\n\n for (let i = 1; i < events.length; i++) {\n const prevRepo = events[i - 1].repo_name\n const currRepo = events[i].repo_name\n if (currRepo !== prevRepo) {\n groups.push([events[i]])\n } else {\n groups[groups.length - 1].push(events[i])\n }\n }\n\n return groups\n}\n\nexport function minutesBetween(start: string, end: string): number {\n return (new Date(end).getTime() - new Date(start).getTime()) / 60000\n}\n","import type { EventRow } from '../db/types'\nimport {\n insertSession,\n insertSessionEvent,\n clearSessionsForDate,\n getSessionsForDate,\n getEventsForDate,\n} from '../db'\nimport { readConfig } from '../config'\nimport { splitByIdleGap, splitByRepo, minutesBetween } from './grouper'\n\nexport interface Session {\n id: number\n date: string\n startedAt: string\n endedAt: string\n repoName: string | null\n durationMinutes: number\n eventCount: number\n summary: string | null\n events: EventRow[]\n}\n\nexport function getOrBuildSessions(date: string): Session[] {\n // Check if sessions already exist for this date\n const existing = getSessionsForDate(date)\n const events = getEventsForDate(date)\n\n if (events.length === 0) return []\n\n // If sessions exist and event count matches, use cached\n const cachedEventCount = existing.reduce((sum, s) => sum + s.event_count, 0)\n if (existing.length > 0 && cachedEventCount === events.length) {\n return existing.map((s) => ({\n id: s.id,\n date: s.date,\n startedAt: s.started_at,\n endedAt: s.ended_at,\n repoName: s.repo_name,\n durationMinutes: minutesBetween(s.started_at, s.ended_at),\n eventCount: s.event_count,\n summary: s.summary,\n events: [], // not needed when using cached\n }))\n }\n\n // Rebuild sessions\n clearSessionsForDate(date)\n return buildSessions(date, events)\n}\n\nfunction buildSessions(date: string, events: EventRow[]): Session[] {\n const config = readConfig()\n const idleMs = config.session.idleTimeout * 60 * 1000\n\n // Group events by repo, then split by idle gap within each repo group\n const sessions: Session[] = []\n\n // Split by idle gaps first (regardless of repo)\n const timeGroups = splitByIdleGap(events, idleMs)\n\n for (const group of timeGroups) {\n if (group.length === 0) continue\n\n // Within each time group, further split by repo change\n const repoGroups = splitByRepo(group)\n\n for (const repoGroup of repoGroups) {\n if (repoGroup.length === 0) continue\n\n const first = repoGroup[0]\n const last = repoGroup[repoGroup.length - 1]\n const repoName = last.repo_name ?? first.repo_name ?? null\n\n const sessionId = insertSession({\n date,\n started_at: first.captured_at,\n ended_at: last.captured_at,\n repo_name: repoName,\n event_count: repoGroup.length,\n summary: null,\n })\n\n for (const event of repoGroup) {\n insertSessionEvent(sessionId, event.id)\n }\n\n sessions.push({\n id: sessionId,\n date,\n startedAt: first.captured_at,\n endedAt: last.captured_at,\n repoName,\n durationMinutes: minutesBetween(first.captured_at, last.captured_at),\n eventCount: repoGroup.length,\n summary: null,\n events: repoGroup,\n })\n }\n }\n\n return sessions\n}\n\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport fs from 'fs'\nimport path from 'path'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { getProvider } from '../ai/router'\nimport { readConfig } from '../config'\nimport { updateSessionSummary } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate, startOfWeek, eachDayOfRange, isoWeekKey } from '../utils/format'\nimport { postToWebhook } from '../integrations/webhook'\nimport { WEEKLY_CACHE_DIR } from '../utils/paths'\nimport type { DaySummaryPayload, SessionPayload, EventPayload, WeekSummaryPayload } from '../ai/types'\nimport type { EventRow } from '../db'\n\nfunction eventRowToPayload(row: EventRow): EventPayload {\n let metadata: Record<string, unknown> = {}\n try {\n if (row.metadata) metadata = JSON.parse(row.metadata)\n } catch { /* ignore */ }\n return {\n category: row.category,\n metadata,\n exitCode: row.exit_code,\n durationMs: row.duration_ms,\n }\n}\n\nfunction sessionToPayload(s: ReturnType<typeof getOrBuildSessions>[number], date: string): SessionPayload {\n return {\n date,\n project: s.repoName,\n branch: s.events[0]?.branch ?? null,\n startTime: formatDate(s.startedAt),\n endTime: formatDate(s.endedAt),\n durationMinutes: s.durationMinutes,\n events: s.events.map(eventRowToPayload),\n }\n}\n\nasync function handleWebhook(\n opts: { slack?: boolean; webhook?: string },\n config: ReturnType<typeof readConfig>,\n summary: string,\n): Promise<void> {\n const webhookUrl =\n opts.webhook ??\n (opts.slack ? config.integrations?.slackWebhook : undefined)\n\n if (webhookUrl) {\n try {\n log.info('Posting to webhook…')\n await postToWebhook(webhookUrl, summary)\n log.success('Posted.')\n } catch (e) {\n log.error(`Webhook failed: ${(e as Error).message}`)\n }\n } else if (opts.slack && !config.integrations?.slackWebhook) {\n log.warn('--slack requires integrations.slackWebhook to be set in config')\n }\n}\n\nexport function registerSummary(program: Command): void {\n program\n .command('summary')\n .description('Generate an AI work summary')\n .option('--date <YYYY-MM-DD>', 'date to summarize (default: today)')\n .option('--week', 'summarize the current week')\n .option('--fresh', 'bypass cache and regenerate')\n .option('--slack', 'post summary to Slack webhook from config')\n .option('--webhook <url>', 'post summary to a custom webhook URL')\n .action(async (opts) => {\n const config = readConfig()\n let provider\n try {\n provider = getProvider(config)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n\n // ── Weekly branch ────────────────────────────────────────────────────────\n if (opts.week) {\n const today = todayDate()\n const weekStart = startOfWeek(today)\n const days = eachDayOfRange(weekStart, today)\n\n const weeklyDays: WeekSummaryPayload['days'] = []\n for (const day of days) {\n const sessions = getOrBuildSessions(day)\n if (sessions.length > 0) {\n weeklyDays.push({ date: day, sessions: sessions.map((s) => sessionToPayload(s, day)) })\n }\n }\n\n if (weeklyDays.length === 0) {\n log.plain('No activity captured this week.')\n return\n }\n\n const cacheKey = isoWeekKey(today)\n const cacheFile = path.join(WEEKLY_CACHE_DIR, `${cacheKey}.md`)\n\n if (!opts.fresh && fs.existsSync(cacheFile)) {\n const cached = fs.readFileSync(cacheFile, 'utf-8')\n log.plain('')\n log.plain(cached)\n log.plain('')\n log.plain(chalk.dim(' (cached — use --fresh to regenerate)'))\n await handleWebhook(opts, config, cached)\n return\n }\n\n const payload: WeekSummaryPayload = { weekStart, weekEnd: today, days: weeklyDays }\n\n log.plain('')\n log.info(`Generating weekly summary via ${chalk.cyan(config.ai.provider)}…`)\n\n try {\n const summary = provider.summarizeWeek\n ? await provider.summarizeWeek(payload)\n : await provider.summarize({ date: today, sessions: weeklyDays.flatMap((d) => d.sessions) })\n\n log.plain('')\n log.plain(summary)\n log.plain('')\n\n fs.mkdirSync(WEEKLY_CACHE_DIR, { recursive: true })\n fs.writeFileSync(cacheFile, summary, { encoding: 'utf-8', mode: 0o600 })\n\n await handleWebhook(opts, config, summary)\n } catch (e) {\n log.error(`Weekly summary failed [${config.ai.provider}]: ${(e as Error).message}`)\n process.exit(1)\n }\n return\n }\n\n // ── Daily branch ─────────────────────────────────────────────────────────\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const sessions = getOrBuildSessions(date)\n\n if (sessions.length === 0) {\n log.plain(`No activity captured for ${date}`)\n return\n }\n\n if (!opts.fresh) {\n const allCached = sessions.every((s) => s.summary)\n if (allCached) {\n log.plain('')\n for (const s of sessions) {\n log.plain(s.summary!)\n }\n log.plain('')\n log.plain(chalk.dim(' (cached — use --fresh to regenerate)'))\n return\n }\n }\n\n const sessionPayloads = sessions.map((s) => sessionToPayload(s, date))\n const payload: DaySummaryPayload = { date, sessions: sessionPayloads }\n\n log.plain('')\n log.info(`Generating summary via ${chalk.cyan(config.ai.provider)}…`)\n\n try {\n const summary = await provider.summarize(payload)\n log.plain('')\n log.plain(summary)\n log.plain('')\n\n if (sessions[0]) {\n updateSessionSummary(sessions[0].id, summary)\n }\n\n await handleWebhook(opts, config, summary)\n } catch (e) {\n log.error(`Summary failed [${config.ai.provider}]: ${(e as Error).message}`)\n process.exit(1)\n }\n })\n}\n","import type { DaySummaryPayload, WeekSummaryPayload } from './types'\nimport { formatDuration } from '../utils/format'\n\nexport const SYSTEM_PROMPT = `You write short developer work summaries. Here is an example:\n\nINPUT:\nProject: api-service\nBranch: feature/auth\nDuration: 1h 30m\nEvents:\n - branch_switch {\"branch\":\"feature/auth\"}\n - 2x dependency_install {\"tool\":\"npm\",\"packages\":[\"bcrypt\",\"jsonwebtoken\"]}\n - 2x test_run {\"tool\":\"jest\"} [failed]\n - test_run {\"tool\":\"jest\"}\n - code_commit {\"message\":\"fix: auth token validation\"}\n\nOUTPUT:\n## api-service (feature/auth) — 1h 30m\n- Switched to the authentication feature branch\n- Added JWT and bcrypt dependencies\n- Resolved failing auth tests\n- Committed authentication fixes\n\nNow summarise the following. Output ONLY the summary, nothing else. Maximum 5 bullets.`\n\nexport function buildUserMessage(payload: DaySummaryPayload): string {\n const lines: string[] = [`Date: ${payload.date}`, '']\n\n for (const session of payload.sessions) {\n lines.push(`Project: ${session.project ?? 'unknown'}`)\n if (session.branch) lines.push(`Branch: ${session.branch}`)\n lines.push(`Duration: ${formatDuration(session.durationMinutes)}`)\n lines.push('Events:')\n\n // Aggregate repeated events so the model sees \"3x build\" not 3 separate build lines\n const counts = new Map<string, number>()\n for (const event of session.events) {\n const meta = Object.keys(event.metadata).length > 0 ? ` ${JSON.stringify(event.metadata)}` : ''\n const failed = event.exitCode !== null && event.exitCode !== 0 ? ' [failed]' : ''\n const key = `${event.category}${meta}${failed}`\n counts.set(key, (counts.get(key) ?? 0) + 1)\n }\n\n for (const [key, count] of counts) {\n lines.push(` - ${count > 1 ? `${count}x ` : ''}${key}`)\n }\n\n lines.push('')\n }\n\n return lines.join('\\n')\n}\n\nexport const WEEKLY_SYSTEM_PROMPT = `You write short weekly developer summaries. Here is an example:\n\nINPUT:\nWeek: 2026-05-18 to 2026-05-22\n\nMonday 2026-05-18:\n Project: api-service (feature/auth) — 2h 10m\n Events: 3x code_commit, 2x test_run, dependency_install\n\nTuesday 2026-05-19:\n Project: api-service (feature/auth) — 1h 30m\n Events: 2x code_commit, test_run, build\n\nWednesday 2026-05-20:\n Project: dashboard (main) — 3h 00m\n Events: 4x code_commit, 2x build, test_run\n\nThursday 2026-05-21:\n Project: api-service (feature/auth) — 1h 00m\n Events: code_commit {\"message\":\"feat: merge auth\"}, branch_switch\n\nFriday 2026-05-22:\n Project: dashboard (main) — 2h 00m\n Events: 3x code_commit, build\n\nOUTPUT:\n## Week of 18 May — 9h 40m across 2 projects\n\n**api-service** (Mon–Thu): Built and shipped the auth feature — JWT setup, token validation fixes, and final merge.\n**dashboard** (Wed, Fri): Active development across 7 commits with builds passing.\n\nNow summarise the following week. Output ONLY the summary — a one-line header then one bullet per active project. Maximum 4 bullets.`\n\nconst DAY_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']\n\nexport function buildWeeklyMessage(payload: WeekSummaryPayload): string {\n const lines: string[] = [`Week: ${payload.weekStart} to ${payload.weekEnd}`, '']\n\n for (const { date, sessions } of payload.days) {\n const dayName = DAY_NAMES[new Date(date + 'T00:00:00').getDay()]\n lines.push(`${dayName} ${date}:`)\n\n for (const s of sessions) {\n const project = s.project ?? 'unknown'\n const branch = s.branch ? ` (${s.branch})` : ''\n lines.push(` Project: ${project}${branch} — ${formatDuration(s.durationMinutes)}`)\n\n const counts = new Map<string, number>()\n for (const e of s.events) {\n const meta = Object.keys(e.metadata).length > 0 ? ` ${JSON.stringify(e.metadata)}` : ''\n const failed = e.exitCode !== null && e.exitCode !== 0 ? ' [failed]' : ''\n const key = `${e.category}${meta}${failed}`\n counts.set(key, (counts.get(key) ?? 0) + 1)\n }\n const eventLine = [...counts.entries()]\n .map(([k, n]) => `${n > 1 ? `${n}x ` : ''}${k}`)\n .join(', ')\n lines.push(` Events: ${eventLine}`)\n }\n\n lines.push('')\n }\n\n return lines.join('\\n')\n}\n","const DEFAULT_TIMEOUT_MS = 30_000\n\nexport async function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs = DEFAULT_TIMEOUT_MS\n): Promise<Response> {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n return await fetch(url, { ...init, signal: controller.signal })\n } catch (err) {\n if ((err as Error).name === 'AbortError') {\n throw new Error(`Request timed out after ${timeoutMs / 1000}s`)\n }\n throw err\n } finally {\n clearTimeout(timer)\n }\n}\n","import type { AIProvider, DaySummaryPayload, WeekSummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage, WEEKLY_SYSTEM_PROMPT, buildWeeklyMessage } from '../prompt'\nimport { fetchWithTimeout } from '../fetch'\n\nexport class AnthropicProvider implements AIProvider {\n private apiKey: string\n private model: string\n\n constructor(apiKey: string, model?: string) {\n this.apiKey = apiKey\n this.model = model || 'claude-haiku-4-5-20251001'\n }\n\n async summarize(payload: DaySummaryPayload): Promise<string> {\n const response = await fetchWithTimeout('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: this.model,\n max_tokens: 1024,\n system: SYSTEM_PROMPT,\n messages: [{ role: 'user', content: buildUserMessage(payload) }],\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Anthropic API error ${response.status}: ${body}`)\n }\n\n const data = (await response.json()) as { content: Array<{ type: string; text: string }> }\n return data.content.find((c) => c.type === 'text')?.text ?? ''\n }\n\n async summarizeWeek(payload: WeekSummaryPayload): Promise<string> {\n const response = await fetchWithTimeout('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: this.model,\n max_tokens: 1024,\n system: WEEKLY_SYSTEM_PROMPT,\n messages: [{ role: 'user', content: buildWeeklyMessage(payload) }],\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Anthropic API error ${response.status}: ${body}`)\n }\n\n const data = (await response.json()) as { content: Array<{ type: string; text: string }> }\n return data.content.find((c) => c.type === 'text')?.text ?? ''\n }\n}\n","import type { AIProvider, DaySummaryPayload, WeekSummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage, WEEKLY_SYSTEM_PROMPT, buildWeeklyMessage } from '../prompt'\nimport { fetchWithTimeout } from '../fetch'\n\nexport class OpenAIProvider implements AIProvider {\n private apiKey: string\n private model: string\n private baseUrl: string\n\n constructor(apiKey: string, model?: string, baseUrl?: string) {\n this.apiKey = apiKey\n this.model = model || 'gpt-4o-mini'\n this.baseUrl = baseUrl || 'https://api.openai.com/v1'\n }\n\n async summarize(payload: DaySummaryPayload): Promise<string> {\n const response = await fetchWithTimeout(`${this.baseUrl}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n model: this.model,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserMessage(payload) },\n ],\n max_tokens: 1024,\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`OpenAI API error ${response.status}: ${body}`)\n }\n\n const data = (await response.json()) as {\n choices: Array<{ message: { content: string } }>\n }\n return data.choices[0]?.message?.content ?? ''\n }\n\n async summarizeWeek(payload: WeekSummaryPayload): Promise<string> {\n const response = await fetchWithTimeout(`${this.baseUrl}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n model: this.model,\n messages: [\n { role: 'system', content: WEEKLY_SYSTEM_PROMPT },\n { role: 'user', content: buildWeeklyMessage(payload) },\n ],\n max_tokens: 1024,\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`OpenAI API error ${response.status}: ${body}`)\n }\n\n const data = (await response.json()) as {\n choices: Array<{ message: { content: string } }>\n }\n return data.choices[0]?.message?.content ?? ''\n }\n}\n","import type { AIProvider, DaySummaryPayload, WeekSummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage, WEEKLY_SYSTEM_PROMPT, buildWeeklyMessage } from '../prompt'\nimport { fetchWithTimeout } from '../fetch'\n\nexport class OllamaProvider implements AIProvider {\n private model: string\n private baseUrl: string\n\n constructor(model?: string, baseUrl?: string) {\n this.model = model || 'llama3'\n this.baseUrl = baseUrl || 'http://localhost:11434'\n }\n\n async summarize(payload: DaySummaryPayload): Promise<string> {\n const response = await fetchWithTimeout(`${this.baseUrl}/api/chat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.model,\n stream: false,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserMessage(payload) },\n ],\n options: {\n num_predict: 300, // hard cap on output tokens — forces conciseness\n temperature: 0.3, // lower temperature = more focused, less wandering\n },\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Ollama error ${response.status}: ${body}. Is Ollama running? Try: ollama serve`)\n }\n\n const data = (await response.json()) as { message: { content: string } }\n return data.message?.content ?? ''\n }\n\n async summarizeWeek(payload: WeekSummaryPayload): Promise<string> {\n const response = await fetchWithTimeout(`${this.baseUrl}/api/chat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.model,\n stream: false,\n messages: [\n { role: 'system', content: WEEKLY_SYSTEM_PROMPT },\n { role: 'user', content: buildWeeklyMessage(payload) },\n ],\n options: {\n num_predict: 400,\n temperature: 0.3,\n },\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Ollama error ${response.status}: ${body}. Is Ollama running? Try: ollama serve`)\n }\n\n const data = (await response.json()) as { message: { content: string } }\n return data.message?.content ?? ''\n }\n}\n","import type { AIProvider } from './types'\nimport type { WorkscribeConfig } from '../config'\nimport { AnthropicProvider } from './providers/anthropic'\nimport { OpenAIProvider } from './providers/openai'\nimport { OllamaProvider } from './providers/ollama'\n\nexport function getProvider(config: WorkscribeConfig): AIProvider {\n const { provider, apiKey, model, baseUrl } = config.ai\n\n if (!provider) {\n throw new Error(\n 'No AI provider configured.\\nRun: workscribe config set ai.provider <anthropic|openai|ollama|openai-compatible>'\n )\n }\n\n switch (provider) {\n case 'anthropic': {\n if (!apiKey) throw new Error('Anthropic requires an API key.\\nRun: workscribe config set ai.apiKey <your-key>')\n return new AnthropicProvider(apiKey, model || undefined)\n }\n\n case 'openai': {\n if (!apiKey) throw new Error('OpenAI requires an API key.\\nRun: workscribe config set ai.apiKey <your-key>')\n return new OpenAIProvider(apiKey, model || undefined)\n }\n\n case 'openai-compatible': {\n if (!apiKey) throw new Error('This provider requires an API key.\\nRun: workscribe config set ai.apiKey <your-key>')\n if (!baseUrl) throw new Error('openai-compatible requires a base URL.\\nRun: workscribe config set ai.baseUrl <url>')\n return new OpenAIProvider(apiKey, model || undefined, baseUrl)\n }\n\n case 'ollama': {\n return new OllamaProvider(model || undefined, baseUrl || undefined)\n }\n\n default:\n throw new Error(\n `Unknown provider: \"${provider}\"\\nSupported providers: anthropic, openai, ollama, openai-compatible`\n )\n }\n}\n","import { fetchWithTimeout } from '../ai/fetch'\n\nexport async function postToWebhook(url: string, text: string): Promise<void> {\n const response = await fetchWithTimeout(\n url,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ text }),\n },\n 10_000,\n )\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Webhook POST failed ${response.status}: ${body}`)\n }\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { getProvider } from '../ai/router'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\nimport { EXPORTS_DIR } from '../utils/paths'\nimport type { DaySummaryPayload, SessionPayload, EventPayload } from '../ai/types'\nimport type { EventRow } from '../db'\n\nfunction eventRowToPayload(row: EventRow): EventPayload {\n let metadata: Record<string, unknown> = {}\n try {\n if (row.metadata) metadata = JSON.parse(row.metadata)\n } catch { /* ignore */ }\n return { category: row.category, metadata, exitCode: row.exit_code, durationMs: row.duration_ms }\n}\n\nexport function registerExport(program: Command): void {\n program\n .command('export')\n .description('Export work summary as a markdown file')\n .option('--date <YYYY-MM-DD>', 'date to export (default: today)')\n .option('--output <path>', 'output file path (default: ~/.workscribe/exports/)')\n .action(async (opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const sessions = getOrBuildSessions(date)\n if (sessions.length === 0) {\n log.plain(`No activity captured for ${date}`)\n return\n }\n\n const config = readConfig()\n let provider\n try {\n provider = getProvider(config)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n\n const sessionPayloads: SessionPayload[] = sessions.map((s) => ({\n date,\n project: s.repoName,\n branch: s.events[0]?.branch ?? null,\n startTime: formatDate(s.startedAt),\n endTime: formatDate(s.endedAt),\n durationMinutes: s.durationMinutes,\n events: s.events.map(eventRowToPayload),\n }))\n\n const payload: DaySummaryPayload = { date, sessions: sessionPayloads }\n\n log.info(`Generating summary via ${config.ai.provider}…`)\n const summary = await provider.summarize(payload)\n\n const outputPath = opts.output ?? path.join(EXPORTS_DIR, `workscribe-${date}.md`)\n\n if (!fs.existsSync(EXPORTS_DIR)) fs.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 0o700 })\n\n fs.writeFileSync(outputPath, `# Work Summary — ${date}\\n\\n${summary}\\n`, { encoding: 'utf-8', mode: 0o600 })\n log.success(`Saved: ${outputPath}`)\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { readConfig, writeConfig, getConfigValue, setConfigValue } from '../config'\nimport { log } from '../utils/logger'\n\nexport function registerConfig(program: Command): void {\n const configCmd = program\n .command('config')\n .description('View or update Workscribe configuration')\n .action(() => {\n const config = readConfig()\n log.plain('')\n log.plain(chalk.bold(' Workscribe Config'))\n log.plain('')\n printConfig(config, ' ')\n log.plain('')\n })\n\n configCmd\n .command('set <key> <value>')\n .description('Set a config value (e.g. workscribe config set ai.provider anthropic)')\n .action((key: string, value: string) => {\n const config = readConfig()\n const updated = setConfigValue(config, key, value)\n writeConfig(updated)\n const isSensitive = key === 'ai.apiKey' || key.toLowerCase().includes('key') || key.toLowerCase().includes('secret') || key.toLowerCase().includes('token')\n const displayValue = isSensitive ? '[set]' : chalk.yellow(value)\n log.success(`Set ${chalk.cyan(key)} = ${displayValue}`)\n })\n\n configCmd\n .command('get <key>')\n .description('Get a config value')\n .action((key: string) => {\n const config = readConfig()\n const value = getConfigValue(config, key)\n if (value === undefined) {\n log.warn(`Key not found: ${key}`)\n } else {\n log.plain(String(value))\n }\n })\n}\n\nconst SENSITIVE_KEYS = new Set(['apiKey', 'api_key', 'secret', 'token', 'password', 'passwd', 'webhook', 'slackWebhook'])\n\nfunction maskValue(key: string, value: unknown): string {\n if (SENSITIVE_KEYS.has(key) && typeof value === 'string' && value.length > 0) {\n return chalk.dim(`${value.slice(0, 4)}${'*'.repeat(8)} (${value.length} chars)`)\n }\n if (Array.isArray(value)) return `[${(value as unknown[]).join(', ')}]`\n return String(value) || chalk.dim('(not set)')\n}\n\nfunction printConfig(obj: unknown, indent: string): void {\n if (typeof obj !== 'object' || obj === null) return\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n log.plain(`${indent}${chalk.bold(key)}:`)\n printConfig(value, indent + ' ')\n } else {\n log.plain(`${indent}${chalk.dim(key)}: ${chalk.yellow(maskValue(key, value))}`)\n }\n }\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport { PAUSE_FLAG_PATH } from '../utils/paths'\nimport { log } from '../utils/logger'\n\nexport function registerPause(program: Command): void {\n program\n .command('pause')\n .description('Pause capture temporarily')\n .action(() => {\n if (fs.existsSync(PAUSE_FLAG_PATH)) {\n log.info('Capture is already paused. Run workscribe resume to restart.')\n return\n }\n fs.writeFileSync(PAUSE_FLAG_PATH, '', { mode: 0o600 })\n log.success('Capture paused. Run workscribe resume to restart.')\n })\n\n program\n .command('resume')\n .description('Resume capture after a pause')\n .action(() => {\n if (!fs.existsSync(PAUSE_FLAG_PATH)) {\n log.info('Capture is not paused.')\n return\n }\n fs.rmSync(PAUSE_FLAG_PATH)\n log.success('Capture resumed.')\n })\n}\n","import { Command } from 'commander'\nimport { getLastEventIds, getEventsByIds, deleteEventsByIds } from '../db'\nimport { log } from '../utils/logger'\n\nexport function registerUndo(program: Command): void {\n program\n .command('undo')\n .description('Remove the last N captured events')\n .option('-n, --count <number>', 'number of events to remove', '1')\n .option('--dry-run', 'preview what would be deleted without deleting')\n .action((opts) => {\n const count = parseInt(opts.count, 10)\n if (isNaN(count) || count < 1) {\n log.error('--count must be a positive integer')\n process.exit(1)\n }\n\n const ids = getLastEventIds(count)\n if (ids.length === 0) {\n log.plain('No events to undo.')\n return\n }\n\n const events = getEventsByIds(ids)\n log.plain('')\n log.plain(`Events to remove (${events.length}):`)\n for (const e of events) {\n log.plain(` ${e.captured_at} ${e.category} ${e.command}`)\n }\n log.plain('')\n\n if (opts.dryRun) {\n log.plain('Dry run — nothing deleted.')\n return\n }\n\n deleteEventsByIds(ids)\n log.success(`Removed ${ids.length} event${ids.length > 1 ? 's' : ''}.`)\n })\n}\n"],"mappings":"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","names":["import_fs","path","os","fs","import_path","import_fs","fs","path","import_fs","fs","fs","import_fs","chalk","fs","program","import_fs","import_path","import_os","import_chalk","path","os","fs","program","chalk","prompts","import_fs","import_chalk","program","fs","chalk","import_chalk","chalk","program","import_chalk","program","chalk","import_chalk","import_fs","import_path","program","sessions","path","fs","chalk","payload","import_fs","import_path","eventRowToPayload","program","path","fs","import_chalk","program","chalk","import_fs","program","fs","program"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workscribe/cli",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
4
4
  "description": "Passive developer work journal — automatic terminal activity summaries",
5
5
  "type": "commonjs",
6
6
  "bin": {