@workscribe/cli 1.2.2 → 1.4.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
@@ -31,7 +31,7 @@ shell hook → capture → SQLite → session grouper → AI provider → markdo
31
31
  |---|---|---|
32
32
  | Node.js | **22 or later** | Uses the built-in `node:sqlite` module — not available in Node 20 or earlier |
33
33
  | Shell | zsh or bash | Hook is appended to `~/.zshrc` or `~/.bashrc` by `workscribe init` |
34
- | OS | macOS or Linux | Windows is not supported |
34
+ | OS | macOS or Linux | Windows is not supported — the shell hook requires zsh or bash. WSL2 works. |
35
35
 
36
36
  > Check your version: `node --version`
37
37
 
package/dist/index.js CHANGED
@@ -271,6 +271,78 @@ function getGitContext(cwd, nameOverride) {
271
271
  }
272
272
  }
273
273
 
274
+ // src/capture/git-snapshot.ts
275
+ var import_child_process2 = require("child_process");
276
+ function getCommitsSince(sha, cwd) {
277
+ if (!sha) return [];
278
+ try {
279
+ const out = (0, import_child_process2.execSync)(`git log ${sha}..HEAD --format=%s`, { cwd, timeout: 3e3 }).toString().trim();
280
+ return out ? out.split("\n").filter(Boolean) : [];
281
+ } catch {
282
+ return [];
283
+ }
284
+ }
285
+
286
+ // src/config/ai-tools.json
287
+ var ai_tools_default = [
288
+ {
289
+ tool: "claude",
290
+ displayName: "Claude Code",
291
+ pattern: "^claude\\b",
292
+ hook: "claude-code",
293
+ contextSource: "hook"
294
+ },
295
+ {
296
+ tool: "aider",
297
+ displayName: "Aider",
298
+ pattern: "^aider\\b",
299
+ hook: null,
300
+ contextSource: "history-file"
301
+ },
302
+ {
303
+ tool: "gemini",
304
+ displayName: "Gemini",
305
+ pattern: "^gemini\\b",
306
+ hook: null,
307
+ contextSource: null
308
+ },
309
+ {
310
+ tool: "gh copilot",
311
+ displayName: "GitHub Copilot",
312
+ pattern: "^gh\\s+copilot\\b",
313
+ hook: null,
314
+ contextSource: null
315
+ },
316
+ {
317
+ tool: "cody",
318
+ displayName: "Cody",
319
+ pattern: "^cody\\b",
320
+ hook: null,
321
+ contextSource: null
322
+ },
323
+ {
324
+ tool: "amazon-q",
325
+ displayName: "Amazon Q",
326
+ pattern: "^q\\s+(chat|dev)\\b",
327
+ hook: null,
328
+ contextSource: null
329
+ },
330
+ {
331
+ tool: "continue",
332
+ displayName: "Continue",
333
+ pattern: "^continue\\b",
334
+ hook: null,
335
+ contextSource: null
336
+ },
337
+ {
338
+ tool: "cursor",
339
+ displayName: "Cursor",
340
+ pattern: "^cursor\\b",
341
+ hook: null,
342
+ contextSource: null
343
+ }
344
+ ];
345
+
274
346
  // src/normalize/patterns.ts
275
347
  function makeRule(pattern, category, extract = () => ({})) {
276
348
  return {
@@ -413,10 +485,15 @@ var RULES = [
413
485
  makeRule(/^npm\s+run\s+\S+/, "script_run", (_m, cmd) => ({
414
486
  tool: "npm",
415
487
  script: cmd.split(/\s+/)[2]
416
- }))
488
+ })),
489
+ // ─── AI terminal tools (registry-driven) ─────────────────────────────────
490
+ ...ai_tools_default.map(
491
+ (entry) => makeRule(new RegExp(entry.pattern), "ai_session", () => ({ tool: entry.tool }))
492
+ )
417
493
  ];
418
494
  function normalizeCommand(command) {
419
- const trimmed = command.trim().replace(/\s+/g, " ");
495
+ let trimmed = command.trim().replace(/\s+/g, " ");
496
+ trimmed = trimmed.replace(/^(git)\s+-C\s+\S+\s+/, "$1 ");
420
497
  for (const rule of RULES) {
421
498
  const result = rule.match(trimmed);
422
499
  if (result) return result;
@@ -507,12 +584,14 @@ function deepMerge(defaults, overrides) {
507
584
  function normalizeForFilter(cmd) {
508
585
  return cmd.trim().replace(/\s+/g, " ");
509
586
  }
587
+ var BUILTIN_IGNORE = /* @__PURE__ */ new Set(["workscribe"]);
510
588
  function shouldFilter(command, ignoreList) {
511
589
  const normalized = normalizeForFilter(command);
512
590
  if (!normalized) return true;
591
+ const baseCmd = normalized.split(" ")[0];
592
+ if (BUILTIN_IGNORE.has(baseCmd)) return true;
513
593
  const normalizedIgnore = ignoreList.map(normalizeForFilter);
514
594
  if (normalizedIgnore.includes(normalized)) return true;
515
- const baseCmd = normalized.split(" ")[0];
516
595
  if (normalizedIgnore.includes(baseCmd)) return true;
517
596
  return false;
518
597
  }
@@ -534,6 +613,10 @@ function captureCommand(opts) {
534
613
  const nameOverride = config.projects?.[cwd] ?? null;
535
614
  const { repoName, branch } = getGitContext(cwd, nameOverride);
536
615
  const { category, metadata } = normalizeCommand(safeCommand);
616
+ if (category === "ai_session" && opts.gitBefore) {
617
+ const commits = getCommitsSince(opts.gitBefore, cwd);
618
+ if (commits.length > 0) metadata.commits = commits;
619
+ }
537
620
  insertEvent({
538
621
  captured_at: (/* @__PURE__ */ new Date()).toISOString(),
539
622
  command: safeCommand,
@@ -570,14 +653,15 @@ function debugLog(context, error) {
570
653
 
571
654
  // src/cli/capture.ts
572
655
  function registerCapture(program2) {
573
- program2.command("_capture").description("Internal: capture a shell command event (called by shell hook)").option("--cmd <command>", "command that was executed").option("--cwd <path>", "working directory").option("--exit <code>", "exit code", parseInt).option("--duration <ms>", "duration in milliseconds", parseInt).action((opts) => {
656
+ program2.command("_capture").description("Internal: capture a shell command event (called by shell hook)").option("--cmd <command>", "command that was executed").option("--cwd <path>", "working directory").option("--exit <code>", "exit code", parseInt).option("--duration <ms>", "duration in milliseconds", parseInt).option("--git-before <sha>", "git HEAD SHA before this command ran").action((opts) => {
574
657
  try {
575
658
  if (!opts.cmd || !opts.cwd) return;
576
659
  captureCommand({
577
660
  command: opts.cmd,
578
661
  cwd: opts.cwd,
579
662
  exitCode: opts.exit ?? null,
580
- durationMs: opts.duration ?? null
663
+ durationMs: opts.duration ?? null,
664
+ gitBefore: opts.gitBefore ?? void 0
581
665
  });
582
666
  } catch (err) {
583
667
  debugLog("_capture", err);
@@ -585,22 +669,62 @@ function registerCapture(program2) {
585
669
  });
586
670
  }
587
671
 
672
+ // src/cli/hook.ts
673
+ function parseHookPayload(raw) {
674
+ if (!raw.trim()) return null;
675
+ const payload = JSON.parse(raw);
676
+ const toolInput = payload.tool_input;
677
+ const command = typeof toolInput?.command === "string" ? toolInput.command : null;
678
+ const cwd = typeof payload.cwd === "string" ? payload.cwd : null;
679
+ if (!command || !cwd) return null;
680
+ return { command, cwd };
681
+ }
682
+ function registerHook(program2) {
683
+ program2.command("_hook").description("Internal: capture events from AI tool hooks (stdin JSON payload)").option("--source <source>", "hook source identifier (e.g. claude-code)").action(() => {
684
+ let raw = "";
685
+ process.stdin.setEncoding("utf-8");
686
+ process.stdin.on("data", (chunk) => {
687
+ raw += chunk;
688
+ });
689
+ process.stdin.on("end", () => {
690
+ try {
691
+ const parsed = parseHookPayload(raw);
692
+ if (!parsed) return;
693
+ captureCommand({ command: parsed.command, cwd: parsed.cwd, exitCode: null, durationMs: null });
694
+ } catch (err) {
695
+ debugLog("_hook", err);
696
+ }
697
+ });
698
+ });
699
+ }
700
+
588
701
  // src/cli/init.ts
589
- var import_fs6 = __toESM(require("fs"));
590
- var import_path3 = __toESM(require("path"));
591
- var import_os2 = __toESM(require("os"));
702
+ var import_fs8 = __toESM(require("fs"));
703
+ var import_path5 = __toESM(require("path"));
704
+ var import_os3 = __toESM(require("os"));
592
705
  var import_prompts = __toESM(require("prompts"));
593
706
  var import_chalk2 = __toESM(require("chalk"));
707
+
708
+ // src/capture/shell-hook.ts
709
+ var import_fs6 = __toESM(require("fs"));
710
+ var import_path3 = __toESM(require("path"));
594
711
  var HOOK_START = "# workscribe:hook:start";
595
- function detectShell() {
596
- const shell = process.env.SHELL ?? "";
597
- if (shell.includes("zsh")) return { name: "zsh", rcFile: import_path3.default.join(import_os2.default.homedir(), ".zshrc") };
598
- if (shell.includes("bash")) return { name: "bash", rcFile: import_path3.default.join(import_os2.default.homedir(), ".bashrc") };
599
- return null;
712
+ var HOOK_END = "# workscribe:hook:end";
713
+ function getInstalledHookBlock(rcFile) {
714
+ if (!import_fs6.default.existsSync(rcFile)) return null;
715
+ const content = import_fs6.default.readFileSync(rcFile, "utf-8");
716
+ const startIdx = content.indexOf(HOOK_START);
717
+ const endIdx = content.indexOf(HOOK_END);
718
+ if (startIdx === -1 || endIdx === -1) return null;
719
+ return content.slice(startIdx, endIdx + HOOK_END.length);
600
720
  }
601
721
  function isHookInstalled(rcFile) {
602
- if (!import_fs6.default.existsSync(rcFile)) return false;
603
- return import_fs6.default.readFileSync(rcFile, "utf-8").includes(HOOK_START);
722
+ return getInstalledHookBlock(rcFile) !== null;
723
+ }
724
+ function isHookOutdated(rcFile, hookContent) {
725
+ const installed = getInstalledHookBlock(rcFile);
726
+ if (!installed) return false;
727
+ return installed.trim() !== hookContent.trim();
604
728
  }
605
729
  function installHook(rcFile, hookContent) {
606
730
  const backup = `${rcFile}.workscribe-backup-${Date.now()}`;
@@ -610,15 +734,98 @@ function installHook(rcFile, hookContent) {
610
734
  ${hookContent}
611
735
  `);
612
736
  }
737
+ function updateHook(rcFile, hookContent) {
738
+ const backup = `${rcFile}.workscribe-backup-${Date.now()}`;
739
+ import_fs6.default.copyFileSync(rcFile, backup);
740
+ log.info(`Backed up ${rcFile} \u2192 ${import_path3.default.basename(backup)}`);
741
+ const content = import_fs6.default.readFileSync(rcFile, "utf-8");
742
+ const startIdx = content.indexOf(HOOK_START);
743
+ const endIdx = content.indexOf(HOOK_END) + HOOK_END.length;
744
+ import_fs6.default.writeFileSync(rcFile, content.slice(0, startIdx) + hookContent + content.slice(endIdx));
745
+ }
746
+
747
+ // src/capture/hooks/claude-code.ts
748
+ var import_fs7 = __toESM(require("fs"));
749
+ var import_path4 = __toESM(require("path"));
750
+ var import_os2 = __toESM(require("os"));
751
+
752
+ // src/capture/hooks/utils.ts
753
+ var import_child_process3 = require("child_process");
754
+ function isCommandInstalled(cmd) {
755
+ try {
756
+ (0, import_child_process3.execSync)(`which ${cmd}`, { stdio: "ignore" });
757
+ return true;
758
+ } catch {
759
+ return false;
760
+ }
761
+ }
762
+
763
+ // src/capture/hooks/claude-code.ts
764
+ var CLAUDE_SETTINGS_PATH = import_path4.default.join(import_os2.default.homedir(), ".claude", "settings.json");
765
+ function readSettings() {
766
+ if (!import_fs7.default.existsSync(CLAUDE_SETTINGS_PATH)) return {};
767
+ try {
768
+ return JSON.parse(import_fs7.default.readFileSync(CLAUDE_SETTINGS_PATH, "utf-8"));
769
+ } catch {
770
+ return {};
771
+ }
772
+ }
773
+ var claudeCodeIntegration = {
774
+ displayName: "Claude Code",
775
+ isInstalled() {
776
+ return isCommandInstalled("claude");
777
+ },
778
+ isHookInstalled() {
779
+ const settings = readSettings();
780
+ const postToolUse = settings.hooks?.PostToolUse ?? [];
781
+ return postToolUse.some((entry) => {
782
+ const hooks = entry.hooks ?? [];
783
+ return hooks.some((h) => {
784
+ const hook = h;
785
+ return typeof hook.command === "string" && hook.command.includes("workscribe _hook");
786
+ });
787
+ });
788
+ },
789
+ install() {
790
+ const settings = readSettings();
791
+ const existingHooks = settings.hooks ?? {};
792
+ const existingPostToolUse = existingHooks.PostToolUse ?? [];
793
+ settings.hooks = {
794
+ ...existingHooks,
795
+ PostToolUse: [
796
+ ...existingPostToolUse,
797
+ {
798
+ matcher: "Bash",
799
+ hooks: [{ type: "command", command: "workscribe _hook --source claude-code", async: true }]
800
+ }
801
+ ]
802
+ };
803
+ import_fs7.default.mkdirSync(import_path4.default.dirname(CLAUDE_SETTINGS_PATH), { recursive: true });
804
+ import_fs7.default.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
805
+ }
806
+ };
807
+
808
+ // src/capture/hooks/index.ts
809
+ var AI_HOOK_INTEGRATIONS = {
810
+ "claude-code": claudeCodeIntegration
811
+ };
812
+
813
+ // src/cli/init.ts
814
+ function detectShell() {
815
+ const shell = process.env.SHELL ?? "";
816
+ if (shell.includes("zsh")) return { name: "zsh", rcFile: import_path5.default.join(import_os3.default.homedir(), ".zshrc") };
817
+ if (shell.includes("bash")) return { name: "bash", rcFile: import_path5.default.join(import_os3.default.homedir(), ".bashrc") };
818
+ return null;
819
+ }
613
820
  function registerInit(program2) {
614
- program2.command("init").description("Install shell hooks and set up Workscribe").action(async () => {
821
+ program2.command("init").description("Set up Workscribe and install or update shell hooks").action(async () => {
615
822
  log.plain(import_chalk2.default.bold("\n Workscribe \u2014 Developer Work Journal\n"));
616
- if (!import_fs6.default.existsSync(WORKSCRIBE_DIR)) {
617
- import_fs6.default.mkdirSync(WORKSCRIBE_DIR, { recursive: true, mode: 448 });
823
+ if (!import_fs8.default.existsSync(WORKSCRIBE_DIR)) {
824
+ import_fs8.default.mkdirSync(WORKSCRIBE_DIR, { recursive: true, mode: 448 });
618
825
  log.success(`Created ${WORKSCRIBE_DIR}`);
619
826
  } else {
620
827
  try {
621
- import_fs6.default.chmodSync(WORKSCRIBE_DIR, 448);
828
+ import_fs8.default.chmodSync(WORKSCRIBE_DIR, 448);
622
829
  } catch {
623
830
  }
624
831
  }
@@ -629,17 +836,48 @@ function registerInit(program2) {
629
836
  log.warn("Could not detect shell. Supported: zsh, bash");
630
837
  log.plain(`Manually add the hook from: ${import_chalk2.default.cyan("shell/hook.zsh")} or ${import_chalk2.default.cyan("shell/hook.bash")}`);
631
838
  } else {
632
- if (isHookInstalled(shell.rcFile)) {
633
- log.info(`Shell hook already installed in ${shell.rcFile}`);
839
+ const hookFile = import_path5.default.join(__dirname, "..", "shell", `hook.${shell.name}`);
840
+ if (!import_fs8.default.existsSync(hookFile)) {
841
+ log.warn(`Hook template not found at ${hookFile}`);
634
842
  } else {
635
- const hookFile = import_path3.default.join(__dirname, "..", "shell", `hook.${shell.name}`);
636
- if (!import_fs6.default.existsSync(hookFile)) {
637
- log.warn(`Hook template not found at ${hookFile}`);
638
- } else {
639
- const hookContent = import_fs6.default.readFileSync(hookFile, "utf-8");
640
- if (!import_fs6.default.existsSync(shell.rcFile)) import_fs6.default.writeFileSync(shell.rcFile, "");
843
+ const hookContent = import_fs8.default.readFileSync(hookFile, "utf-8");
844
+ if (!import_fs8.default.existsSync(shell.rcFile)) import_fs8.default.writeFileSync(shell.rcFile, "");
845
+ if (!isHookInstalled(shell.rcFile)) {
641
846
  installHook(shell.rcFile, hookContent);
642
847
  log.success(`Shell hook installed in ${shell.rcFile}`);
848
+ } else if (isHookOutdated(shell.rcFile, hookContent)) {
849
+ const { update } = await (0, import_prompts.default)({
850
+ type: "confirm",
851
+ name: "update",
852
+ message: `Shell hook in ${shell.rcFile} is outdated. Update?`,
853
+ initial: true
854
+ });
855
+ if (update) {
856
+ updateHook(shell.rcFile, hookContent);
857
+ log.success(`Shell hook updated in ${shell.rcFile}`);
858
+ } else {
859
+ log.info("Shell hook update skipped");
860
+ }
861
+ } else {
862
+ log.info(`Shell hook up to date in ${shell.rcFile}`);
863
+ }
864
+ }
865
+ }
866
+ for (const tool of ai_tools_default.filter((t) => t.hook !== null)) {
867
+ const integration = AI_HOOK_INTEGRATIONS[tool.hook];
868
+ if (!integration?.isInstalled()) continue;
869
+ if (integration.isHookInstalled()) {
870
+ log.info(`${integration.displayName} session hook already installed`);
871
+ } else {
872
+ const { install } = await (0, import_prompts.default)({
873
+ type: "confirm",
874
+ name: "install",
875
+ message: `${integration.displayName} detected. Install session capture hook?`,
876
+ initial: true
877
+ });
878
+ if (install) {
879
+ integration.install();
880
+ log.success(`${integration.displayName} session hook installed`);
643
881
  }
644
882
  }
645
883
  }
@@ -689,7 +927,7 @@ function registerInit(program2) {
689
927
  log.info("Skipped \u2014 run later: workscribe config set ai.provider <provider>");
690
928
  }
691
929
  }
692
- if (!import_fs6.default.existsSync(CONFIG_PATH)) {
930
+ if (!import_fs8.default.existsSync(CONFIG_PATH)) {
693
931
  writeConfig(config);
694
932
  log.success("Config file created");
695
933
  }
@@ -708,7 +946,7 @@ function registerInit(program2) {
708
946
  }
709
947
 
710
948
  // src/cli/status.ts
711
- var import_fs7 = __toESM(require("fs"));
949
+ var import_fs9 = __toESM(require("fs"));
712
950
  var import_chalk3 = __toESM(require("chalk"));
713
951
 
714
952
  // src/utils/format.ts
@@ -766,7 +1004,7 @@ function registerStatus(program2) {
766
1004
  const total = getEventCount();
767
1005
  const todayEvents = getEventsForDate(todayDate());
768
1006
  const last = getLastEvent();
769
- const isPaused = import_fs7.default.existsSync(PAUSE_FLAG_PATH);
1007
+ const isPaused = import_fs9.default.existsSync(PAUSE_FLAG_PATH);
770
1008
  log.plain("");
771
1009
  log.plain(import_chalk3.default.bold(" Workscribe Status"));
772
1010
  log.plain("");
@@ -805,8 +1043,12 @@ var CATEGORY_COLORS = {
805
1043
  git_other: import_chalk4.default.blue,
806
1044
  code_merge: import_chalk4.default.green,
807
1045
  code_stash: import_chalk4.default.blue,
1046
+ ai_session: import_chalk4.default.bold.magenta,
808
1047
  other: import_chalk4.default.gray
809
1048
  };
1049
+ var AI_TOOL_DISPLAY_NAMES = Object.fromEntries(
1050
+ ai_tools_default.map((t) => [t.tool, t.displayName])
1051
+ );
810
1052
  function registerEvents(program2) {
811
1053
  program2.command("events").description("List captured events").option("--date <YYYY-MM-DD>", "date to list events for (default: today)").option("--limit <n>", "max events to show", parseInt).action((opts) => {
812
1054
  let date = todayDate();
@@ -831,10 +1073,22 @@ function registerEvents(program2) {
831
1073
  const colorFn = CATEGORY_COLORS[event.category] ?? import_chalk4.default.gray;
832
1074
  const time = formatDate(event.captured_at);
833
1075
  const category = colorFn(event.category.padEnd(20));
834
- const cmd = import_chalk4.default.dim(event.command.slice(0, 55));
835
1076
  const repo = event.repo_name ? import_chalk4.default.dim(` [${event.repo_name}]`) : "";
836
1077
  const exitBadge = event.exit_code && event.exit_code !== 0 ? import_chalk4.default.red(` \u2717${event.exit_code}`) : "";
837
- log.plain(` ${import_chalk4.default.dim(time)} ${category} ${cmd}${repo}${exitBadge}`);
1078
+ let cmdDisplay;
1079
+ if (event.category === "ai_session") {
1080
+ let meta = {};
1081
+ try {
1082
+ meta = event.metadata ? JSON.parse(event.metadata) : {};
1083
+ } catch {
1084
+ }
1085
+ const toolKey = typeof meta.tool === "string" ? meta.tool : "";
1086
+ const toolName = AI_TOOL_DISPLAY_NAMES[toolKey] ?? toolKey;
1087
+ cmdDisplay = import_chalk4.default.white(toolName.padEnd(20)) + import_chalk4.default.magenta(" \u2726");
1088
+ } else {
1089
+ cmdDisplay = import_chalk4.default.dim(event.command.slice(0, 55));
1090
+ }
1091
+ log.plain(` ${import_chalk4.default.dim(time)} ${category} ${cmdDisplay}${repo}${exitBadge}`);
838
1092
  }
839
1093
  if (limit < events.length) {
840
1094
  log.plain("");
@@ -976,8 +1230,8 @@ function registerSessions(program2) {
976
1230
 
977
1231
  // src/cli/summary.ts
978
1232
  var import_chalk6 = __toESM(require("chalk"));
979
- var import_fs8 = __toESM(require("fs"));
980
- var import_path4 = __toESM(require("path"));
1233
+ var import_fs10 = __toESM(require("fs"));
1234
+ var import_path6 = __toESM(require("path"));
981
1235
 
982
1236
  // src/ai/prompt.ts
983
1237
  var SYSTEM_PROMPT = `You write short developer work summaries. Here is an example:
@@ -999,6 +1253,8 @@ OUTPUT:
999
1253
  - Resolved failing auth tests
1000
1254
  - Committed authentication fixes
1001
1255
 
1256
+ For ai_session events: describe what was built during the AI session using the tool name (e.g. "via Claude Code"). If a commits array is present in metadata, use those commit messages to describe what was built. Group all activity from the session into one narrative bullet. Maximum 5 bullets.
1257
+
1002
1258
  Now summarise the following. Output ONLY the bullet points, nothing else. Maximum 5 bullets.`;
1003
1259
  function buildUserMessage(payload) {
1004
1260
  const lines = [`Date: ${payload.date}`, ""];
@@ -1009,6 +1265,7 @@ function buildUserMessage(payload) {
1009
1265
  lines.push("Events:");
1010
1266
  const counts = /* @__PURE__ */ new Map();
1011
1267
  for (const event of session.events) {
1268
+ if (event.category === "environment") continue;
1012
1269
  const meta = Object.keys(event.metadata).length > 0 ? ` ${JSON.stringify(event.metadata)}` : "";
1013
1270
  const failed = event.exitCode !== null && event.exitCode !== 0 ? " [failed]" : "";
1014
1271
  const key = `${event.category}${meta}${failed}`;
@@ -1065,6 +1322,7 @@ function buildWeeklyMessage(payload) {
1065
1322
  lines.push(` Project: ${project}${branch} \u2014 ${formatDuration(s.durationMinutes)}`);
1066
1323
  const counts = /* @__PURE__ */ new Map();
1067
1324
  for (const e of s.events) {
1325
+ if (e.category === "environment") continue;
1068
1326
  const meta = Object.keys(e.metadata).length > 0 ? ` ${JSON.stringify(e.metadata)}` : "";
1069
1327
  const failed = e.exitCode !== null && e.exitCode !== 0 ? " [failed]" : "";
1070
1328
  const key = `${e.category}${meta}${failed}`;
@@ -1382,9 +1640,9 @@ function registerSummary(program2) {
1382
1640
  return;
1383
1641
  }
1384
1642
  const cacheKey = isoWeekKey(today);
1385
- const cacheFile = import_path4.default.join(WEEKLY_CACHE_DIR, `${cacheKey}.md`);
1386
- if (!opts.fresh && import_fs8.default.existsSync(cacheFile)) {
1387
- const cached = import_fs8.default.readFileSync(cacheFile, "utf-8");
1643
+ const cacheFile = import_path6.default.join(WEEKLY_CACHE_DIR, `${cacheKey}.md`);
1644
+ if (!opts.fresh && import_fs10.default.existsSync(cacheFile)) {
1645
+ const cached = import_fs10.default.readFileSync(cacheFile, "utf-8");
1388
1646
  log.plain("");
1389
1647
  log.plain(cached);
1390
1648
  log.plain("");
@@ -1400,8 +1658,8 @@ function registerSummary(program2) {
1400
1658
  log.plain("");
1401
1659
  log.plain(summary);
1402
1660
  log.plain("");
1403
- import_fs8.default.mkdirSync(WEEKLY_CACHE_DIR, { recursive: true });
1404
- import_fs8.default.writeFileSync(cacheFile, summary, { encoding: "utf-8", mode: 384 });
1661
+ import_fs10.default.mkdirSync(WEEKLY_CACHE_DIR, { recursive: true });
1662
+ import_fs10.default.writeFileSync(cacheFile, summary, { encoding: "utf-8", mode: 384 });
1405
1663
  await handleWebhook(opts, config, summary);
1406
1664
  } catch (e) {
1407
1665
  log.error(`Weekly summary failed [${config.ai.provider}]: ${e.message}`);
@@ -1464,8 +1722,8 @@ ${bullets.trim()}`;
1464
1722
  }
1465
1723
 
1466
1724
  // src/cli/export.ts
1467
- var import_fs9 = __toESM(require("fs"));
1468
- var import_path5 = __toESM(require("path"));
1725
+ var import_fs11 = __toESM(require("fs"));
1726
+ var import_path7 = __toESM(require("path"));
1469
1727
  function eventRowToPayload2(row) {
1470
1728
  let metadata = {};
1471
1729
  try {
@@ -1510,9 +1768,9 @@ function registerExport(program2) {
1510
1768
  const payload = { date, sessions: sessionPayloads };
1511
1769
  log.info(`Generating summary via ${config.ai.provider}\u2026`);
1512
1770
  const summary = await provider.summarize(payload);
1513
- const outputPath = opts.output ?? import_path5.default.join(EXPORTS_DIR, `workscribe-${date}.md`);
1514
- if (!import_fs9.default.existsSync(EXPORTS_DIR)) import_fs9.default.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 448 });
1515
- import_fs9.default.writeFileSync(outputPath, `# Work Summary \u2014 ${date}
1771
+ const outputPath = opts.output ?? import_path7.default.join(EXPORTS_DIR, `workscribe-${date}.md`);
1772
+ if (!import_fs11.default.existsSync(EXPORTS_DIR)) import_fs11.default.mkdirSync(EXPORTS_DIR, { recursive: true, mode: 448 });
1773
+ import_fs11.default.writeFileSync(outputPath, `# Work Summary \u2014 ${date}
1516
1774
 
1517
1775
  ${summary}
1518
1776
  `, { encoding: "utf-8", mode: 384 });
@@ -1570,22 +1828,22 @@ function printConfig(obj, indent) {
1570
1828
  }
1571
1829
 
1572
1830
  // src/cli/pause.ts
1573
- var import_fs10 = __toESM(require("fs"));
1831
+ var import_fs12 = __toESM(require("fs"));
1574
1832
  function registerPause(program2) {
1575
1833
  program2.command("pause").description("Pause capture temporarily").action(() => {
1576
- if (import_fs10.default.existsSync(PAUSE_FLAG_PATH)) {
1834
+ if (import_fs12.default.existsSync(PAUSE_FLAG_PATH)) {
1577
1835
  log.info("Capture is already paused. Run workscribe resume to restart.");
1578
1836
  return;
1579
1837
  }
1580
- import_fs10.default.writeFileSync(PAUSE_FLAG_PATH, "", { mode: 384 });
1838
+ import_fs12.default.writeFileSync(PAUSE_FLAG_PATH, "", { mode: 384 });
1581
1839
  log.success("Capture paused. Run workscribe resume to restart.");
1582
1840
  });
1583
1841
  program2.command("resume").description("Resume capture after a pause").action(() => {
1584
- if (!import_fs10.default.existsSync(PAUSE_FLAG_PATH)) {
1842
+ if (!import_fs12.default.existsSync(PAUSE_FLAG_PATH)) {
1585
1843
  log.info("Capture is not paused.");
1586
1844
  return;
1587
1845
  }
1588
- import_fs10.default.rmSync(PAUSE_FLAG_PATH);
1846
+ import_fs12.default.rmSync(PAUSE_FLAG_PATH);
1589
1847
  log.success("Capture resumed.");
1590
1848
  });
1591
1849
  }
@@ -1621,8 +1879,9 @@ function registerUndo(program2) {
1621
1879
 
1622
1880
  // src/index.ts
1623
1881
  var program = new import_commander.Command();
1624
- program.name("workscribe").description("Automatic developer work journal").version("1.2.2");
1882
+ program.name("workscribe").description("Automatic developer work journal").version("1.4.0");
1625
1883
  registerCapture(program);
1884
+ registerHook(program);
1626
1885
  registerInit(program);
1627
1886
  registerStatus(program);
1628
1887
  registerEvents(program);
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/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, formatDuration, 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 log.plain('')\n log.info(`Generating summary via ${chalk.cyan(config.ai.provider)}…`)\n\n const allSummaries: string[] = []\n for (const session of sessions) {\n const sessionPayload: DaySummaryPayload = { date, sessions: [sessionToPayload(session, date)] }\n try {\n const bullets = await provider.summarize(sessionPayload)\n const project = session.repoName ?? 'unknown'\n const branch = session.events[0]?.branch ? ` (${session.events[0].branch})` : ''\n const duration = formatDuration(session.durationMinutes)\n const heading = `## ${project}${branch} — ${duration}`\n const summary = `${heading}\\n${bullets.trim()}`\n allSummaries.push(summary)\n updateSessionSummary(session.id, summary)\n } catch (e) {\n log.error(`Summary failed [${config.ai.provider}]: ${(e as Error).message}`)\n process.exit(1)\n }\n }\n\n const fullSummary = allSummaries.join('\\n\\n')\n log.plain('')\n log.plain(fullSummary)\n log.plain('')\n\n await handleWebhook(opts, config, fullSummary)\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- 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 bullet points, 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","import_fs","import_path","eventRowToPayload","program","path","fs","import_chalk","program","chalk","import_fs","program","fs","program"]}
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/capture/git-snapshot.ts","../src/config/ai-tools.json","../src/normalize/patterns.ts","../src/config/index.ts","../src/capture/filter.ts","../src/utils/logger.ts","../src/cli/capture.ts","../src/cli/hook.ts","../src/cli/init.ts","../src/capture/shell-hook.ts","../src/capture/hooks/claude-code.ts","../src/capture/hooks/utils.ts","../src/capture/hooks/index.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'\nimport { registerHook } from './cli/hook'\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)\nregisterHook(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 { getCommitsSince } from './git-snapshot'\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 gitBefore?: string\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. For ai_session events, enrich metadata with commits made during the session\n if (category === 'ai_session' && opts.gitBefore) {\n const commits = getCommitsSince(opts.gitBefore, cwd)\n if (commits.length > 0) metadata.commits = commits\n }\n\n // 7. 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","import { execSync } from 'child_process'\n\n/**\n * Returns commit subject lines for all commits made between `sha` and HEAD.\n * Returns an empty array if sha is empty, HEAD hasn't moved, or git fails.\n */\nexport function getCommitsSince(sha: string, cwd: string): string[] {\n if (!sha) return []\n try {\n const out = execSync(`git log ${sha}..HEAD --format=%s`, { cwd, timeout: 3000 }).toString().trim()\n return out ? out.split('\\n').filter(Boolean) : []\n } catch {\n return []\n }\n}\n","[\n {\n \"tool\": \"claude\",\n \"displayName\": \"Claude Code\",\n \"pattern\": \"^claude\\\\b\",\n \"hook\": \"claude-code\",\n \"contextSource\": \"hook\"\n },\n {\n \"tool\": \"aider\",\n \"displayName\": \"Aider\",\n \"pattern\": \"^aider\\\\b\",\n \"hook\": null,\n \"contextSource\": \"history-file\"\n },\n {\n \"tool\": \"gemini\",\n \"displayName\": \"Gemini\",\n \"pattern\": \"^gemini\\\\b\",\n \"hook\": null,\n \"contextSource\": null\n },\n {\n \"tool\": \"gh copilot\",\n \"displayName\": \"GitHub Copilot\",\n \"pattern\": \"^gh\\\\s+copilot\\\\b\",\n \"hook\": null,\n \"contextSource\": null\n },\n {\n \"tool\": \"cody\",\n \"displayName\": \"Cody\",\n \"pattern\": \"^cody\\\\b\",\n \"hook\": null,\n \"contextSource\": null\n },\n {\n \"tool\": \"amazon-q\",\n \"displayName\": \"Amazon Q\",\n \"pattern\": \"^q\\\\s+(chat|dev)\\\\b\",\n \"hook\": null,\n \"contextSource\": null\n },\n {\n \"tool\": \"continue\",\n \"displayName\": \"Continue\",\n \"pattern\": \"^continue\\\\b\",\n \"hook\": null,\n \"contextSource\": null\n },\n {\n \"tool\": \"cursor\",\n \"displayName\": \"Cursor\",\n \"pattern\": \"^cursor\\\\b\",\n \"hook\": null,\n \"contextSource\": null\n }\n]\n","import AI_TOOLS from '../config/ai-tools.json'\n\nexport 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 // ─── AI terminal tools (registry-driven) ─────────────────────────────────\n ...AI_TOOLS.map((entry) =>\n makeRule(new RegExp(entry.pattern), 'ai_session', () => ({ tool: entry.tool }))\n ),\n]\n\nexport function normalizeCommand(command: string): NormalizedEvent {\n let trimmed = command.trim().replace(/\\s+/g, ' ')\n\n // Strip `git -C <path>` prefix so rules match regardless of working directory flag\n trimmed = trimmed.replace(/^(git)\\s+-C\\s+\\S+\\s+/, '$1 ')\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// Always filtered regardless of user config — internal tool noise\nconst BUILTIN_IGNORE = new Set(['workscribe'])\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 baseCmd = normalized.split(' ')[0]\n\n // Built-in filter: always excluded, not user-configurable\n if (BUILTIN_IGNORE.has(baseCmd)) return true\n\n // User-configurable ignore list\n const normalizedIgnore = ignoreList.map(normalizeForFilter)\n if (normalizedIgnore.includes(normalized)) return true\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 .option('--git-before <sha>', 'git HEAD SHA before this command ran')\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 gitBefore: opts.gitBefore ?? undefined,\n })\n } catch (err) {\n debugLog('_capture', err)\n }\n })\n}\n","import { Command } from 'commander'\nimport { captureCommand } from '../capture'\nimport { debugLog } from '../utils/logger'\n\n/** Parses a Claude Code PostToolUse stdin payload. Returns null if required fields are missing. */\nexport function parseHookPayload(raw: string): { command: string; cwd: string } | null {\n if (!raw.trim()) return null\n const payload = JSON.parse(raw) as Record<string, unknown>\n const toolInput = payload.tool_input as Record<string, unknown> | undefined\n const command = typeof toolInput?.command === 'string' ? toolInput.command : null\n const cwd = typeof payload.cwd === 'string' ? payload.cwd : null\n if (!command || !cwd) return null\n return { command, cwd }\n}\n\nexport function registerHook(program: Command): void {\n // Internal command — called by AI tool hooks (e.g. Claude Code PostToolUse), not by the user\n program\n .command('_hook')\n .description('Internal: capture events from AI tool hooks (stdin JSON payload)')\n .option('--source <source>', 'hook source identifier (e.g. claude-code)')\n .action(() => {\n let raw = ''\n process.stdin.setEncoding('utf-8')\n process.stdin.on('data', (chunk: string) => { raw += chunk })\n process.stdin.on('end', () => {\n try {\n const parsed = parseHookPayload(raw)\n if (!parsed) return\n captureCommand({ command: parsed.command, cwd: parsed.cwd, exitCode: null, durationMs: null })\n } catch (err) {\n debugLog('_hook', err)\n }\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'\nimport AI_TOOLS from '../config/ai-tools.json'\nimport { isHookInstalled, isHookOutdated, installHook, updateHook } from '../capture/shell-hook'\nimport { AI_HOOK_INTEGRATIONS } from '../capture/hooks'\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\nexport function registerInit(program: Command): void {\n program\n .command('init')\n .description('Set up Workscribe and install or update shell hooks')\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 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 and install or update hook\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 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\n if (!isHookInstalled(shell.rcFile)) {\n installHook(shell.rcFile, hookContent)\n log.success(`Shell hook installed in ${shell.rcFile}`)\n } else if (isHookOutdated(shell.rcFile, hookContent)) {\n const { update } = await prompts({\n type: 'confirm',\n name: 'update',\n message: `Shell hook in ${shell.rcFile} is outdated. Update?`,\n initial: true,\n })\n if (update) {\n updateHook(shell.rcFile, hookContent)\n log.success(`Shell hook updated in ${shell.rcFile}`)\n } else {\n log.info('Shell hook update skipped')\n }\n } else {\n log.info(`Shell hook up to date in ${shell.rcFile}`)\n }\n }\n }\n\n // 4. AI tool hook integrations (registry-driven — add new tools in src/capture/hooks/)\n for (const tool of AI_TOOLS.filter((t) => t.hook !== null)) {\n const integration = AI_HOOK_INTEGRATIONS[tool.hook!]\n if (!integration?.isInstalled()) continue\n\n if (integration.isHookInstalled()) {\n log.info(`${integration.displayName} session hook already installed`)\n } else {\n const { install } = await prompts({\n type: 'confirm',\n name: 'install',\n message: `${integration.displayName} detected. Install session capture hook?`,\n initial: true,\n })\n if (install) {\n integration.install()\n log.success(`${integration.displayName} session hook installed`)\n }\n }\n }\n\n // 5. 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 // 6. 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 fs from 'fs'\nimport path from 'path'\nimport { log } from '../utils/logger'\n\nconst HOOK_START = '# workscribe:hook:start'\nconst HOOK_END = '# workscribe:hook:end'\n\nexport function getInstalledHookBlock(rcFile: string): string | null {\n if (!fs.existsSync(rcFile)) return null\n const content = fs.readFileSync(rcFile, 'utf-8')\n const startIdx = content.indexOf(HOOK_START)\n const endIdx = content.indexOf(HOOK_END)\n if (startIdx === -1 || endIdx === -1) return null\n return content.slice(startIdx, endIdx + HOOK_END.length)\n}\n\nexport function isHookInstalled(rcFile: string): boolean {\n return getInstalledHookBlock(rcFile) !== null\n}\n\nexport function isHookOutdated(rcFile: string, hookContent: string): boolean {\n const installed = getInstalledHookBlock(rcFile)\n if (!installed) return false\n return installed.trim() !== hookContent.trim()\n}\n\nexport function installHook(rcFile: string, hookContent: string): void {\n const backup = `${rcFile}.workscribe-backup-${Date.now()}`\n fs.copyFileSync(rcFile, backup)\n log.info(`Backed up ${rcFile} → ${path.basename(backup)}`)\n fs.appendFileSync(rcFile, `\\n${hookContent}\\n`)\n}\n\nexport function updateHook(rcFile: string, hookContent: string): void {\n const backup = `${rcFile}.workscribe-backup-${Date.now()}`\n fs.copyFileSync(rcFile, backup)\n log.info(`Backed up ${rcFile} → ${path.basename(backup)}`)\n const content = fs.readFileSync(rcFile, 'utf-8')\n const startIdx = content.indexOf(HOOK_START)\n const endIdx = content.indexOf(HOOK_END) + HOOK_END.length\n fs.writeFileSync(rcFile, content.slice(0, startIdx) + hookContent + content.slice(endIdx))\n}\n","import fs from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport type { AIHookIntegration } from './types'\nimport { isCommandInstalled } from './utils'\n\nconst CLAUDE_SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json')\n\nfunction readSettings(): Record<string, unknown> {\n if (!fs.existsSync(CLAUDE_SETTINGS_PATH)) return {}\n try {\n return JSON.parse(fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf-8')) as Record<string, unknown>\n } catch {\n return {}\n }\n}\n\nexport const claudeCodeIntegration: AIHookIntegration = {\n displayName: 'Claude Code',\n\n isInstalled() {\n return isCommandInstalled('claude')\n },\n\n isHookInstalled() {\n const settings = readSettings()\n const postToolUse = ((settings.hooks as Record<string, unknown>)?.PostToolUse ?? []) as unknown[]\n return postToolUse.some((entry) => {\n const hooks = ((entry as Record<string, unknown>).hooks ?? []) as unknown[]\n return hooks.some((h) => {\n const hook = h as Record<string, unknown>\n return typeof hook.command === 'string' && hook.command.includes('workscribe _hook')\n })\n })\n },\n\n install() {\n const settings = readSettings()\n const existingHooks = (settings.hooks ?? {}) as Record<string, unknown>\n const existingPostToolUse = (existingHooks.PostToolUse ?? []) as unknown[]\n settings.hooks = {\n ...existingHooks,\n PostToolUse: [\n ...existingPostToolUse,\n {\n matcher: 'Bash',\n hooks: [{ type: 'command', command: 'workscribe _hook --source claude-code', async: true }],\n },\n ],\n }\n fs.mkdirSync(path.dirname(CLAUDE_SETTINGS_PATH), { recursive: true })\n fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2))\n },\n}\n","import { execSync } from 'child_process'\n\n/** Returns true if the given CLI command is available on PATH */\nexport function isCommandInstalled(cmd: string): boolean {\n try {\n execSync(`which ${cmd}`, { stdio: 'ignore' })\n return true\n } catch {\n return false\n }\n}\n","import type { AIHookIntegration } from './types'\nimport { claudeCodeIntegration } from './claude-code'\n\n/**\n * Registry of AI tool hook integrations, keyed by the \"hook\" field in src/config/ai-tools.json.\n * Adding support for a new tool = add a new entry here and a corresponding integration file.\n */\nexport const AI_HOOK_INTEGRATIONS: Record<string, AIHookIntegration> = {\n 'claude-code': claudeCodeIntegration,\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'\nimport AI_TOOLS from '../config/ai-tools.json'\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 ai_session: chalk.bold.magenta,\n other: chalk.gray,\n}\n\nconst AI_TOOL_DISPLAY_NAMES: Record<string, string> = Object.fromEntries(\n AI_TOOLS.map((t) => [t.tool, t.displayName])\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 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\n let cmdDisplay: string\n if (event.category === 'ai_session') {\n let meta: Record<string, unknown> = {}\n try { meta = event.metadata ? (JSON.parse(event.metadata) as Record<string, unknown>) : {} } catch { /* ignore */ }\n const toolKey = typeof meta.tool === 'string' ? meta.tool : ''\n const toolName = AI_TOOL_DISPLAY_NAMES[toolKey] ?? toolKey\n cmdDisplay = chalk.white(toolName.padEnd(20)) + chalk.magenta(' ✦')\n } else {\n cmdDisplay = chalk.dim(event.command.slice(0, 55))\n }\n\n log.plain(` ${chalk.dim(time)} ${category} ${cmdDisplay}${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, formatDuration, 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 log.plain('')\n log.info(`Generating summary via ${chalk.cyan(config.ai.provider)}…`)\n\n const allSummaries: string[] = []\n for (const session of sessions) {\n const sessionPayload: DaySummaryPayload = { date, sessions: [sessionToPayload(session, date)] }\n try {\n const bullets = await provider.summarize(sessionPayload)\n const project = session.repoName ?? 'unknown'\n const branch = session.events[0]?.branch ? ` (${session.events[0].branch})` : ''\n const duration = formatDuration(session.durationMinutes)\n const heading = `## ${project}${branch} — ${duration}`\n const summary = `${heading}\\n${bullets.trim()}`\n allSummaries.push(summary)\n updateSessionSummary(session.id, summary)\n } catch (e) {\n log.error(`Summary failed [${config.ai.provider}]: ${(e as Error).message}`)\n process.exit(1)\n }\n }\n\n const fullSummary = allSummaries.join('\\n\\n')\n log.plain('')\n log.plain(fullSummary)\n log.plain('')\n\n await handleWebhook(opts, config, fullSummary)\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- Switched to the authentication feature branch\n- Added JWT and bcrypt dependencies\n- Resolved failing auth tests\n- Committed authentication fixes\n\nFor ai_session events: describe what was built during the AI session using the tool name (e.g. \"via Claude Code\"). If a commits array is present in metadata, use those commit messages to describe what was built. Group all activity from the session into one narrative bullet. Maximum 5 bullets.\n\nNow summarise the following. Output ONLY the bullet points, 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 // environment events (source, export) are excluded — they are shell noise, not developer work\n const counts = new Map<string, number>()\n for (const event of session.events) {\n if (event.category === 'environment') continue\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 if (e.category === 'environment') continue\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_child_process","import_fs","fs","fs","import_fs","chalk","fs","program","program","import_fs","import_path","import_os","import_chalk","import_fs","import_path","fs","path","import_fs","import_path","import_os","import_child_process","path","os","fs","path","os","program","chalk","fs","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","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.2.2",
3
+ "version": "1.4.0",
4
4
  "description": "Passive developer work journal — automatic terminal activity summaries",
5
5
  "type": "commonjs",
6
6
  "bin": {
package/shell/hook.bash CHANGED
@@ -5,6 +5,7 @@ _workscribe_preexec() {
5
5
  if [ "$BASH_COMMAND" != "_workscribe_precmd" ]; then
6
6
  _workscribe_cmd_start=$(date +%s%3N 2>/dev/null || echo 0)
7
7
  _workscribe_last_cmd="$BASH_COMMAND"
8
+ _workscribe_git_before=$(git rev-parse HEAD 2>/dev/null || echo "")
8
9
  fi
9
10
  }
10
11
 
@@ -19,8 +20,10 @@ _workscribe_precmd() {
19
20
  --cwd "$PWD" \
20
21
  --exit "$exit_code" \
21
22
  --duration "$duration" \
23
+ ${_workscribe_git_before:+--git-before "$_workscribe_git_before"} \
22
24
  2>/dev/null &
23
25
  _workscribe_last_cmd=""
26
+ _workscribe_git_before=""
24
27
  fi
25
28
  }
26
29
 
package/shell/hook.zsh CHANGED
@@ -4,6 +4,7 @@
4
4
  _workscribe_preexec() {
5
5
  _workscribe_cmd_start=$EPOCHREALTIME
6
6
  _workscribe_cmd="$1"
7
+ _workscribe_git_before=$(git rev-parse HEAD 2>/dev/null || echo "")
7
8
  }
8
9
 
9
10
  _workscribe_precmd() {
@@ -15,8 +16,10 @@ _workscribe_precmd() {
15
16
  --cwd "$PWD" \
16
17
  --exit "$exit_code" \
17
18
  --duration "$duration_ms" \
19
+ ${_workscribe_git_before:+--git-before "$_workscribe_git_before"} \
18
20
  2>/dev/null &!
19
21
  _workscribe_cmd=""
22
+ _workscribe_git_before=""
20
23
  fi
21
24
  }
22
25