devgrowth 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,111 +1,135 @@
1
- # DevGrowth CLI
2
-
3
- Zero-friction developer growth CLI for Saw Eh Doh Wah.
4
-
5
- ## Installation
6
-
7
- Install the published package globally so the `devgrowth` command is
8
- available anywhere:
9
-
10
- ```bash
11
- npm install -g devgrowth
12
- ```
13
-
14
- ### Developing locally
15
-
16
- If you're working from a clone of this repo instead of the published
17
- package, use `npm link` to symlink your local checkout into the global npm
18
- bin:
19
-
20
- ```bash
21
- npm install
22
- npm link
23
- ```
24
-
25
- ## Windows Notification Setup
26
-
27
- ```powershell
28
- # Requires devgrowth to be globally available (via npm link or installed globally)
29
- .\scripts\install-windows.ps1
30
- ```
31
-
32
- ## Usage
33
-
34
- ```bash
35
- devgrowth # Splash banner + command list
36
- devgrowth banner # Banner with current stats
37
- devgrowth banner --compact # Single-line banner
38
- devgrowth init # Initialize configuration
39
- devgrowth start # Start tonight's session
40
- devgrowth start --light # Low-energy mode (skip timer)
41
- devgrowth start --no-banner # Skip the startup banner
42
- devgrowth log "..." # Write session log
43
- devgrowth log "..." --light # Mark as light session
44
- devgrowth status # Show weekly progress
45
- devgrowth review # Weekly review (auto-saves review section)
46
- devgrowth milestone list # Show achievement grid
47
- devgrowth milestone check laravel m1 formRequests # Check off milestone
48
- devgrowth milestone progress laravel # Show milestone progress
49
- devgrowth history # Browse current week logs
50
- devgrowth history --week 32 # View a specific week
51
- devgrowth history --week 1 --year 2027 # View week across year boundaries
52
- devgrowth history --skill laravel # Filter by skill
53
- devgrowth history --search "Form Request" # Full-text search
54
- devgrowth config show # View configuration
55
- devgrowth config set <key> <value> # Update configuration
56
- devgrowth config edit # Open config in default editor
57
- ```
58
-
59
- ## Banner
60
-
61
- Running `devgrowth` with no arguments shows a splash screen: the `devGrowth`
62
- wordmark in block letters with the turtle riding beside it, then tonight's
63
- skill, weekly progress, and your streak. `devgrowth start` shows a one-line
64
- version of it.
65
-
66
- ```
67
- █ ▄▀▀▀▄ ▄ █    __   _
68
- ▄▄▄█ ▄▄▄ ▄ ▄ █ ▄ ▄▄ ▄▄▄ ▄ ▄ ▄█▄ █ ▄▄   / ♯ # \ /・_)
69
- █ █ █▄▄▄█ █ █ █ ▀▀█ █▀ █ █ █ ▄ █ █ █▀ █ (ソ♯ # ♯ #Y /
70
- ▀▄▄▄█ ▀▄▄▄ ▀▄▀ ▀▄▄▄█ █ ▀▄▄▄▀ █▀ ▀█ █▄▄ █ █  (_)――-(_)′
71
-
72
- Saw Eh Doh Wah@DESKTOP-BOD610M · v1.0.0
73
- ──────────────────────────────────────────────────────────────────────────
74
- Tonight laravel — Deepen
75
- Resource Laravel Daily
76
- Session 21:00 · 20 min
77
- Week 36 ░░░░░ 0/5 sessions
78
- Streak 0 weeks (best: 0)
79
- Totals 1 session · 0 min
80
- Data ~/.devgrowth
81
- ```
82
-
83
- The banner is suppressed automatically when output is not a TTY, so piping and
84
- scheduled runs stay clean:
85
-
86
- ```bash
87
- devgrowth config show | jq . # no banner
88
- ```
89
-
90
- | Control | Effect |
91
- | --- | --- |
92
- | `DEVGROWTH_NO_BANNER=1` | Never show the banner |
93
- | `DEVGROWTH_BANNER=1` | Show it even when piped |
94
- | `--no-banner` | Skip it for one `devgrowth start` |
95
- | `NO_COLOR=1` | Banner without color (handled by chalk) |
96
-
97
- Terminals narrower than the 74-column header automatically fall back to the
98
- one-line banner, so the layout never wraps.
99
-
100
- ## Troubleshooting
101
-
102
- - If `devgrowth` command is not found, you likely installed without `-g`
103
- (`npm install devgrowth` puts it in a local `node_modules` folder, not on
104
- your PATH). Run `npm install -g devgrowth` instead. If you're developing
105
- from a clone, run `npm link` from the project directory.
106
- - Still not found after a global install? The npm global bin folder may not
107
- be on your PATH. Run `npm config get prefix` to find it (on Windows,
108
- typically `%AppData%\npm`), confirm that folder is on PATH, then open a
109
- new terminal.
110
- - Windows install script requires the CLI to be in PATH. Ensure it has been
111
- installed globally (or linked) first.
1
+ # DevGrowth CLI
2
+
3
+ Zero-friction developer growth CLI for Saw Eh Doh Wah.
4
+
5
+ ## Installation
6
+
7
+ Install the published package globally so the `devgrowth` command is
8
+ available anywhere:
9
+
10
+ ```bash
11
+ npm install -g devgrowth
12
+ ```
13
+
14
+ ### Developing locally
15
+
16
+ If you're working from a clone of this repo instead of the published
17
+ package, use `npm link` to symlink your local checkout into the global npm
18
+ bin:
19
+
20
+ ```bash
21
+ npm install
22
+ npm link
23
+ ```
24
+
25
+ ## Windows Notification Setup
26
+
27
+ ```powershell
28
+ # Requires devgrowth to be globally available (via npm link or installed globally)
29
+ .\scripts\install-windows.ps1
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ```bash
35
+ devgrowth # Splash banner + command list
36
+ devgrowth banner # Banner with current stats
37
+ devgrowth banner --compact # Single-line banner
38
+ devgrowth init # Initialize configuration
39
+ devgrowth start # Start tonight's session
40
+ devgrowth start --light # Low-energy mode (skip timer)
41
+ devgrowth start --no-banner # Skip the startup banner
42
+ devgrowth log "..." # Write session log
43
+ devgrowth log "..." --light # Mark as light session
44
+ devgrowth status # Show weekly progress
45
+ devgrowth review # Weekly review (auto-saves review section)
46
+ devgrowth milestone list # Show achievement grid
47
+ devgrowth milestone check laravel m1 formRequests # Check off milestone
48
+ devgrowth milestone progress laravel # Show milestone progress
49
+ devgrowth history # Browse current week logs
50
+ devgrowth history --week 32 # View a specific week
51
+ devgrowth history --week 1 --year 2027 # View week across year boundaries
52
+ devgrowth history --skill laravel # Filter by skill
53
+ devgrowth history --search "Form Request" # Full-text search
54
+ devgrowth config show # View configuration
55
+ devgrowth config set <key> <value> # Update configuration
56
+ devgrowth config edit # Open config in default editor
57
+ ```
58
+
59
+ ## Sync across devices
60
+
61
+ devgrowth works fully offline. To share your sessions, streaks, milestones and config across several computers (and see them in the web dashboard), connect each device to a [devgrowth-cloud](https://github.com/EhDohWah/devgrowth-cloud) server:
62
+
63
+ ```bash
64
+ devgrowth register --api https://your-server # first device: creates the account
65
+ devgrowth login --api https://your-server # every other device
66
+ devgrowth sync # sync now (commands also sync on their own)
67
+ devgrowth sync status # pending changes, last sync
68
+ devgrowth dashboard # open the web dashboard
69
+ devgrowth logout # disconnect this device; local data stays
70
+ ```
71
+
72
+ You can also set `DEVGROWTH_API_URL` instead of passing `--api` each time. The default is `http://localhost:3000`.
73
+
74
+ **What happens on first login**
75
+ - **The account is empty** (usually your first device): this device's current stats, milestones and config become the account's starting point.
76
+ - **The account already has data**: this device's own data is copied to `~/.devgrowth/backups/<time>/`. The device then takes on the account's history, including its Markdown logs.
77
+
78
+ **Offline**
79
+ - Commands never wait on the network for more than a few seconds, and they never fail because you're offline.
80
+ - Changes queue up in `~/.devgrowth/events.jsonl` and upload the next time a command can reach the server.
81
+ - In scripts, pipe the password in: `printf '%s\n' "$PW" | devgrowth login --email me@example.com`.
82
+
83
+ ## Banner
84
+
85
+ Running `devgrowth` with no arguments shows a splash screen: the `devGrowth`
86
+ wordmark in block letters with the turtle riding beside it, then tonight's
87
+ skill, weekly progress, and your streak. `devgrowth start` shows a one-line
88
+ version of it.
89
+
90
+ ```
91
+ █ ▄▀▀▀▄ ▄ █    __   _
92
+ ▄▄▄█ ▄▄▄ ▄ ▄ █ ▄ ▄▄ ▄▄▄ ▄ ▄ ▄█▄ █ ▄▄   / ♯ # \ /・_)
93
+ █ █ █▄▄▄█ █ █ █ ▀▀█ █▀ █ █ █ ▄ █ █ █▀ █ (ソ♯ # ♯ #Y /
94
+ ▀▄▄▄█ ▀▄▄▄ ▀▄▀ ▀▄▄▄█ █ ▀▄▄▄▀ █▀ ▀█ █▄▄ █ █  (_)――-(_)′
95
+
96
+ Saw Eh Doh Wah@DESKTOP-BOD610M · v1.0.0
97
+ ──────────────────────────────────────────────────────────────────────────
98
+ Tonight laravel — Deepen
99
+ Resource Laravel Daily
100
+ Session 21:00 · 20 min
101
+ Week 36 ░░░░░ 0/5 sessions
102
+ Streak 0 weeks (best: 0)
103
+ Totals 1 session · 0 min
104
+ Data ~/.devgrowth
105
+ ```
106
+
107
+ The banner is suppressed automatically when output is not a TTY, so piping and
108
+ scheduled runs stay clean:
109
+
110
+ ```bash
111
+ devgrowth config show | jq . # no banner
112
+ ```
113
+
114
+ | Control | Effect |
115
+ | --- | --- |
116
+ | `DEVGROWTH_NO_BANNER=1` | Never show the banner |
117
+ | `DEVGROWTH_BANNER=1` | Show it even when piped |
118
+ | `--no-banner` | Skip it for one `devgrowth start` |
119
+ | `NO_COLOR=1` | Banner without color (handled by chalk) |
120
+
121
+ Terminals narrower than the 74-column header automatically fall back to the
122
+ one-line banner, so the layout never wraps.
123
+
124
+ ## Troubleshooting
125
+
126
+ - If `devgrowth` command is not found, you likely installed without `-g`
127
+ (`npm install devgrowth` puts it in a local `node_modules` folder, not on
128
+ your PATH). Run `npm install -g devgrowth` instead. If you're developing
129
+ from a clone, run `npm link` from the project directory.
130
+ - Still not found after a global install? The npm global bin folder may not
131
+ be on your PATH. Run `npm config get prefix` to find it (on Windows,
132
+ typically `%AppData%\npm`), confirm that folder is on PATH, then open a
133
+ new terminal.
134
+ - Windows install script requires the CLI to be in PATH. Ensure it has been
135
+ installed globally (or linked) first.
package/package.json CHANGED
@@ -1,48 +1,52 @@
1
- {
2
- "name": "devgrowth",
3
- "version": "1.0.1",
4
- "description": "Zero-friction developer growth CLI",
5
- "type": "module",
6
- "keywords": [
7
- "cli",
8
- "productivity",
9
- "habit-tracker",
10
- "developer-growth",
11
- "learning"
12
- ],
13
- "homepage": "https://github.com/EhDohWah/devgrowth#readme",
14
- "bugs": {
15
- "url": "https://github.com/EhDohWah/devgrowth/issues"
16
- },
17
- "repository": {
18
- "type": "git",
19
- "url": "git+https://github.com/EhDohWah/devgrowth.git"
20
- },
21
- "license": "MIT",
22
- "author": "EhDohWah <ehdohwah007@gmail.com>",
23
- "files": [
24
- "bin",
25
- "src",
26
- "scripts"
27
- ],
28
- "bin": {
29
- "devgrowth": "bin/devgrowth.js"
30
- },
31
- "scripts": {
32
- "test": "NODE_OPTIONS='--experimental-vm-modules' jest --coverage",
33
- "test:win": "set NODE_OPTIONS=--experimental-vm-modules && jest --coverage"
34
- },
35
- "dependencies": {
36
- "chalk": "^5.3.0",
37
- "commander": "^11.1.0",
38
- "fs-extra": "^11.2.0",
39
- "node-notifier": "^10.0.1",
40
- "open": "^9.1.0"
41
- },
42
- "devDependencies": {
43
- "jest": "^29.7.0"
44
- },
45
- "engines": {
46
- "node": ">=18.0.0"
47
- }
48
- }
1
+ {
2
+ "name": "devgrowth",
3
+ "version": "1.1.0",
4
+ "description": "Zero-friction developer growth CLI",
5
+ "type": "module",
6
+ "workspaces": [
7
+ "packages/*"
8
+ ],
9
+ "keywords": [
10
+ "cli",
11
+ "productivity",
12
+ "habit-tracker",
13
+ "developer-growth",
14
+ "learning"
15
+ ],
16
+ "homepage": "https://github.com/EhDohWah/devgrowth#readme",
17
+ "bugs": {
18
+ "url": "https://github.com/EhDohWah/devgrowth/issues"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/EhDohWah/devgrowth.git"
23
+ },
24
+ "license": "MIT",
25
+ "author": "EhDohWah <ehdohwah007@gmail.com>",
26
+ "files": [
27
+ "bin",
28
+ "src",
29
+ "scripts"
30
+ ],
31
+ "bin": {
32
+ "devgrowth": "bin/devgrowth.js"
33
+ },
34
+ "scripts": {
35
+ "test": "NODE_OPTIONS='--experimental-vm-modules' jest --coverage",
36
+ "test:win": "set NODE_OPTIONS=--experimental-vm-modules && jest --coverage"
37
+ },
38
+ "dependencies": {
39
+ "chalk": "^5.3.0",
40
+ "commander": "^11.1.0",
41
+ "devgrowth-core": "^1.0.0",
42
+ "fs-extra": "^11.2.0",
43
+ "node-notifier": "^10.0.1",
44
+ "open": "^9.1.0"
45
+ },
46
+ "devDependencies": {
47
+ "jest": "^29.7.0"
48
+ },
49
+ "engines": {
50
+ "node": ">=18.0.0"
51
+ }
52
+ }
@@ -1,50 +1,54 @@
1
- import * as configManager from '../core/configManager.js';
2
- import * as storage from '../core/storage.js';
3
- import * as path from 'path';
4
- import open from 'open';
5
- import chalk from 'chalk';
6
-
7
- function parseValue(raw) {
8
- if (raw === 'true') return true;
9
- if (raw === 'false') return false;
10
- if (/^-?\d+$/.test(raw)) return parseInt(raw, 10);
11
- if (/^-?\d*\.\d+$/.test(raw)) return parseFloat(raw);
12
- return raw;
13
- }
14
-
15
- export async function configCommand(subcommand, args = []) {
16
- switch (subcommand) {
17
- case 'show': {
18
- const config = await configManager.getConfig();
19
- console.log(JSON.stringify(config, null, 2));
20
- break;
21
- }
22
- case 'set': {
23
- const [keyPath, value] = args;
24
- if (!keyPath || value === undefined) {
25
- console.error(chalk.red('Usage: devgrowth config set <key.path> <value>'));
26
- process.exit(1);
27
- }
28
- const parsed = parseValue(value);
29
- await configManager.setConfigValue(keyPath, parsed);
30
- console.log(chalk.green(`Updated ${keyPath} = ${JSON.stringify(parsed)}`));
31
- break;
32
- }
33
- case 'edit': {
34
- const configPath = path.join(storage.getDevGrowthDir(), 'config.json');
35
- try {
36
- await open(configPath);
37
- console.log('Opening config in default editor...');
38
- } catch (err) {
39
- console.error(chalk.red(`Could not open editor: ${err.message}`));
40
- console.log(chalk.yellow(`Fallback: Open this file manually:\n ${configPath}`));
41
- }
42
- break;
43
- }
44
- default: {
45
- console.error(chalk.red(`Unknown subcommand: ${subcommand}`));
46
- console.log('Usage: devgrowth config <show|set|edit>');
47
- process.exit(1);
48
- }
49
- }
50
- }
1
+ import * as configManager from '../core/configManager.js';
2
+ import * as storage from '../core/storage.js';
3
+ import * as sync from '../core/sync.js';
4
+ import * as path from 'path';
5
+ import open from 'open';
6
+ import chalk from 'chalk';
7
+
8
+ function parseValue(raw) {
9
+ if (raw === 'true') return true;
10
+ if (raw === 'false') return false;
11
+ if (/^-?\d+$/.test(raw)) return parseInt(raw, 10);
12
+ if (/^-?\d*\.\d+$/.test(raw)) return parseFloat(raw);
13
+ return raw;
14
+ }
15
+
16
+ export async function configCommand(subcommand, args = []) {
17
+ switch (subcommand) {
18
+ case 'show': {
19
+ const config = await configManager.getConfig();
20
+ console.log(JSON.stringify(config, null, 2));
21
+ break;
22
+ }
23
+ case 'set': {
24
+ const [keyPath, value] = args;
25
+ if (!keyPath || value === undefined) {
26
+ console.error(chalk.red('Usage: devgrowth config set <key.path> <value>'));
27
+ process.exit(1);
28
+ }
29
+ const parsed = parseValue(value);
30
+ await configManager.setConfigValue(keyPath, parsed);
31
+ console.log(chalk.green(`Updated ${keyPath} = ${JSON.stringify(parsed)}`));
32
+ // Sync notices config.json changed and records it for your other devices.
33
+ await sync.trySync();
34
+ break;
35
+ }
36
+ case 'edit': {
37
+ const configPath = path.join(storage.getDevGrowthDir(), 'config.json');
38
+ try {
39
+ await open(configPath);
40
+ console.log('Opening config in default editor...');
41
+ console.log(chalk.gray('Your edits sync to other devices the next time devgrowth syncs.'));
42
+ } catch (err) {
43
+ console.error(chalk.red(`Could not open editor: ${err.message}`));
44
+ console.log(chalk.yellow(`Fallback: Open this file manually:\n ${configPath}`));
45
+ }
46
+ break;
47
+ }
48
+ default: {
49
+ console.error(chalk.red(`Unknown subcommand: ${subcommand}`));
50
+ console.log('Usage: devgrowth config <show|set|edit>');
51
+ process.exit(1);
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,13 @@
1
+ import chalk from 'chalk';
2
+ import * as auth from '../core/auth.js';
3
+ import * as browser from '../core/browser.js';
4
+
5
+ export async function dashboardCommand(options = {}) {
6
+ const url = `${await auth.resolveApiUrl(options.api)}/`;
7
+ console.log(chalk.gray(`Opening ${url}`));
8
+ try {
9
+ await browser.openUrl(url);
10
+ } catch {
11
+ console.log(chalk.yellow(`Could not open the browser. Visit: ${url}`));
12
+ }
13
+ }
@@ -1,73 +1,75 @@
1
- import * as logger from '../core/logger.js';
2
- import * as schedule from '../core/schedule.js';
3
- import * as storage from '../core/storage.js';
4
- import * as path from 'path';
5
- import chalk from 'chalk';
6
-
7
- function getDateOfISOWeek(w, y) {
8
- const simple = new Date(Date.UTC(y, 0, 1 + (w - 1) * 7));
9
- const dow = simple.getUTCDay();
10
- const ISOweekStart = new Date(simple);
11
- if (dow <= 4) {
12
- ISOweekStart.setUTCDate(simple.getUTCDate() - simple.getUTCDay() + 1);
13
- } else {
14
- ISOweekStart.setUTCDate(simple.getUTCDate() + 8 - simple.getUTCDay());
15
- }
16
- return ISOweekStart;
17
- }
18
-
19
- export async function historyCommand(options) {
20
- const now = new Date();
21
- let year;
22
- let month;
23
- let weekFile;
24
-
25
- if (options.week) {
26
- const weekNum = parseInt(options.week, 10);
27
- const requestedYear = options.year ? parseInt(options.year, 10) : now.getFullYear();
28
- const mon = getDateOfISOWeek(weekNum, requestedYear);
29
- // The week may start in the previous year (e.g. ISO week 1 of 2026 begins
30
- // Dec 29, 2025) — key the file by the Monday's actual year/month.
31
- year = mon.getUTCFullYear();
32
- month = String(mon.getUTCMonth() + 1).padStart(2, '0');
33
- weekFile = `week${weekNum}.md`;
34
- } else {
35
- // Current week: key by this week's Monday so month boundaries don't split logs
36
- const weekStart = schedule.getWeekStart(now);
37
- year = weekStart.getFullYear();
38
- month = String(weekStart.getMonth() + 1).padStart(2, '0');
39
- weekFile = schedule.getCurrentWeekFileName(now);
40
- }
41
-
42
- const logs = await logger.readWeekLogs(year, month, weekFile);
43
-
44
- console.log(chalk.bold(`\nHistory \u2014 ${weekFile.replace('.md', '')}`));
45
- console.log(chalk.gray('\u2500'.repeat(50)));
46
-
47
- let entries = logger.parseLogEntries(logs);
48
-
49
- if (options.skill) {
50
- const skillLower = options.skill.toLowerCase();
51
- entries = entries.filter(entry => {
52
- const block = logs.split(/\n(?=## )/).find(b => b.includes(entry.date));
53
- return block && block.toLowerCase().includes(`**skill:** ${skillLower}`);
54
- });
55
- }
56
-
57
- if (options.search) {
58
- const searchLower = options.search.toLowerCase();
59
- entries = entries.filter(entry => {
60
- const block = logs.split(/\n(?=## )/).find(b => b.includes(entry.date));
61
- return block && block.toLowerCase().includes(searchLower);
62
- });
63
- }
64
-
65
- if (entries.length === 0) {
66
- console.log(chalk.yellow('No logs found.'));
67
- } else {
68
- for (const entry of entries) {
69
- console.log(`${chalk.cyan(entry.date)} \u2014 ${entry.message}`);
70
- }
71
- }
72
- console.log();
73
- }
1
+ import * as logger from '../core/logger.js';
2
+ import * as schedule from '../core/schedule.js';
3
+ import * as storage from '../core/storage.js';
4
+ import * as sync from '../core/sync.js';
5
+ import * as path from 'path';
6
+ import chalk from 'chalk';
7
+
8
+ function getDateOfISOWeek(w, y) {
9
+ const simple = new Date(Date.UTC(y, 0, 1 + (w - 1) * 7));
10
+ const dow = simple.getUTCDay();
11
+ const ISOweekStart = new Date(simple);
12
+ if (dow <= 4) {
13
+ ISOweekStart.setUTCDate(simple.getUTCDate() - simple.getUTCDay() + 1);
14
+ } else {
15
+ ISOweekStart.setUTCDate(simple.getUTCDate() + 8 - simple.getUTCDay());
16
+ }
17
+ return ISOweekStart;
18
+ }
19
+
20
+ export async function historyCommand(options) {
21
+ await sync.trySync();
22
+ const now = new Date();
23
+ let year;
24
+ let month;
25
+ let weekFile;
26
+
27
+ if (options.week) {
28
+ const weekNum = parseInt(options.week, 10);
29
+ const requestedYear = options.year ? parseInt(options.year, 10) : now.getFullYear();
30
+ const mon = getDateOfISOWeek(weekNum, requestedYear);
31
+ // The week may start in the previous year (e.g. ISO week 1 of 2026 begins
32
+ // Dec 29, 2025) — key the file by the Monday's actual year/month.
33
+ year = mon.getUTCFullYear();
34
+ month = String(mon.getUTCMonth() + 1).padStart(2, '0');
35
+ weekFile = `week${weekNum}.md`;
36
+ } else {
37
+ // Current week: key by this week's Monday so month boundaries don't split logs
38
+ const weekStart = schedule.getWeekStart(now);
39
+ year = weekStart.getFullYear();
40
+ month = String(weekStart.getMonth() + 1).padStart(2, '0');
41
+ weekFile = schedule.getCurrentWeekFileName(now);
42
+ }
43
+
44
+ const logs = await logger.readWeekLogs(year, month, weekFile);
45
+
46
+ console.log(chalk.bold(`\nHistory \u2014 ${weekFile.replace('.md', '')}`));
47
+ console.log(chalk.gray('\u2500'.repeat(50)));
48
+
49
+ let entries = logger.parseLogEntries(logs);
50
+
51
+ if (options.skill) {
52
+ const skillLower = options.skill.toLowerCase();
53
+ entries = entries.filter(entry => {
54
+ const block = logs.split(/\n(?=## )/).find(b => b.includes(entry.date));
55
+ return block && block.toLowerCase().includes(`**skill:** ${skillLower}`);
56
+ });
57
+ }
58
+
59
+ if (options.search) {
60
+ const searchLower = options.search.toLowerCase();
61
+ entries = entries.filter(entry => {
62
+ const block = logs.split(/\n(?=## )/).find(b => b.includes(entry.date));
63
+ return block && block.toLowerCase().includes(searchLower);
64
+ });
65
+ }
66
+
67
+ if (entries.length === 0) {
68
+ console.log(chalk.yellow('No logs found.'));
69
+ } else {
70
+ for (const entry of entries) {
71
+ console.log(`${chalk.cyan(entry.date)} \u2014 ${entry.message}`);
72
+ }
73
+ }
74
+ console.log();
75
+ }