@workscribe/cli 0.2.1 → 1.0.1
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/LICENSE +21 -0
- package/README.md +29 -28
- package/dist/index.js +24 -24
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/shell/hook.bash +16 -16
- package/shell/hook.zsh +15 -15
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mark Edomwande
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Workscribe
|
|
2
2
|
|
|
3
3
|
> Your work, automatically documented.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Workscribe is a CLI tool that passively captures your terminal activity and transforms it into readable daily work summaries — without you having to write anything.
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
-
$
|
|
8
|
+
$ workscribe summary
|
|
9
9
|
|
|
10
10
|
## api-service (feature/auth) — 2h 15m
|
|
11
11
|
|
|
@@ -21,7 +21,7 @@ $ chronicle summary
|
|
|
21
21
|
|
|
22
22
|
## How it works
|
|
23
23
|
|
|
24
|
-
A shell hook captures every command you run. At the end of the day, `
|
|
24
|
+
A shell hook captures every command you run. At the end of the day, `workscribe summary` groups your activity into work sessions and sends structured event data (never raw commands) to your configured AI provider, which returns a standup-ready narrative.
|
|
25
25
|
|
|
26
26
|
```
|
|
27
27
|
shell hook → capture → SQLite → session grouper → AI provider → markdown
|
|
@@ -31,17 +31,17 @@ shell hook → capture → SQLite → session grouper → AI provider → markdo
|
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
npm install -g @workscribe/cli
|
|
34
|
-
|
|
34
|
+
workscribe init
|
|
35
35
|
source ~/.zshrc # or restart your terminal
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
`
|
|
38
|
+
`workscribe init` walks you through choosing an AI provider and installs the shell hook. From that point, capture is automatic.
|
|
39
39
|
|
|
40
40
|
**Requirements:** Node.js 22+, macOS or Linux (zsh or bash)
|
|
41
41
|
|
|
42
42
|
## AI providers
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Workscribe works with any of these — switch at any time via config:
|
|
45
45
|
|
|
46
46
|
| Provider | Config value | Requires |
|
|
47
47
|
|---|---|---|
|
|
@@ -51,48 +51,49 @@ Chronicle works with any of these — switch at any time via config:
|
|
|
51
51
|
| OpenAI-compatible | `openai-compatible` | API key + base URL (Groq, Together, Mistral…) |
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
workscribe config set ai.provider ollama # switch to local
|
|
55
|
+
workscribe config set ai.provider anthropic # switch to Anthropic
|
|
56
|
+
workscribe config set ai.apiKey <your-key>
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
## Commands
|
|
60
60
|
|
|
61
61
|
| Command | Description |
|
|
62
62
|
|---|---|
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
63
|
+
| `workscribe summary` | Generate today's work summary |
|
|
64
|
+
| `workscribe summary --date 2026-05-20` | Summary for a specific date |
|
|
65
|
+
| `workscribe summary --week` | Summary for the current week |
|
|
66
|
+
| `workscribe sessions` | List detected work sessions |
|
|
67
|
+
| `workscribe events` | List captured events |
|
|
68
|
+
| `workscribe export` | Save today's summary as a markdown file |
|
|
69
|
+
| `workscribe status` | Check capture is active and show event counts |
|
|
70
|
+
| `workscribe config` | View current configuration |
|
|
71
|
+
| `workscribe config set KEY VALUE` | Update a config value |
|
|
72
72
|
|
|
73
73
|
## Privacy
|
|
74
74
|
|
|
75
|
-
- All captured data is stored locally in `~/.
|
|
75
|
+
- All captured data is stored locally in `~/.workscribe/` — nothing is written anywhere else
|
|
76
76
|
- Credentials are redacted before any event is written to disk (tokens, API keys, passwords, JWTs)
|
|
77
|
-
- Nothing is sent to any remote server except your configured AI provider
|
|
78
|
-
-
|
|
77
|
+
- Nothing is sent to any remote server except your configured AI provider, only when you run `workscribe summary`
|
|
78
|
+
- The AI provider receives structured event categories (e.g. `test_run`, `code_commit`) — never raw commands, file contents, or file paths
|
|
79
|
+
- Use Ollama to keep everything fully on-device with zero external data transmission
|
|
79
80
|
|
|
80
81
|
## Configuration
|
|
81
82
|
|
|
82
83
|
```bash
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
workscribe config set session.idleTimeout 20 # minutes before a new session starts (default: 30)
|
|
85
|
+
workscribe config set ai.model gpt-4o # override the default model for your provider
|
|
86
|
+
workscribe config set redact.extra "MY_PATTERN" # add a custom redaction regex
|
|
86
87
|
```
|
|
87
88
|
|
|
88
|
-
Config is stored at `~/.
|
|
89
|
+
Config is stored at `~/.workscribe/config.json`.
|
|
89
90
|
|
|
90
91
|
## Debug mode
|
|
91
92
|
|
|
92
|
-
If capture isn't working, set `
|
|
93
|
+
If capture isn't working, set `WORKSCRIBE_DEBUG=1` to write errors to `~/.workscribe/debug.log`:
|
|
93
94
|
|
|
94
95
|
```bash
|
|
95
|
-
|
|
96
|
+
WORKSCRIBE_DEBUG=1 workscribe status
|
|
96
97
|
```
|
|
97
98
|
|
|
98
99
|
## License
|
package/dist/index.js
CHANGED
|
@@ -33,18 +33,18 @@ var import_fs = __toESM(require("fs"));
|
|
|
33
33
|
// src/utils/paths.ts
|
|
34
34
|
var import_path = __toESM(require("path"));
|
|
35
35
|
var import_os = __toESM(require("os"));
|
|
36
|
-
var
|
|
37
|
-
var DB_PATH = import_path.default.join(
|
|
38
|
-
var CONFIG_PATH = import_path.default.join(
|
|
39
|
-
var DEBUG_LOG_PATH = import_path.default.join(
|
|
40
|
-
var EXPORTS_DIR = import_path.default.join(
|
|
36
|
+
var WORKSCRIBE_DIR = import_path.default.join(import_os.default.homedir(), ".workscribe");
|
|
37
|
+
var DB_PATH = import_path.default.join(WORKSCRIBE_DIR, "workscribe.db");
|
|
38
|
+
var CONFIG_PATH = import_path.default.join(WORKSCRIBE_DIR, "config.json");
|
|
39
|
+
var DEBUG_LOG_PATH = import_path.default.join(WORKSCRIBE_DIR, "debug.log");
|
|
40
|
+
var EXPORTS_DIR = import_path.default.join(WORKSCRIBE_DIR, "exports");
|
|
41
41
|
|
|
42
42
|
// src/db/index.ts
|
|
43
43
|
var _db = null;
|
|
44
44
|
function getDb() {
|
|
45
45
|
if (_db) return _db;
|
|
46
|
-
if (!import_fs.default.existsSync(
|
|
47
|
-
import_fs.default.mkdirSync(
|
|
46
|
+
if (!import_fs.default.existsSync(WORKSCRIBE_DIR)) {
|
|
47
|
+
import_fs.default.mkdirSync(WORKSCRIBE_DIR, { recursive: true });
|
|
48
48
|
}
|
|
49
49
|
_db = new import_node_sqlite.DatabaseSync(DB_PATH);
|
|
50
50
|
_db.exec("PRAGMA journal_mode = WAL");
|
|
@@ -490,7 +490,7 @@ function captureCommand(opts) {
|
|
|
490
490
|
// src/utils/logger.ts
|
|
491
491
|
var import_chalk = __toESM(require("chalk"));
|
|
492
492
|
var import_fs3 = __toESM(require("fs"));
|
|
493
|
-
var isDebug = process.env.
|
|
493
|
+
var isDebug = process.env.WORKSCRIBE_DEBUG === "1";
|
|
494
494
|
var log = {
|
|
495
495
|
info: (msg) => console.log(import_chalk.default.cyan("\u2192"), msg),
|
|
496
496
|
success: (msg) => console.log(import_chalk.default.green("\u2713"), msg),
|
|
@@ -531,7 +531,7 @@ var import_path3 = __toESM(require("path"));
|
|
|
531
531
|
var import_os2 = __toESM(require("os"));
|
|
532
532
|
var import_prompts = __toESM(require("prompts"));
|
|
533
533
|
var import_chalk2 = __toESM(require("chalk"));
|
|
534
|
-
var HOOK_START = "#
|
|
534
|
+
var HOOK_START = "# workscribe:hook:start";
|
|
535
535
|
function detectShell() {
|
|
536
536
|
const shell = process.env.SHELL ?? "";
|
|
537
537
|
if (shell.includes("zsh")) return { name: "zsh", rcFile: import_path3.default.join(import_os2.default.homedir(), ".zshrc") };
|
|
@@ -543,7 +543,7 @@ function isHookInstalled(rcFile) {
|
|
|
543
543
|
return import_fs4.default.readFileSync(rcFile, "utf-8").includes(HOOK_START);
|
|
544
544
|
}
|
|
545
545
|
function installHook(rcFile, hookContent) {
|
|
546
|
-
const backup = `${rcFile}.
|
|
546
|
+
const backup = `${rcFile}.workscribe-backup-${Date.now()}`;
|
|
547
547
|
import_fs4.default.copyFileSync(rcFile, backup);
|
|
548
548
|
log.info(`Backed up ${rcFile} \u2192 ${import_path3.default.basename(backup)}`);
|
|
549
549
|
import_fs4.default.appendFileSync(rcFile, `
|
|
@@ -551,11 +551,11 @@ ${hookContent}
|
|
|
551
551
|
`);
|
|
552
552
|
}
|
|
553
553
|
function registerInit(program2) {
|
|
554
|
-
program2.command("init").description("Install shell hooks and set up
|
|
555
|
-
log.plain(import_chalk2.default.bold("\n
|
|
556
|
-
if (!import_fs4.default.existsSync(
|
|
557
|
-
import_fs4.default.mkdirSync(
|
|
558
|
-
log.success(`Created ${
|
|
554
|
+
program2.command("init").description("Install shell hooks and set up Workscribe").action(async () => {
|
|
555
|
+
log.plain(import_chalk2.default.bold("\n Workscribe \u2014 Developer Work Journal\n"));
|
|
556
|
+
if (!import_fs4.default.existsSync(WORKSCRIBE_DIR)) {
|
|
557
|
+
import_fs4.default.mkdirSync(WORKSCRIBE_DIR, { recursive: true });
|
|
558
|
+
log.success(`Created ${WORKSCRIBE_DIR}`);
|
|
559
559
|
}
|
|
560
560
|
getDb();
|
|
561
561
|
log.success("Database initialized");
|
|
@@ -600,7 +600,7 @@ function registerInit(program2) {
|
|
|
600
600
|
if (provider === "ollama") {
|
|
601
601
|
log.plain(`
|
|
602
602
|
${import_chalk2.default.dim("Make sure Ollama is running: ollama serve")}`);
|
|
603
|
-
log.plain(` ${import_chalk2.default.dim("Default model: llama3 (change with:
|
|
603
|
+
log.plain(` ${import_chalk2.default.dim("Default model: llama3 (change with: workscribe config set ai.model <model>)")}
|
|
604
604
|
`);
|
|
605
605
|
} else {
|
|
606
606
|
const { apiKey } = await (0, import_prompts.default)({
|
|
@@ -621,7 +621,7 @@ function registerInit(program2) {
|
|
|
621
621
|
writeConfig(config);
|
|
622
622
|
log.success(`AI provider set to ${import_chalk2.default.cyan(provider)}`);
|
|
623
623
|
} else if (provider === "skip" || !provider) {
|
|
624
|
-
log.info("Skipped \u2014 run later:
|
|
624
|
+
log.info("Skipped \u2014 run later: workscribe config set ai.provider <provider>");
|
|
625
625
|
}
|
|
626
626
|
}
|
|
627
627
|
if (!import_fs4.default.existsSync(CONFIG_PATH)) {
|
|
@@ -636,8 +636,8 @@ function registerInit(program2) {
|
|
|
636
636
|
log.plain(` Or restart your terminal.`);
|
|
637
637
|
}
|
|
638
638
|
log.plain("");
|
|
639
|
-
log.plain(` ${import_chalk2.default.dim("
|
|
640
|
-
log.plain(` ${import_chalk2.default.dim("
|
|
639
|
+
log.plain(` ${import_chalk2.default.dim("workscribe summary")} \u2014 generate today's work summary`);
|
|
640
|
+
log.plain(` ${import_chalk2.default.dim("workscribe status")} \u2014 check capture is working`);
|
|
641
641
|
log.plain("");
|
|
642
642
|
});
|
|
643
643
|
}
|
|
@@ -672,7 +672,7 @@ function registerStatus(program2) {
|
|
|
672
672
|
const todayEvents = getEventsForDate(todayDate());
|
|
673
673
|
const last = getLastEvent();
|
|
674
674
|
log.plain("");
|
|
675
|
-
log.plain(import_chalk3.default.bold("
|
|
675
|
+
log.plain(import_chalk3.default.bold(" Workscribe Status"));
|
|
676
676
|
log.plain("");
|
|
677
677
|
log.plain(` Capture ${import_chalk3.default.green("active")}`);
|
|
678
678
|
log.plain(` Events today ${import_chalk3.default.cyan(String(todayEvents.length))}`);
|
|
@@ -1182,15 +1182,15 @@ ${summary}
|
|
|
1182
1182
|
// src/cli/config.ts
|
|
1183
1183
|
var import_chalk7 = __toESM(require("chalk"));
|
|
1184
1184
|
function registerConfig(program2) {
|
|
1185
|
-
const configCmd = program2.command("config").description("View or update
|
|
1185
|
+
const configCmd = program2.command("config").description("View or update Workscribe configuration").action(() => {
|
|
1186
1186
|
const config = readConfig();
|
|
1187
1187
|
log.plain("");
|
|
1188
|
-
log.plain(import_chalk7.default.bold("
|
|
1188
|
+
log.plain(import_chalk7.default.bold(" Workscribe Config"));
|
|
1189
1189
|
log.plain("");
|
|
1190
1190
|
printConfig(config, " ");
|
|
1191
1191
|
log.plain("");
|
|
1192
1192
|
});
|
|
1193
|
-
configCmd.command("set <key> <value>").description("Set a config value (e.g.
|
|
1193
|
+
configCmd.command("set <key> <value>").description("Set a config value (e.g. workscribe config set ai.provider anthropic)").action((key, value) => {
|
|
1194
1194
|
const config = readConfig();
|
|
1195
1195
|
const updated = setConfigValue(config, key, value);
|
|
1196
1196
|
writeConfig(updated);
|
|
@@ -1221,7 +1221,7 @@ function printConfig(obj, indent) {
|
|
|
1221
1221
|
|
|
1222
1222
|
// src/index.ts
|
|
1223
1223
|
var program = new import_commander.Command();
|
|
1224
|
-
program.name("
|
|
1224
|
+
program.name("workscribe").description("Automatic developer work journal").version("0.2.1");
|
|
1225
1225
|
registerCapture(program);
|
|
1226
1226
|
registerInit(program);
|
|
1227
1227
|
registerStatus(program);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/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/capture/index.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/providers/anthropic.ts","../src/ai/providers/openai.ts","../src/ai/providers/ollama.ts","../src/ai/router.ts","../src/cli/export.ts","../src/cli/config.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { registerCapture } from './cli/capture'\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'\n\nconst program = new Command()\n\nprogram\n .name('chronicle')\n .description('Automatic developer work journal')\n .version('0.1.0')\n\nregisterCapture(program)\nregisterInit(program)\nregisterStatus(program)\nregisterEvents(program)\nregisterSessions(program)\nregisterSummary(program)\nregisterExport(program)\nregisterConfig(program)\n\nprogram.parse()\n","import { DatabaseSync } from 'node:sqlite'\nimport fs from 'fs'\nimport { CHRONICLE_DIR, DB_PATH } from '../utils/paths'\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 ~/.chronicle/ exists\n if (!fs.existsSync(CHRONICLE_DIR)) {\n fs.mkdirSync(CHRONICLE_DIR, { recursive: true })\n }\n\n _db = new DatabaseSync(DB_PATH)\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 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 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 SessionRow[]\n}\n\nexport function updateSessionSummary(sessionId: number, summary: string): void {\n const db = getDb()\n const stmt = db.prepare('UPDATE sessions SET summary = $summary WHERE id = $id')\n stmt.run({ summary, id: sessionId })\n}\n\nexport function clearSessionsForDate(date: string): void {\n const db = getDb()\n // Remove session_events first due to FK constraint\n db.exec(`\n DELETE FROM session_events WHERE session_id IN (\n SELECT id FROM sessions WHERE date = '${date.replace(/'/g, \"''\")}'\n )\n `)\n db.exec(`DELETE FROM sessions WHERE date = '${date.replace(/'/g, \"''\")}'`)\n}\n","import path from 'path'\nimport os from 'os'\n\nexport const CHRONICLE_DIR = path.join(os.homedir(), '.chronicle')\nexport const DB_PATH = path.join(CHRONICLE_DIR, 'chronicle.db')\nexport const CONFIG_PATH = path.join(CHRONICLE_DIR, 'config.json')\nexport const DEBUG_LOG_PATH = path.join(CHRONICLE_DIR, 'debug.log')\nexport const EXPORTS_DIR = path.join(CHRONICLE_DIR, 'exports')\n","// Patterns that match known sensitive values\nconst BUILTIN_PATTERNS: RegExp[] = [\n // AWS access keys\n /AKIA[0-9A-Z]{16}/g,\n // AWS secret access keys (40 char base64-ish after common env vars)\n /(?<=AWS_SECRET_ACCESS_KEY[=\\s]+)\\S+/gi,\n // GitHub tokens\n /ghp_[a-zA-Z0-9]{36}/g,\n /github_pat_[a-zA-Z0-9_]{82}/g,\n // GitLab tokens\n /glpat-[a-zA-Z0-9\\-]{20}/g,\n // Generic JWTs (three base64url segments)\n /eyJ[a-zA-Z0-9_-]+\\.eyJ[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_-]+/g,\n // Slack tokens\n /xox[baprs]-[a-zA-Z0-9\\-]+/g,\n // Stripe keys\n /sk_(live|test)_[a-zA-Z0-9]{24,}/g,\n // Values after long-form secret flags (single-char flags like -t/-p/-k are too ambiguous)\n /(?<=--(password|passwd|secret|token|key|api[-_]?key|auth[-_]?token)\\s+)\\S+/gi,\n // export VAR=VALUE where VAR looks sensitive\n /(?<=export\\s+(?:[A-Z_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL)[A-Z_]*)=)\\S+/gi,\n]\n\nexport function redact(command: string, extraPatterns: string[] = []): string {\n let result = command\n\n const allPatterns = [...BUILTIN_PATTERNS]\n\n for (const pattern of extraPatterns) {\n try {\n allPatterns.push(new RegExp(pattern, 'g'))\n } catch {\n // Invalid regex — skip silently\n }\n }\n\n for (const pattern of allPatterns) {\n // Reset lastIndex for global regexes\n pattern.lastIndex = 0\n result = result.replace(pattern, '[REDACTED]')\n }\n\n return result\n}\n","import { execSync } from 'child_process'\nimport path from 'path'\n\nexport interface GitContext {\n repoName: string | null\n branch: string | null\n}\n\nexport function getGitContext(cwd: string): GitContext {\n try {\n const root = execSync('git rev-parse --show-toplevel', {\n cwd,\n timeout: 2000,\n stdio: 'pipe',\n })\n .toString()\n .trim()\n\n const repoName = path.basename(root)\n\n let branch: string | null = null\n try {\n branch = execSync('git branch --show-current', {\n cwd,\n timeout: 2000,\n stdio: 'pipe',\n })\n .toString()\n .trim() || null\n } catch {\n // Detached HEAD or not a git repo\n }\n\n return { repoName, branch }\n } catch {\n return { repoName: null, branch: null }\n }\n}\n","export interface NormalizedEvent {\n category: string\n metadata: Record<string, unknown>\n}\n\ninterface Rule {\n // Returns metadata if command matches, null otherwise\n match(cmd: string): NormalizedEvent | null\n}\n\nfunction makeRule(\n pattern: RegExp,\n category: string,\n extract: (match: RegExpMatchArray, cmd: string) => Record<string, unknown> = () => ({})\n): Rule {\n return {\n match(cmd) {\n const m = cmd.match(pattern)\n if (!m) return null\n return { category, metadata: extract(m, cmd) }\n },\n }\n}\n\n// Extract package names from install commands like: npm install foo bar\nfunction extractPackages(cmd: string, afterKeyword: string): string[] {\n const idx = cmd.indexOf(afterKeyword)\n if (idx === -1) return []\n return cmd\n .slice(idx + afterKeyword.length)\n .trim()\n .split(/\\s+/)\n .filter((p) => p && !p.startsWith('-'))\n}\n\nexport const RULES: Rule[] = [\n // ─── Git operations ───────────────────────────────────────────────────────\n makeRule(/^git\\s+commit\\b/, 'code_commit', (_m, cmd) => {\n const msgMatch = cmd.match(/-m\\s+[\"']?([^\"']+)[\"']?/)\n return { message: msgMatch ? msgMatch[1].trim() : null }\n }),\n\n makeRule(/^git\\s+push\\b/, 'code_push', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { remote: parts[2] ?? 'origin', branch: parts[3] ?? null }\n }),\n\n makeRule(/^git\\s+(pull|fetch)\\b/, 'code_pull', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], remote: parts[2] ?? 'origin' }\n }),\n\n makeRule(/^git\\s+(checkout|switch)\\b/, 'branch_switch', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n const branch = parts.find((p) => !p.startsWith('-') && p !== 'git' && p !== parts[1])\n return { branch: branch ?? null }\n }),\n\n makeRule(/^git\\s+(merge|rebase)\\b/, 'code_merge', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], target: parts[2] ?? null }\n }),\n\n makeRule(/^git\\s+stash\\b/, 'code_stash', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[2] ?? 'push' }\n }),\n\n makeRule(/^git\\s+/, 'git_other', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1] ?? null }\n }),\n\n // ─── Package managers — install ───────────────────────────────────────────\n makeRule(/^npm\\s+(install|i|add)\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'npm',\n packages: extractPackages(cmd, cmd.includes('install') ? 'install' : cmd.includes(' i ') ? ' i ' : 'add'),\n })),\n\n makeRule(/^(yarn|pnpm)\\s+(install|add)\\b/, 'dependency_install', (m, cmd) => ({\n tool: m[1],\n packages: extractPackages(cmd, 'add'),\n })),\n\n makeRule(/^pip\\s+(install)\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'pip',\n packages: extractPackages(cmd, 'install'),\n })),\n\n makeRule(/^cargo\\s+add\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'cargo',\n packages: extractPackages(cmd, 'add'),\n })),\n\n makeRule(/^(brew)\\s+install\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'brew',\n packages: extractPackages(cmd, 'install'),\n })),\n\n makeRule(/^go\\s+get\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'go',\n packages: extractPackages(cmd, 'get'),\n })),\n\n // ─── Package managers — remove ────────────────────────────────────────────\n makeRule(/^npm\\s+(uninstall|remove|rm)\\b/, 'dependency_remove', (_m, cmd) => ({\n tool: 'npm',\n packages: extractPackages(cmd, cmd.split(/\\s+/)[1]),\n })),\n\n makeRule(/^(yarn|pnpm)\\s+remove\\b/, 'dependency_remove', (m, cmd) => ({\n tool: m[1],\n packages: extractPackages(cmd, 'remove'),\n })),\n\n makeRule(/^pip\\s+uninstall\\b/, 'dependency_remove', (_m, cmd) => ({\n tool: 'pip',\n packages: extractPackages(cmd, 'uninstall'),\n })),\n\n // ─── Tests ────────────────────────────────────────────────────────────────\n makeRule(/^(npm\\s+(test|run\\s+test)|yarn\\s+test|pnpm\\s+test)\\b/, 'test_run', () => ({ tool: 'npm' })),\n makeRule(/^(jest|vitest)\\b/, 'test_run', (m) => ({ tool: m[1] })),\n makeRule(/^pytest\\b/, 'test_run', () => ({ tool: 'pytest' })),\n makeRule(/^go\\s+test\\b/, 'test_run', () => ({ tool: 'go' })),\n makeRule(/^cargo\\s+test\\b/, 'test_run', () => ({ tool: 'cargo' })),\n makeRule(/^rspec\\b/, 'test_run', () => ({ tool: 'rspec' })),\n makeRule(/^(php\\s+)?phpunit\\b/, 'test_run', () => ({ tool: 'phpunit' })),\n\n // ─── Build ────────────────────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+build\\b/, 'build', () => ({ tool: 'npm' })),\n makeRule(/^(yarn|pnpm)\\s+build\\b/, 'build', (m) => ({ tool: m[1] })),\n makeRule(/^(make|gmake)\\b/, 'build', (m) => ({ tool: m[1] })),\n makeRule(/^cargo\\s+build\\b/, 'build', () => ({ tool: 'cargo' })),\n makeRule(/^go\\s+build\\b/, 'build', () => ({ tool: 'go' })),\n makeRule(/^docker\\s+build\\b/, 'build', () => ({ tool: 'docker' })),\n makeRule(/^(mvn|gradle)\\b/, 'build', (m) => ({ tool: m[1] })),\n\n // ─── Dev server ───────────────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+(dev|start|serve)\\b/, 'server_run', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n makeRule(/^(yarn|pnpm)\\s+(dev|start)\\b/, 'server_run', (m) => ({ tool: m[1], script: m[2] })),\n makeRule(/^python\\s+.*manage\\.py\\s+runserver\\b/, 'server_run', () => ({ tool: 'django' })),\n\n // ─── Infrastructure ───────────────────────────────────────────────────────\n makeRule(/^ssh\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'ssh', target: parts[parts.length - 1] }\n }),\n\n makeRule(/^kubectl\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'kubectl', subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^(helm|terraform|pulumi)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^docker\\s+(run|compose|ps|stop|start|rm|exec)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'docker', subcommand: parts[1] }\n }),\n\n makeRule(/^(aws|gcloud|az)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n // ─── Scripts ──────────────────────────────────────────────────────────────\n makeRule(/^(bash|sh|zsh|fish)\\s+\\S+/, 'script_run', (m) => ({ shell: m[1] })),\n makeRule(/^node\\s+\\S+\\.(?:js|mjs|cjs)\\b/, 'script_run', () => ({ runtime: 'node' })),\n makeRule(/^python3?\\s+\\S+\\.py\\b/, 'script_run', () => ({ runtime: 'python' })),\n\n // ─── Environment ──────────────────────────────────────────────────────────\n makeRule(/^export\\s+\\w+=/, 'environment', (_m, cmd) => {\n const varName = cmd.match(/^export\\s+(\\w+)=/)?.[1] ?? null\n return { variable: varName }\n }),\n makeRule(/^source\\s+/, 'environment', () => ({})),\n\n // ─── npm scripts (generic) ────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+\\S+/, 'script_run', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n]\n\nexport function normalizeCommand(command: string): NormalizedEvent {\n const trimmed = command.trim().replace(/\\s+/g, ' ')\n\n for (const rule of RULES) {\n const result = rule.match(trimmed)\n if (result) return result\n }\n\n return { category: 'other', metadata: {} }\n}\n","import fs from 'fs'\nimport { CONFIG_PATH } from '../utils/paths'\n\nexport interface ChronicleConfig {\n ai: {\n provider: string // 'anthropic' | 'openai' | 'ollama' | 'openai-compatible'\n apiKey: string\n model: string\n baseUrl: string\n }\n session: {\n idleTimeout: number // minutes\n }\n capture: {\n ignore: string[]\n }\n redact: {\n extra: string[]\n }\n summary: {\n format: 'markdown' | 'json'\n }\n}\n\nconst DEFAULTS: ChronicleConfig = {\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(): ChronicleConfig {\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: ChronicleConfig): void {\n fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8')\n}\n\nexport function getConfigValue(config: ChronicleConfig, 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: ChronicleConfig, key: string, value: string): ChronicleConfig {\n const parts = key.split('.')\n const result = JSON.parse(JSON.stringify(config)) as ChronicleConfig\n let cur = result 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 { 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'\n\nexport function captureCommand(opts: {\n command: string\n cwd: string\n exitCode: number | null\n durationMs: number | null\n}): void {\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(normalized, config.redact.extra)\n\n // 4. Extract git context\n const { repoName, branch } = getGitContext(cwd)\n\n // 5. Normalize to typed event\n const { category, metadata } = normalizeCommand(safeCommand)\n\n // 6. Persist\n insertEvent({\n captured_at: new Date().toISOString(),\n command: safeCommand,\n cwd,\n exit_code: exitCode,\n duration_ms: durationMs,\n repo_name: repoName,\n branch,\n category,\n metadata: Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null,\n })\n}\n","import chalk from 'chalk'\nimport fs from 'fs'\nimport { DEBUG_LOG_PATH } from './paths'\n\nconst isDebug = process.env.CHRONICLE_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)\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 { CHRONICLE_DIR, CONFIG_PATH } from '../utils/paths'\nimport { readConfig, writeConfig } from '../config'\nimport { getDb } from '../db'\nimport { log } from '../utils/logger'\n\nconst HOOK_START = '# chronicle:hook:start'\nconst HOOK_END = '# chronicle: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}.chronicle-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 Chronicle')\n .action(async () => {\n log.plain(chalk.bold('\\n Chronicle — Developer Work Journal\\n'))\n\n // 1. Create ~/.chronicle/\n if (!fs.existsSync(CHRONICLE_DIR)) {\n fs.mkdirSync(CHRONICLE_DIR, { recursive: true })\n log.success(`Created ${CHRONICLE_DIR}`)\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: chronicle 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: chronicle 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('chronicle summary')} — generate today\\'s work summary`)\n log.plain(` ${chalk.dim('chronicle status')} — check capture is working`)\n log.plain('')\n })\n}\n","import { Command } from 'commander'\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'\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\n log.plain('')\n log.plain(chalk.bold(' Chronicle Status'))\n log.plain('')\n log.plain(` Capture ${chalk.green('active')}`)\n log.plain(` Events today ${chalk.cyan(String(todayEvents.length))}`)\n log.plain(` Events total ${chalk.cyan(String(total))}`)\n\n if (last) {\n log.plain(` Last event ${formatDate(last.captured_at)} ${chalk.dim(last.command.slice(0, 60))}`)\n }\n\n log.plain('')\n log.plain(` AI provider ${config.ai.provider ? chalk.cyan(config.ai.provider) : chalk.yellow('not configured')}`)\n if (config.ai.model) log.plain(` Model ${chalk.dim(config.ai.model)}`)\n log.plain('')\n })\n}\n","export function formatDuration(minutes: number): string {\n if (minutes < 60) return `${Math.round(minutes)}m`\n const h = Math.floor(minutes / 60)\n const m = Math.round(minutes % 60)\n return m > 0 ? `${h}h ${m}m` : `${h}h`\n}\n\nexport function formatDate(isoString: string): string {\n return new Date(isoString).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\nexport function todayDate(): string {\n return new Date().toISOString().split('T')[0]\n}\n\nexport function parseDate(input: string): string {\n // Accepts YYYY-MM-DD, returns YYYY-MM-DD. Throws on invalid.\n const d = new Date(input + 'T00:00:00')\n if (isNaN(d.getTime())) throw new Error(`Invalid date: ${input}`)\n return input\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getEventsForDate } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\n\nconst CATEGORY_COLORS: Record<string, (s: string) => string> = {\n code_commit: chalk.green,\n code_push: chalk.green,\n code_pull: chalk.blue,\n branch_switch: chalk.blue,\n dependency_install: chalk.yellow,\n dependency_remove: chalk.yellow,\n test_run: chalk.magenta,\n build: chalk.cyan,\n server_run: chalk.cyan,\n infrastructure: chalk.red,\n script_run: chalk.white,\n environment: chalk.gray,\n git_other: chalk.blue,\n code_merge: chalk.green,\n code_stash: chalk.blue,\n other: chalk.gray,\n}\n\nexport function registerEvents(program: Command): void {\n program\n .command('events')\n .description('List captured events')\n .option('--date <YYYY-MM-DD>', 'date to list events for (default: today)')\n .option('--limit <n>', 'max events to show', parseInt)\n .action((opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const events = getEventsForDate(date)\n const limit = opts.limit ?? events.length\n\n if (events.length === 0) {\n log.plain(`No events captured for ${date}`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Events for ${date} (${events.length} total)`))\n log.plain('')\n\n for (const event of events.slice(0, limit)) {\n const colorFn = CATEGORY_COLORS[event.category] ?? chalk.gray\n const time = formatDate(event.captured_at)\n const category = colorFn(event.category.padEnd(20))\n const cmd = chalk.dim(event.command.slice(0, 55))\n const repo = event.repo_name ? chalk.dim(` [${event.repo_name}]`) : ''\n const exitBadge = event.exit_code && event.exit_code !== 0 ? chalk.red(` ✗${event.exit_code}`) : ''\n log.plain(` ${chalk.dim(time)} ${category} ${cmd}${repo}${exitBadge}`)\n }\n\n if (limit < events.length) {\n log.plain('')\n log.plain(chalk.dim(` … and ${events.length - limit} more (remove --limit to see all)`))\n }\n\n log.plain('')\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate, formatDuration } from '../utils/format'\n\nexport function registerSessions(program: Command): void {\n program\n .command('sessions')\n .description('List detected work sessions')\n .option('--date <YYYY-MM-DD>', 'date to show sessions for (default: today)')\n .action((opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const sessions = getOrBuildSessions(date)\n\n if (sessions.length === 0) {\n log.plain(`No sessions detected for ${date}`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Sessions for ${date} (${sessions.length} session${sessions.length > 1 ? 's' : ''})`))\n log.plain('')\n\n for (const session of sessions) {\n const start = formatDate(session.startedAt)\n const end = formatDate(session.endedAt)\n const duration = formatDuration(session.durationMinutes)\n const repo = session.repoName ? chalk.cyan(session.repoName) : chalk.gray('no repo')\n log.plain(` ${chalk.dim(start)} – ${chalk.dim(end)} ${repo} ${chalk.dim(duration)} ${chalk.dim(session.eventCount + ' events')}`)\n }\n\n log.plain('')\n })\n}\n","import type { EventRow } from '../db/types'\n\n/**\n * Split a sorted list of events into groups separated by idle gaps.\n */\nexport function splitByIdleGap(events: EventRow[], idleMs: number): EventRow[][] {\n if (events.length === 0) return []\n const groups: EventRow[][] = [[events[0]]]\n\n for (let i = 1; i < events.length; i++) {\n const prev = new Date(events[i - 1].captured_at).getTime()\n const curr = new Date(events[i].captured_at).getTime()\n if (curr - prev >= idleMs) {\n groups.push([events[i]])\n } else {\n groups[groups.length - 1].push(events[i])\n }\n }\n\n return groups\n}\n\n/**\n * Split a list of events into groups by consecutive repo_name.\n * A repo change mid-stream creates a new group.\n */\nexport function splitByRepo(events: EventRow[]): EventRow[][] {\n if (events.length === 0) return []\n const groups: EventRow[][] = [[events[0]]]\n\n for (let i = 1; i < events.length; i++) {\n const prevRepo = events[i - 1].repo_name\n const currRepo = events[i].repo_name\n if (currRepo !== prevRepo) {\n groups.push([events[i]])\n } else {\n groups[groups.length - 1].push(events[i])\n }\n }\n\n return groups\n}\n\nexport function minutesBetween(start: string, end: string): number {\n return (new Date(end).getTime() - new Date(start).getTime()) / 60000\n}\n","import type { EventRow } from '../db/types'\nimport {\n insertSession,\n insertSessionEvent,\n clearSessionsForDate,\n getSessionsForDate,\n getEventsForDate,\n} from '../db'\nimport { readConfig } from '../config'\nimport { splitByIdleGap, splitByRepo, minutesBetween } from './grouper'\n\nexport interface Session {\n id: number\n date: string\n startedAt: string\n endedAt: string\n repoName: string | null\n durationMinutes: number\n eventCount: number\n summary: string | null\n events: EventRow[]\n}\n\nexport function getOrBuildSessions(date: string): Session[] {\n // Check if sessions already exist for this date\n const existing = getSessionsForDate(date)\n const events = getEventsForDate(date)\n\n if (events.length === 0) return []\n\n // If sessions exist and event count matches, use cached\n const cachedEventCount = existing.reduce((sum, s) => sum + s.event_count, 0)\n if (existing.length > 0 && cachedEventCount === events.length) {\n return existing.map((s) => ({\n id: s.id,\n date: s.date,\n startedAt: s.started_at,\n endedAt: s.ended_at,\n repoName: s.repo_name,\n durationMinutes: minutesBetween(s.started_at, s.ended_at),\n eventCount: s.event_count,\n summary: s.summary,\n events: [], // not needed when using cached\n }))\n }\n\n // Rebuild sessions\n clearSessionsForDate(date)\n return buildSessions(date, events)\n}\n\nfunction buildSessions(date: string, events: EventRow[]): Session[] {\n const config = readConfig()\n const idleMs = config.session.idleTimeout * 60 * 1000\n\n // Group events by repo, then split by idle gap within each repo group\n const sessions: Session[] = []\n\n // Split by idle gaps first (regardless of repo)\n const timeGroups = splitByIdleGap(events, idleMs)\n\n for (const group of timeGroups) {\n if (group.length === 0) continue\n\n // Within each time group, further split by repo change\n const repoGroups = splitByRepo(group)\n\n for (const repoGroup of repoGroups) {\n if (repoGroup.length === 0) continue\n\n const first = repoGroup[0]\n const last = repoGroup[repoGroup.length - 1]\n const repoName = last.repo_name ?? first.repo_name ?? null\n\n const sessionId = insertSession({\n date,\n started_at: first.captured_at,\n ended_at: last.captured_at,\n repo_name: repoName,\n event_count: repoGroup.length,\n summary: null,\n })\n\n for (const event of repoGroup) {\n insertSessionEvent(sessionId, event.id)\n }\n\n sessions.push({\n id: sessionId,\n date,\n startedAt: first.captured_at,\n endedAt: last.captured_at,\n repoName,\n durationMinutes: minutesBetween(first.captured_at, last.captured_at),\n eventCount: repoGroup.length,\n summary: null,\n events: repoGroup,\n })\n }\n }\n\n return sessions\n}\n\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { getProvider } from '../ai/router'\nimport { readConfig } from '../config'\nimport { updateSessionSummary } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\nimport type { DaySummaryPayload, SessionPayload, EventPayload } from '../ai/types'\nimport type { EventRow } from '../db'\n\nfunction eventRowToPayload(row: EventRow): EventPayload {\n let metadata: Record<string, unknown> = {}\n try {\n if (row.metadata) metadata = JSON.parse(row.metadata)\n } catch { /* ignore */ }\n return {\n category: row.category,\n metadata,\n exitCode: row.exit_code,\n durationMs: row.duration_ms,\n }\n}\n\nexport function registerSummary(program: Command): void {\n program\n .command('summary')\n .description('Generate an AI work summary')\n .option('--date <YYYY-MM-DD>', 'date to summarize (default: today)')\n .option('--week', 'summarize the current week')\n .option('--fresh', 'bypass cache and regenerate')\n .action(async (opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const sessions = getOrBuildSessions(date)\n\n if (sessions.length === 0) {\n log.plain(`No activity captured for ${date}`)\n return\n }\n\n // Check cache (unless --fresh)\n if (!opts.fresh) {\n const allCached = sessions.every((s) => s.summary)\n if (allCached) {\n log.plain('')\n for (const s of sessions) {\n log.plain(s.summary!)\n }\n log.plain('')\n log.plain(chalk.dim(' (cached — use --fresh to regenerate)'))\n return\n }\n }\n\n // Build payload\n const config = readConfig()\n let provider\n try {\n provider = getProvider(config)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n\n const sessionPayloads: SessionPayload[] = sessions.map((s) => ({\n date,\n project: s.repoName,\n branch: s.events[0]?.branch ?? null,\n startTime: formatDate(s.startedAt),\n endTime: formatDate(s.endedAt),\n durationMinutes: s.durationMinutes,\n events: s.events.map(eventRowToPayload),\n }))\n\n const payload: DaySummaryPayload = { date, sessions: sessionPayloads }\n\n log.plain('')\n log.info(`Generating summary via ${chalk.cyan(config.ai.provider)}…`)\n\n try {\n const summary = await provider.summarize(payload)\n log.plain('')\n log.plain(summary)\n log.plain('')\n\n // Cache per session (store full summary on first session for simplicity)\n if (sessions[0]) {\n updateSessionSummary(sessions[0].id, summary)\n }\n } catch (e) {\n log.error(`Summary failed [${config.ai.provider}]: ${(e as Error).message}`)\n process.exit(1)\n }\n })\n}\n","import type { DaySummaryPayload } from './types'\nimport { formatDuration } from '../utils/format'\n\nexport const SYSTEM_PROMPT = `You are a technical writer generating a work summary for a software developer.\n\nRules:\n- Write in past tense, professional tone\n- Describe what was ACCOMPLISHED, not what commands were run\n- Never write literal command names (e.g. do not write \"ran npm install\" — write \"added HTTP client dependency\")\n- Never list commands literally\n- Each bullet point should describe a meaningful unit of work\n- Group related events into single bullet points\n- If tests failed then passed, describe it as \"resolved failing tests\" not two separate events\n- Return 3-6 bullet points per project section\n- Do not pad or add filler sentences\n- Output only the bullet points grouped under project headers using this exact format:\n\n## <project-name> (<branch>) — <duration>\n\n- bullet point one\n- bullet point two\n\nIf there is no branch, omit the branch. If there are multiple projects, repeat the section for each.`\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(`Time: ${session.startTime} – ${session.endTime} (${formatDuration(session.durationMinutes)})`)\n lines.push('Events:')\n for (const event of session.events) {\n const meta = Object.keys(event.metadata).length > 0 ? ` ${JSON.stringify(event.metadata)}` : ''\n const exitNote = event.exitCode !== null && event.exitCode !== 0 ? ` [exit ${event.exitCode}]` : ''\n lines.push(` - ${event.category}${meta}${exitNote}`)\n }\n lines.push('')\n }\n\n return lines.join('\\n')\n}\n","import type { AIProvider, DaySummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage } from '../prompt'\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 fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: this.model,\n max_tokens: 1024,\n system: SYSTEM_PROMPT,\n messages: [{ role: 'user', content: buildUserMessage(payload) }],\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Anthropic API error ${response.status}: ${body}`)\n }\n\n const data = (await response.json()) as { content: Array<{ type: string; text: string }> }\n return data.content.find((c) => c.type === 'text')?.text ?? ''\n }\n}\n","import type { AIProvider, DaySummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage } from '../prompt'\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 fetch(`${this.baseUrl}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n model: this.model,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserMessage(payload) },\n ],\n max_tokens: 1024,\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`OpenAI API error ${response.status}: ${body}`)\n }\n\n const data = (await response.json()) as {\n choices: Array<{ message: { content: string } }>\n }\n return data.choices[0]?.message?.content ?? ''\n }\n}\n","import type { AIProvider, DaySummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage } from '../prompt'\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 fetch(`${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 }),\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 { ChronicleConfig } from '../config'\nimport { AnthropicProvider } from './providers/anthropic'\nimport { OpenAIProvider } from './providers/openai'\nimport { OllamaProvider } from './providers/ollama'\n\nexport function getProvider(config: ChronicleConfig): AIProvider {\n const { provider, apiKey, model, baseUrl } = config.ai\n\n if (!provider) {\n throw new Error(\n 'No AI provider configured.\\nRun: chronicle 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: chronicle 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: chronicle 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: chronicle config set ai.apiKey <your-key>')\n if (!baseUrl) throw new Error('openai-compatible requires a base URL.\\nRun: chronicle config set ai.baseUrl <url>')\n return new OpenAIProvider(apiKey, model || undefined, baseUrl)\n }\n\n case 'ollama': {\n return new OllamaProvider(model || undefined, baseUrl || undefined)\n }\n\n default:\n throw new Error(\n `Unknown provider: \"${provider}\"\\nSupported providers: anthropic, openai, ollama, openai-compatible`\n )\n }\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { getProvider } from '../ai/router'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\nimport { EXPORTS_DIR } from '../utils/paths'\nimport type { DaySummaryPayload, SessionPayload, EventPayload } from '../ai/types'\nimport type { EventRow } from '../db'\n\nfunction eventRowToPayload(row: EventRow): EventPayload {\n let metadata: Record<string, unknown> = {}\n try {\n if (row.metadata) metadata = JSON.parse(row.metadata)\n } catch { /* ignore */ }\n return { category: row.category, metadata, exitCode: row.exit_code, durationMs: row.duration_ms }\n}\n\nexport function registerExport(program: Command): void {\n program\n .command('export')\n .description('Export work summary as a markdown file')\n .option('--date <YYYY-MM-DD>', 'date to export (default: today)')\n .option('--output <path>', 'output file path (default: ~/.chronicle/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, `chronicle-${date}.md`)\n\n if (!fs.existsSync(EXPORTS_DIR)) fs.mkdirSync(EXPORTS_DIR, { recursive: true })\n\n fs.writeFileSync(outputPath, `# Work Summary — ${date}\\n\\n${summary}\\n`, 'utf-8')\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 Chronicle configuration')\n .action(() => {\n const config = readConfig()\n log.plain('')\n log.plain(chalk.bold(' Chronicle 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. chronicle 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 log.success(`Set ${chalk.cyan(key)} = ${chalk.yellow(value)}`)\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\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 const display = Array.isArray(value)\n ? `[${(value as unknown[]).join(', ')}]`\n : String(value) || chalk.dim('(not set)')\n log.plain(`${indent}${chalk.dim(key)}: ${chalk.yellow(display)}`)\n }\n }\n}\n"],"mappings":"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","names":["path","os","fs","import_path","path","import_fs","fs","normalized","import_fs","chalk","fs","program","import_fs","import_path","import_os","import_chalk","path","os","fs","program","chalk","prompts","import_chalk","program","chalk","import_chalk","chalk","program","import_chalk","program","chalk","import_chalk","program","chalk","import_fs","import_path","eventRowToPayload","program","path","fs","import_chalk","program","chalk"]}
|
|
1
|
+
{"version":3,"sources":["../src/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/capture/index.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/providers/anthropic.ts","../src/ai/providers/openai.ts","../src/ai/providers/ollama.ts","../src/ai/router.ts","../src/cli/export.ts","../src/cli/config.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { registerCapture } from './cli/capture'\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'\n\nconst program = new Command()\n\nprogram\n .name('workscribe')\n .description('Automatic developer work journal')\n .version('0.2.1')\n\nregisterCapture(program)\nregisterInit(program)\nregisterStatus(program)\nregisterEvents(program)\nregisterSessions(program)\nregisterSummary(program)\nregisterExport(program)\nregisterConfig(program)\n\nprogram.parse()\n","import { DatabaseSync } from 'node:sqlite'\nimport fs from 'fs'\nimport { WORKSCRIBE_DIR, DB_PATH } from '../utils/paths'\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 ~/.chronicle/ exists\n if (!fs.existsSync(WORKSCRIBE_DIR)) {\n fs.mkdirSync(WORKSCRIBE_DIR, { recursive: true })\n }\n\n _db = new DatabaseSync(DB_PATH)\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 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 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 SessionRow[]\n}\n\nexport function updateSessionSummary(sessionId: number, summary: string): void {\n const db = getDb()\n const stmt = db.prepare('UPDATE sessions SET summary = $summary WHERE id = $id')\n stmt.run({ summary, id: sessionId })\n}\n\nexport function clearSessionsForDate(date: string): void {\n const db = getDb()\n // Remove session_events first due to FK constraint\n db.exec(`\n DELETE FROM session_events WHERE session_id IN (\n SELECT id FROM sessions WHERE date = '${date.replace(/'/g, \"''\")}'\n )\n `)\n db.exec(`DELETE FROM sessions WHERE date = '${date.replace(/'/g, \"''\")}'`)\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')\n","// Patterns that match known sensitive values\nconst BUILTIN_PATTERNS: RegExp[] = [\n // AWS access keys\n /AKIA[0-9A-Z]{16}/g,\n // AWS secret access keys (40 char base64-ish after common env vars)\n /(?<=AWS_SECRET_ACCESS_KEY[=\\s]+)\\S+/gi,\n // GitHub tokens\n /ghp_[a-zA-Z0-9]{36}/g,\n /github_pat_[a-zA-Z0-9_]{82}/g,\n // GitLab tokens\n /glpat-[a-zA-Z0-9\\-]{20}/g,\n // Generic JWTs (three base64url segments)\n /eyJ[a-zA-Z0-9_-]+\\.eyJ[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_-]+/g,\n // Slack tokens\n /xox[baprs]-[a-zA-Z0-9\\-]+/g,\n // Stripe keys\n /sk_(live|test)_[a-zA-Z0-9]{24,}/g,\n // Values after long-form secret flags (single-char flags like -t/-p/-k are too ambiguous)\n /(?<=--(password|passwd|secret|token|key|api[-_]?key|auth[-_]?token)\\s+)\\S+/gi,\n // export VAR=VALUE where VAR looks sensitive\n /(?<=export\\s+(?:[A-Z_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASS|AUTH|CREDENTIAL)[A-Z_]*)=)\\S+/gi,\n]\n\nexport function redact(command: string, extraPatterns: string[] = []): string {\n let result = command\n\n const allPatterns = [...BUILTIN_PATTERNS]\n\n for (const pattern of extraPatterns) {\n try {\n allPatterns.push(new RegExp(pattern, 'g'))\n } catch {\n // Invalid regex — skip silently\n }\n }\n\n for (const pattern of allPatterns) {\n // Reset lastIndex for global regexes\n pattern.lastIndex = 0\n result = result.replace(pattern, '[REDACTED]')\n }\n\n return result\n}\n","import { execSync } from 'child_process'\nimport path from 'path'\n\nexport interface GitContext {\n repoName: string | null\n branch: string | null\n}\n\nexport function getGitContext(cwd: string): GitContext {\n try {\n const root = execSync('git rev-parse --show-toplevel', {\n cwd,\n timeout: 2000,\n stdio: 'pipe',\n })\n .toString()\n .trim()\n\n const repoName = path.basename(root)\n\n let branch: string | null = null\n try {\n branch = execSync('git branch --show-current', {\n cwd,\n timeout: 2000,\n stdio: 'pipe',\n })\n .toString()\n .trim() || null\n } catch {\n // Detached HEAD or not a git repo\n }\n\n return { repoName, branch }\n } catch {\n return { repoName: null, branch: null }\n }\n}\n","export interface NormalizedEvent {\n category: string\n metadata: Record<string, unknown>\n}\n\ninterface Rule {\n // Returns metadata if command matches, null otherwise\n match(cmd: string): NormalizedEvent | null\n}\n\nfunction makeRule(\n pattern: RegExp,\n category: string,\n extract: (match: RegExpMatchArray, cmd: string) => Record<string, unknown> = () => ({})\n): Rule {\n return {\n match(cmd) {\n const m = cmd.match(pattern)\n if (!m) return null\n return { category, metadata: extract(m, cmd) }\n },\n }\n}\n\n// Extract package names from install commands like: npm install foo bar\nfunction extractPackages(cmd: string, afterKeyword: string): string[] {\n const idx = cmd.indexOf(afterKeyword)\n if (idx === -1) return []\n return cmd\n .slice(idx + afterKeyword.length)\n .trim()\n .split(/\\s+/)\n .filter((p) => p && !p.startsWith('-'))\n}\n\nexport const RULES: Rule[] = [\n // ─── Git operations ───────────────────────────────────────────────────────\n makeRule(/^git\\s+commit\\b/, 'code_commit', (_m, cmd) => {\n const msgMatch = cmd.match(/-m\\s+[\"']?([^\"']+)[\"']?/)\n return { message: msgMatch ? msgMatch[1].trim() : null }\n }),\n\n makeRule(/^git\\s+push\\b/, 'code_push', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { remote: parts[2] ?? 'origin', branch: parts[3] ?? null }\n }),\n\n makeRule(/^git\\s+(pull|fetch)\\b/, 'code_pull', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], remote: parts[2] ?? 'origin' }\n }),\n\n makeRule(/^git\\s+(checkout|switch)\\b/, 'branch_switch', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n const branch = parts.find((p) => !p.startsWith('-') && p !== 'git' && p !== parts[1])\n return { branch: branch ?? null }\n }),\n\n makeRule(/^git\\s+(merge|rebase)\\b/, 'code_merge', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1], target: parts[2] ?? null }\n }),\n\n makeRule(/^git\\s+stash\\b/, 'code_stash', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[2] ?? 'push' }\n }),\n\n makeRule(/^git\\s+/, 'git_other', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { subcommand: parts[1] ?? null }\n }),\n\n // ─── Package managers — install ───────────────────────────────────────────\n makeRule(/^npm\\s+(install|i|add)\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'npm',\n packages: extractPackages(cmd, cmd.includes('install') ? 'install' : cmd.includes(' i ') ? ' i ' : 'add'),\n })),\n\n makeRule(/^(yarn|pnpm)\\s+(install|add)\\b/, 'dependency_install', (m, cmd) => ({\n tool: m[1],\n packages: extractPackages(cmd, 'add'),\n })),\n\n makeRule(/^pip\\s+(install)\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'pip',\n packages: extractPackages(cmd, 'install'),\n })),\n\n makeRule(/^cargo\\s+add\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'cargo',\n packages: extractPackages(cmd, 'add'),\n })),\n\n makeRule(/^(brew)\\s+install\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'brew',\n packages: extractPackages(cmd, 'install'),\n })),\n\n makeRule(/^go\\s+get\\b/, 'dependency_install', (_m, cmd) => ({\n tool: 'go',\n packages: extractPackages(cmd, 'get'),\n })),\n\n // ─── Package managers — remove ────────────────────────────────────────────\n makeRule(/^npm\\s+(uninstall|remove|rm)\\b/, 'dependency_remove', (_m, cmd) => ({\n tool: 'npm',\n packages: extractPackages(cmd, cmd.split(/\\s+/)[1]),\n })),\n\n makeRule(/^(yarn|pnpm)\\s+remove\\b/, 'dependency_remove', (m, cmd) => ({\n tool: m[1],\n packages: extractPackages(cmd, 'remove'),\n })),\n\n makeRule(/^pip\\s+uninstall\\b/, 'dependency_remove', (_m, cmd) => ({\n tool: 'pip',\n packages: extractPackages(cmd, 'uninstall'),\n })),\n\n // ─── Tests ────────────────────────────────────────────────────────────────\n makeRule(/^(npm\\s+(test|run\\s+test)|yarn\\s+test|pnpm\\s+test)\\b/, 'test_run', () => ({ tool: 'npm' })),\n makeRule(/^(jest|vitest)\\b/, 'test_run', (m) => ({ tool: m[1] })),\n makeRule(/^pytest\\b/, 'test_run', () => ({ tool: 'pytest' })),\n makeRule(/^go\\s+test\\b/, 'test_run', () => ({ tool: 'go' })),\n makeRule(/^cargo\\s+test\\b/, 'test_run', () => ({ tool: 'cargo' })),\n makeRule(/^rspec\\b/, 'test_run', () => ({ tool: 'rspec' })),\n makeRule(/^(php\\s+)?phpunit\\b/, 'test_run', () => ({ tool: 'phpunit' })),\n\n // ─── Build ────────────────────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+build\\b/, 'build', () => ({ tool: 'npm' })),\n makeRule(/^(yarn|pnpm)\\s+build\\b/, 'build', (m) => ({ tool: m[1] })),\n makeRule(/^(make|gmake)\\b/, 'build', (m) => ({ tool: m[1] })),\n makeRule(/^cargo\\s+build\\b/, 'build', () => ({ tool: 'cargo' })),\n makeRule(/^go\\s+build\\b/, 'build', () => ({ tool: 'go' })),\n makeRule(/^docker\\s+build\\b/, 'build', () => ({ tool: 'docker' })),\n makeRule(/^(mvn|gradle)\\b/, 'build', (m) => ({ tool: m[1] })),\n\n // ─── Dev server ───────────────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+(dev|start|serve)\\b/, 'server_run', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n makeRule(/^(yarn|pnpm)\\s+(dev|start)\\b/, 'server_run', (m) => ({ tool: m[1], script: m[2] })),\n makeRule(/^python\\s+.*manage\\.py\\s+runserver\\b/, 'server_run', () => ({ tool: 'django' })),\n\n // ─── Infrastructure ───────────────────────────────────────────────────────\n makeRule(/^ssh\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'ssh', target: parts[parts.length - 1] }\n }),\n\n makeRule(/^kubectl\\b/, 'infrastructure', (_m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'kubectl', subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^(helm|terraform|pulumi)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n makeRule(/^docker\\s+(run|compose|ps|stop|start|rm|exec)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: 'docker', subcommand: parts[1] }\n }),\n\n makeRule(/^(aws|gcloud|az)\\b/, 'infrastructure', (m, cmd) => {\n const parts = cmd.split(/\\s+/)\n return { tool: m[1], subcommand: parts[1] ?? null }\n }),\n\n // ─── Scripts ──────────────────────────────────────────────────────────────\n makeRule(/^(bash|sh|zsh|fish)\\s+\\S+/, 'script_run', (m) => ({ shell: m[1] })),\n makeRule(/^node\\s+\\S+\\.(?:js|mjs|cjs)\\b/, 'script_run', () => ({ runtime: 'node' })),\n makeRule(/^python3?\\s+\\S+\\.py\\b/, 'script_run', () => ({ runtime: 'python' })),\n\n // ─── Environment ──────────────────────────────────────────────────────────\n makeRule(/^export\\s+\\w+=/, 'environment', (_m, cmd) => {\n const varName = cmd.match(/^export\\s+(\\w+)=/)?.[1] ?? null\n return { variable: varName }\n }),\n makeRule(/^source\\s+/, 'environment', () => ({})),\n\n // ─── npm scripts (generic) ────────────────────────────────────────────────\n makeRule(/^npm\\s+run\\s+\\S+/, 'script_run', (_m, cmd) => ({\n tool: 'npm',\n script: cmd.split(/\\s+/)[2],\n })),\n]\n\nexport function normalizeCommand(command: string): NormalizedEvent {\n const trimmed = command.trim().replace(/\\s+/g, ' ')\n\n for (const rule of RULES) {\n const result = rule.match(trimmed)\n if (result) return result\n }\n\n return { category: 'other', metadata: {} }\n}\n","import fs from 'fs'\nimport { CONFIG_PATH } from '../utils/paths'\n\nexport interface ChronicleConfig {\n ai: {\n provider: string // 'anthropic' | 'openai' | 'ollama' | 'openai-compatible'\n apiKey: string\n model: string\n baseUrl: string\n }\n session: {\n idleTimeout: number // minutes\n }\n capture: {\n ignore: string[]\n }\n redact: {\n extra: string[]\n }\n summary: {\n format: 'markdown' | 'json'\n }\n}\n\nconst DEFAULTS: ChronicleConfig = {\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(): ChronicleConfig {\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: ChronicleConfig): void {\n fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8')\n}\n\nexport function getConfigValue(config: ChronicleConfig, 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: ChronicleConfig, key: string, value: string): ChronicleConfig {\n const parts = key.split('.')\n const result = JSON.parse(JSON.stringify(config)) as ChronicleConfig\n let cur = result 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 { 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'\n\nexport function captureCommand(opts: {\n command: string\n cwd: string\n exitCode: number | null\n durationMs: number | null\n}): void {\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(normalized, config.redact.extra)\n\n // 4. Extract git context\n const { repoName, branch } = getGitContext(cwd)\n\n // 5. Normalize to typed event\n const { category, metadata } = normalizeCommand(safeCommand)\n\n // 6. Persist\n insertEvent({\n captured_at: new Date().toISOString(),\n command: safeCommand,\n cwd,\n exit_code: exitCode,\n duration_ms: durationMs,\n repo_name: repoName,\n branch,\n category,\n metadata: Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null,\n })\n}\n","import 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)\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/\n if (!fs.existsSync(WORKSCRIBE_DIR)) {\n fs.mkdirSync(WORKSCRIBE_DIR, { recursive: true })\n log.success(`Created ${WORKSCRIBE_DIR}`)\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 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'\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\n log.plain('')\n log.plain(chalk.bold(' Workscribe Status'))\n log.plain('')\n log.plain(` Capture ${chalk.green('active')}`)\n log.plain(` Events today ${chalk.cyan(String(todayEvents.length))}`)\n log.plain(` Events total ${chalk.cyan(String(total))}`)\n\n if (last) {\n log.plain(` Last event ${formatDate(last.captured_at)} ${chalk.dim(last.command.slice(0, 60))}`)\n }\n\n log.plain('')\n log.plain(` AI provider ${config.ai.provider ? chalk.cyan(config.ai.provider) : chalk.yellow('not configured')}`)\n if (config.ai.model) log.plain(` Model ${chalk.dim(config.ai.model)}`)\n log.plain('')\n })\n}\n","export function formatDuration(minutes: number): string {\n if (minutes < 60) return `${Math.round(minutes)}m`\n const h = Math.floor(minutes / 60)\n const m = Math.round(minutes % 60)\n return m > 0 ? `${h}h ${m}m` : `${h}h`\n}\n\nexport function formatDate(isoString: string): string {\n return new Date(isoString).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\nexport function todayDate(): string {\n return new Date().toISOString().split('T')[0]\n}\n\nexport function parseDate(input: string): string {\n // Accepts YYYY-MM-DD, returns YYYY-MM-DD. Throws on invalid.\n const d = new Date(input + 'T00:00:00')\n if (isNaN(d.getTime())) throw new Error(`Invalid date: ${input}`)\n return input\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getEventsForDate } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\n\nconst CATEGORY_COLORS: Record<string, (s: string) => string> = {\n code_commit: chalk.green,\n code_push: chalk.green,\n code_pull: chalk.blue,\n branch_switch: chalk.blue,\n dependency_install: chalk.yellow,\n dependency_remove: chalk.yellow,\n test_run: chalk.magenta,\n build: chalk.cyan,\n server_run: chalk.cyan,\n infrastructure: chalk.red,\n script_run: chalk.white,\n environment: chalk.gray,\n git_other: chalk.blue,\n code_merge: chalk.green,\n code_stash: chalk.blue,\n other: chalk.gray,\n}\n\nexport function registerEvents(program: Command): void {\n program\n .command('events')\n .description('List captured events')\n .option('--date <YYYY-MM-DD>', 'date to list events for (default: today)')\n .option('--limit <n>', 'max events to show', parseInt)\n .action((opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const events = getEventsForDate(date)\n const limit = opts.limit ?? events.length\n\n if (events.length === 0) {\n log.plain(`No events captured for ${date}`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Events for ${date} (${events.length} total)`))\n log.plain('')\n\n for (const event of events.slice(0, limit)) {\n const colorFn = CATEGORY_COLORS[event.category] ?? chalk.gray\n const time = formatDate(event.captured_at)\n const category = colorFn(event.category.padEnd(20))\n const cmd = chalk.dim(event.command.slice(0, 55))\n const repo = event.repo_name ? chalk.dim(` [${event.repo_name}]`) : ''\n const exitBadge = event.exit_code && event.exit_code !== 0 ? chalk.red(` ✗${event.exit_code}`) : ''\n log.plain(` ${chalk.dim(time)} ${category} ${cmd}${repo}${exitBadge}`)\n }\n\n if (limit < events.length) {\n log.plain('')\n log.plain(chalk.dim(` … and ${events.length - limit} more (remove --limit to see all)`))\n }\n\n log.plain('')\n })\n}\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate, formatDuration } from '../utils/format'\n\nexport function registerSessions(program: Command): void {\n program\n .command('sessions')\n .description('List detected work sessions')\n .option('--date <YYYY-MM-DD>', 'date to show sessions for (default: today)')\n .action((opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const sessions = getOrBuildSessions(date)\n\n if (sessions.length === 0) {\n log.plain(`No sessions detected for ${date}`)\n return\n }\n\n log.plain('')\n log.plain(chalk.bold(` Sessions for ${date} (${sessions.length} session${sessions.length > 1 ? 's' : ''})`))\n log.plain('')\n\n for (const session of sessions) {\n const start = formatDate(session.startedAt)\n const end = formatDate(session.endedAt)\n const duration = formatDuration(session.durationMinutes)\n const repo = session.repoName ? chalk.cyan(session.repoName) : chalk.gray('no repo')\n log.plain(` ${chalk.dim(start)} – ${chalk.dim(end)} ${repo} ${chalk.dim(duration)} ${chalk.dim(session.eventCount + ' events')}`)\n }\n\n log.plain('')\n })\n}\n","import type { EventRow } from '../db/types'\n\n/**\n * Split a sorted list of events into groups separated by idle gaps.\n */\nexport function splitByIdleGap(events: EventRow[], idleMs: number): EventRow[][] {\n if (events.length === 0) return []\n const groups: EventRow[][] = [[events[0]]]\n\n for (let i = 1; i < events.length; i++) {\n const prev = new Date(events[i - 1].captured_at).getTime()\n const curr = new Date(events[i].captured_at).getTime()\n if (curr - prev >= idleMs) {\n groups.push([events[i]])\n } else {\n groups[groups.length - 1].push(events[i])\n }\n }\n\n return groups\n}\n\n/**\n * Split a list of events into groups by consecutive repo_name.\n * A repo change mid-stream creates a new group.\n */\nexport function splitByRepo(events: EventRow[]): EventRow[][] {\n if (events.length === 0) return []\n const groups: EventRow[][] = [[events[0]]]\n\n for (let i = 1; i < events.length; i++) {\n const prevRepo = events[i - 1].repo_name\n const currRepo = events[i].repo_name\n if (currRepo !== prevRepo) {\n groups.push([events[i]])\n } else {\n groups[groups.length - 1].push(events[i])\n }\n }\n\n return groups\n}\n\nexport function minutesBetween(start: string, end: string): number {\n return (new Date(end).getTime() - new Date(start).getTime()) / 60000\n}\n","import type { EventRow } from '../db/types'\nimport {\n insertSession,\n insertSessionEvent,\n clearSessionsForDate,\n getSessionsForDate,\n getEventsForDate,\n} from '../db'\nimport { readConfig } from '../config'\nimport { splitByIdleGap, splitByRepo, minutesBetween } from './grouper'\n\nexport interface Session {\n id: number\n date: string\n startedAt: string\n endedAt: string\n repoName: string | null\n durationMinutes: number\n eventCount: number\n summary: string | null\n events: EventRow[]\n}\n\nexport function getOrBuildSessions(date: string): Session[] {\n // Check if sessions already exist for this date\n const existing = getSessionsForDate(date)\n const events = getEventsForDate(date)\n\n if (events.length === 0) return []\n\n // If sessions exist and event count matches, use cached\n const cachedEventCount = existing.reduce((sum, s) => sum + s.event_count, 0)\n if (existing.length > 0 && cachedEventCount === events.length) {\n return existing.map((s) => ({\n id: s.id,\n date: s.date,\n startedAt: s.started_at,\n endedAt: s.ended_at,\n repoName: s.repo_name,\n durationMinutes: minutesBetween(s.started_at, s.ended_at),\n eventCount: s.event_count,\n summary: s.summary,\n events: [], // not needed when using cached\n }))\n }\n\n // Rebuild sessions\n clearSessionsForDate(date)\n return buildSessions(date, events)\n}\n\nfunction buildSessions(date: string, events: EventRow[]): Session[] {\n const config = readConfig()\n const idleMs = config.session.idleTimeout * 60 * 1000\n\n // Group events by repo, then split by idle gap within each repo group\n const sessions: Session[] = []\n\n // Split by idle gaps first (regardless of repo)\n const timeGroups = splitByIdleGap(events, idleMs)\n\n for (const group of timeGroups) {\n if (group.length === 0) continue\n\n // Within each time group, further split by repo change\n const repoGroups = splitByRepo(group)\n\n for (const repoGroup of repoGroups) {\n if (repoGroup.length === 0) continue\n\n const first = repoGroup[0]\n const last = repoGroup[repoGroup.length - 1]\n const repoName = last.repo_name ?? first.repo_name ?? null\n\n const sessionId = insertSession({\n date,\n started_at: first.captured_at,\n ended_at: last.captured_at,\n repo_name: repoName,\n event_count: repoGroup.length,\n summary: null,\n })\n\n for (const event of repoGroup) {\n insertSessionEvent(sessionId, event.id)\n }\n\n sessions.push({\n id: sessionId,\n date,\n startedAt: first.captured_at,\n endedAt: last.captured_at,\n repoName,\n durationMinutes: minutesBetween(first.captured_at, last.captured_at),\n eventCount: repoGroup.length,\n summary: null,\n events: repoGroup,\n })\n }\n }\n\n return sessions\n}\n\n","import { Command } from 'commander'\nimport chalk from 'chalk'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { getProvider } from '../ai/router'\nimport { readConfig } from '../config'\nimport { updateSessionSummary } from '../db'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\nimport type { DaySummaryPayload, SessionPayload, EventPayload } from '../ai/types'\nimport type { EventRow } from '../db'\n\nfunction eventRowToPayload(row: EventRow): EventPayload {\n let metadata: Record<string, unknown> = {}\n try {\n if (row.metadata) metadata = JSON.parse(row.metadata)\n } catch { /* ignore */ }\n return {\n category: row.category,\n metadata,\n exitCode: row.exit_code,\n durationMs: row.duration_ms,\n }\n}\n\nexport function registerSummary(program: Command): void {\n program\n .command('summary')\n .description('Generate an AI work summary')\n .option('--date <YYYY-MM-DD>', 'date to summarize (default: today)')\n .option('--week', 'summarize the current week')\n .option('--fresh', 'bypass cache and regenerate')\n .action(async (opts) => {\n let date = todayDate()\n if (opts.date) {\n try {\n date = parseDate(opts.date)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n }\n\n const sessions = getOrBuildSessions(date)\n\n if (sessions.length === 0) {\n log.plain(`No activity captured for ${date}`)\n return\n }\n\n // Check cache (unless --fresh)\n if (!opts.fresh) {\n const allCached = sessions.every((s) => s.summary)\n if (allCached) {\n log.plain('')\n for (const s of sessions) {\n log.plain(s.summary!)\n }\n log.plain('')\n log.plain(chalk.dim(' (cached — use --fresh to regenerate)'))\n return\n }\n }\n\n // Build payload\n const config = readConfig()\n let provider\n try {\n provider = getProvider(config)\n } catch (e) {\n log.error((e as Error).message)\n process.exit(1)\n }\n\n const sessionPayloads: SessionPayload[] = sessions.map((s) => ({\n date,\n project: s.repoName,\n branch: s.events[0]?.branch ?? null,\n startTime: formatDate(s.startedAt),\n endTime: formatDate(s.endedAt),\n durationMinutes: s.durationMinutes,\n events: s.events.map(eventRowToPayload),\n }))\n\n const payload: DaySummaryPayload = { date, sessions: sessionPayloads }\n\n log.plain('')\n log.info(`Generating summary via ${chalk.cyan(config.ai.provider)}…`)\n\n try {\n const summary = await provider.summarize(payload)\n log.plain('')\n log.plain(summary)\n log.plain('')\n\n // Cache per session (store full summary on first session for simplicity)\n if (sessions[0]) {\n updateSessionSummary(sessions[0].id, summary)\n }\n } catch (e) {\n log.error(`Summary failed [${config.ai.provider}]: ${(e as Error).message}`)\n process.exit(1)\n }\n })\n}\n","import type { DaySummaryPayload } from './types'\nimport { formatDuration } from '../utils/format'\n\nexport const SYSTEM_PROMPT = `You are a technical writer generating a work summary for a software developer.\n\nRules:\n- Write in past tense, professional tone\n- Describe what was ACCOMPLISHED, not what commands were run\n- Never write literal command names (e.g. do not write \"ran npm install\" — write \"added HTTP client dependency\")\n- Never list commands literally\n- Each bullet point should describe a meaningful unit of work\n- Group related events into single bullet points\n- If tests failed then passed, describe it as \"resolved failing tests\" not two separate events\n- Return 3-6 bullet points per project section\n- Do not pad or add filler sentences\n- Output only the bullet points grouped under project headers using this exact format:\n\n## <project-name> (<branch>) — <duration>\n\n- bullet point one\n- bullet point two\n\nIf there is no branch, omit the branch. If there are multiple projects, repeat the section for each.`\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(`Time: ${session.startTime} – ${session.endTime} (${formatDuration(session.durationMinutes)})`)\n lines.push('Events:')\n for (const event of session.events) {\n const meta = Object.keys(event.metadata).length > 0 ? ` ${JSON.stringify(event.metadata)}` : ''\n const exitNote = event.exitCode !== null && event.exitCode !== 0 ? ` [exit ${event.exitCode}]` : ''\n lines.push(` - ${event.category}${meta}${exitNote}`)\n }\n lines.push('')\n }\n\n return lines.join('\\n')\n}\n","import type { AIProvider, DaySummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage } from '../prompt'\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 fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: this.model,\n max_tokens: 1024,\n system: SYSTEM_PROMPT,\n messages: [{ role: 'user', content: buildUserMessage(payload) }],\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Anthropic API error ${response.status}: ${body}`)\n }\n\n const data = (await response.json()) as { content: Array<{ type: string; text: string }> }\n return data.content.find((c) => c.type === 'text')?.text ?? ''\n }\n}\n","import type { AIProvider, DaySummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage } from '../prompt'\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 fetch(`${this.baseUrl}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n model: this.model,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserMessage(payload) },\n ],\n max_tokens: 1024,\n }),\n })\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`OpenAI API error ${response.status}: ${body}`)\n }\n\n const data = (await response.json()) as {\n choices: Array<{ message: { content: string } }>\n }\n return data.choices[0]?.message?.content ?? ''\n }\n}\n","import type { AIProvider, DaySummaryPayload } from '../types'\nimport { SYSTEM_PROMPT, buildUserMessage } from '../prompt'\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 fetch(`${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 }),\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 { ChronicleConfig } from '../config'\nimport { AnthropicProvider } from './providers/anthropic'\nimport { OpenAIProvider } from './providers/openai'\nimport { OllamaProvider } from './providers/ollama'\n\nexport function getProvider(config: ChronicleConfig): AIProvider {\n const { provider, apiKey, model, baseUrl } = config.ai\n\n if (!provider) {\n throw new Error(\n 'No AI provider configured.\\nRun: chronicle 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: chronicle 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: chronicle 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: chronicle config set ai.apiKey <your-key>')\n if (!baseUrl) throw new Error('openai-compatible requires a base URL.\\nRun: chronicle config set ai.baseUrl <url>')\n return new OpenAIProvider(apiKey, model || undefined, baseUrl)\n }\n\n case 'ollama': {\n return new OllamaProvider(model || undefined, baseUrl || undefined)\n }\n\n default:\n throw new Error(\n `Unknown provider: \"${provider}\"\\nSupported providers: anthropic, openai, ollama, openai-compatible`\n )\n }\n}\n","import { Command } from 'commander'\nimport fs from 'fs'\nimport path from 'path'\nimport { getOrBuildSessions } from '../sessions/detector'\nimport { getProvider } from '../ai/router'\nimport { readConfig } from '../config'\nimport { log } from '../utils/logger'\nimport { todayDate, parseDate, formatDate } from '../utils/format'\nimport { EXPORTS_DIR } from '../utils/paths'\nimport type { DaySummaryPayload, SessionPayload, EventPayload } from '../ai/types'\nimport type { EventRow } from '../db'\n\nfunction eventRowToPayload(row: EventRow): EventPayload {\n let metadata: Record<string, unknown> = {}\n try {\n if (row.metadata) metadata = JSON.parse(row.metadata)\n } catch { /* ignore */ }\n return { category: row.category, metadata, exitCode: row.exit_code, durationMs: row.duration_ms }\n}\n\nexport function registerExport(program: Command): void {\n program\n .command('export')\n .description('Export work summary as a markdown file')\n .option('--date <YYYY-MM-DD>', 'date to export (default: today)')\n .option('--output <path>', 'output file path (default: ~/.chronicle/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, `chronicle-${date}.md`)\n\n if (!fs.existsSync(EXPORTS_DIR)) fs.mkdirSync(EXPORTS_DIR, { recursive: true })\n\n fs.writeFileSync(outputPath, `# Work Summary — ${date}\\n\\n${summary}\\n`, 'utf-8')\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 log.success(`Set ${chalk.cyan(key)} = ${chalk.yellow(value)}`)\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\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 const display = Array.isArray(value)\n ? `[${(value as unknown[]).join(', ')}]`\n : String(value) || chalk.dim('(not set)')\n log.plain(`${indent}${chalk.dim(key)}: ${chalk.yellow(display)}`)\n }\n }\n}\n"],"mappings":"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","names":["path","os","fs","import_path","path","import_fs","fs","normalized","import_fs","chalk","fs","program","import_fs","import_path","import_os","import_chalk","path","os","fs","program","chalk","prompts","import_chalk","program","chalk","import_chalk","chalk","program","import_chalk","program","chalk","import_chalk","program","chalk","import_fs","import_path","eventRowToPayload","program","path","fs","import_chalk","program","chalk"]}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workscribe/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Passive developer work journal — automatic terminal activity summaries",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
7
|
-
"
|
|
7
|
+
"workscribe": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsup",
|
package/shell/hook.bash
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Do not edit this block manually. Managed by `
|
|
3
|
-
#
|
|
4
|
-
|
|
5
|
-
if [ "$BASH_COMMAND" != "
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
# Workscribe — developer work journal
|
|
2
|
+
# Do not edit this block manually. Managed by `workscribe init`.
|
|
3
|
+
# workscribe:hook:start
|
|
4
|
+
_workscribe_preexec() {
|
|
5
|
+
if [ "$BASH_COMMAND" != "_workscribe_precmd" ]; then
|
|
6
|
+
_workscribe_cmd_start=$(date +%s%3N 2>/dev/null || echo 0)
|
|
7
|
+
_workscribe_last_cmd="$BASH_COMMAND"
|
|
8
8
|
fi
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
_workscribe_precmd() {
|
|
12
12
|
local exit_code=$?
|
|
13
|
-
if [[ -n "$
|
|
13
|
+
if [[ -n "$_workscribe_last_cmd" ]]; then
|
|
14
14
|
local now
|
|
15
15
|
now=$(date +%s%3N 2>/dev/null || echo 0)
|
|
16
|
-
local duration=$(( now -
|
|
17
|
-
|
|
18
|
-
--cmd "$
|
|
16
|
+
local duration=$(( now - _workscribe_cmd_start ))
|
|
17
|
+
workscribe _capture \
|
|
18
|
+
--cmd "$_workscribe_last_cmd" \
|
|
19
19
|
--cwd "$PWD" \
|
|
20
20
|
--exit "$exit_code" \
|
|
21
21
|
--duration "$duration" \
|
|
22
22
|
2>/dev/null &
|
|
23
|
-
|
|
23
|
+
_workscribe_last_cmd=""
|
|
24
24
|
fi
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
trap '
|
|
28
|
-
PROMPT_COMMAND="
|
|
29
|
-
#
|
|
27
|
+
trap '_workscribe_preexec' DEBUG
|
|
28
|
+
PROMPT_COMMAND="_workscribe_precmd${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
|
|
29
|
+
# workscribe:hook:end
|
package/shell/hook.zsh
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Do not edit this block manually. Managed by `
|
|
3
|
-
#
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
# Workscribe — developer work journal
|
|
2
|
+
# Do not edit this block manually. Managed by `workscribe init`.
|
|
3
|
+
# workscribe:hook:start
|
|
4
|
+
_workscribe_preexec() {
|
|
5
|
+
_workscribe_cmd_start=$EPOCHREALTIME
|
|
6
|
+
_workscribe_cmd="$1"
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
_workscribe_precmd() {
|
|
10
10
|
local exit_code=$?
|
|
11
|
-
if [[ -n "$
|
|
12
|
-
local duration_ms=$(( int(($EPOCHREALTIME - $
|
|
13
|
-
|
|
14
|
-
--cmd "$
|
|
11
|
+
if [[ -n "$_workscribe_cmd" ]]; then
|
|
12
|
+
local duration_ms=$(( int(($EPOCHREALTIME - $_workscribe_cmd_start) * 1000) ))
|
|
13
|
+
workscribe _capture \
|
|
14
|
+
--cmd "$_workscribe_cmd" \
|
|
15
15
|
--cwd "$PWD" \
|
|
16
16
|
--exit "$exit_code" \
|
|
17
17
|
--duration "$duration_ms" \
|
|
18
18
|
2>/dev/null &!
|
|
19
|
-
|
|
19
|
+
_workscribe_cmd=""
|
|
20
20
|
fi
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
autoload -Uz add-zsh-hook
|
|
24
|
-
add-zsh-hook preexec
|
|
25
|
-
add-zsh-hook precmd
|
|
26
|
-
#
|
|
24
|
+
add-zsh-hook preexec _workscribe_preexec
|
|
25
|
+
add-zsh-hook precmd _workscribe_precmd
|
|
26
|
+
# workscribe:hook:end
|