@drazenbebic/wdid 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +46 -20
  2. package/dist/index.js +271 -24
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > What did I do? — a small CLI that summarizes your git activity as a tidy table, so you can fill in your timesheet without trying to remember Tuesday.
4
4
 
5
- `wdid` reads `git log` for your author across one or more repos and renders the output as a colorized table with **Date**, **Ticket** (JIRA-style `ABC-123`, parsed from the commit subject), and **Description**.
5
+ `wdid` reads `git log` for your author across one or more repos and renders the output as a colorized table with **Date** (with commit time, shown in your local timezone), **Ticket** (JIRA-style `ABC-123` by default, parsed from the commit subject), and **Description**.
6
6
 
7
7
  ## Install
8
8
 
@@ -28,17 +28,36 @@ By default `wdid` uses `git config user.name` as the author and the current work
28
28
  ### Example output
29
29
 
30
30
  ```
31
- ┌────────────┬──────────────┬──────────────────────────────────────────────────┐
32
- │ Date │ Ticket │ Description │
33
- ├────────────┼──────────────┼──────────────────────────────────────────────────┤
34
- │ 2026-05-27 │ ABC-123 │ feat(ABC-123): add login flow │
35
- │ 2026-05-27 │ — │ chore: bump deps │
36
- │ 2026-05-26 │ ABC-119 │ fix(ABC-119): handle empty payload │
37
- └────────────┴──────────────┴──────────────────────────────────────────────────┘
31
+ ┌──────────────────┬──────────────┬──────────────────────────────────────────────────┐
32
+ │ Date │ Ticket │ Description │
33
+ ├──────────────────┼──────────────┼──────────────────────────────────────────────────┤
34
+ │ 2026-05-27 16:42 │ ABC-123 │ feat(ABC-123): add login flow │
35
+ │ 2026-05-27 11:08 │ — │ chore: bump deps │
36
+ │ 2026-05-26 17:53 │ ABC-119 │ fix(ABC-119): handle empty payload │
37
+ └──────────────────┴──────────────┴──────────────────────────────────────────────────┘
38
38
  ```
39
39
 
40
+ The time is rendered dimmed and shown in your local timezone (parsed from the committer's full ISO timestamp).
41
+
40
42
  If a commit doesn't reference a ticket, the Ticket column is left blank (rendered as `—`).
41
43
 
44
+ With `--group-by-day`, the date moves into a section heading instead of repeating per row, making longer outputs feel more like a journal:
45
+
46
+ ```
47
+ ┌───────┬──────────────┬─────────────────────────────────────┐
48
+ │ Time │ Ticket │ Description │
49
+ ├───────┴──────────────┴─────────────────────────────────────┤
50
+ │ 2026-05-27 │
51
+ ├───────┬──────────────┬─────────────────────────────────────┤
52
+ │ 16:42 │ ABC-123 │ feat(ABC-123): add login flow │
53
+ │ 11:08 │ — │ chore: bump deps │
54
+ ├───────┴──────────────┴─────────────────────────────────────┤
55
+ │ 2026-05-26 │
56
+ ├───────┬──────────────┬─────────────────────────────────────┤
57
+ │ 17:53 │ ABC-119 │ fix(ABC-119): handle empty payload │
58
+ └───────┴──────────────┴─────────────────────────────────────┘
59
+ ```
60
+
42
61
  ## Options
43
62
 
44
63
  | Option | Description |
@@ -50,6 +69,10 @@ If a commit doesn't reference a ticket, the Ticket column is left blank (rendere
50
69
  | `--repo <path...>` | One or more repo paths to query. Defaults to `defaultRepos` in config, then the current dir. |
51
70
  | `--format <preset>` | Ticket format: `jira`, `github`, `conventional`, or `custom`. Defaults to `jira`. |
52
71
  | `--ticket-pattern <regex>` | Custom regex for ticket extraction. Implies `--format custom`; overrides `--format`. |
72
+ | `--no-color` | Disable colored output. Also honored via the `NO_COLOR` env var. |
73
+ | `--limit <N>` | Cap the table to the most recent `N` rows. Positive integer. |
74
+ | `--group-by-day` | Group rows under a bold date heading per day; the row only shows the time. |
75
+ | `--json` | Emit a JSON array of commit entries to stdout instead of the table. Empty result is `[]`. |
53
76
 
54
77
  ## Configuration
55
78
 
@@ -71,21 +94,24 @@ CLI flags always win. The first match in this list is used in full (configs do n
71
94
  }
72
95
  ```
73
96
 
74
- | Field | Type | Description |
75
- | --------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------- |
76
- | `format` | `"jira" \| "github" \| "conventional" \| "custom"` | Ticket extraction preset. Default `jira`. |
77
- | `customPattern` | `string` | Regex used when `format` is `"custom"`. First capture group wins, else full match. |
78
- | `defaultAuthor` | `string` | Used when `--author` is not passed and you want to skip the `git config` lookup. |
79
- | `defaultRepos` | `string[]` | Paths to query when no `--repo` is given. `~` is expanded. |
97
+ | Field | Type | Description |
98
+ | ------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------- |
99
+ | `format` | `"jira" \| "github" \| "conventional" \| "custom"` | Ticket extraction preset. Default `jira`. |
100
+ | `customPattern` | `string` | Regex used when `format` is `"custom"`. First capture group wins, else full match. |
101
+ | `defaultAuthor` | `string` | Used when `--author` is not passed and you want to skip the `git config` lookup. |
102
+ | `defaultRepos` | `string[]` | Paths to query when no `--repo` is given. `~` is expanded. |
103
+ | `ticketColumnLabel` | `string` | Override the auto-picked column header (see below). |
80
104
 
81
105
  ### Format presets
82
106
 
83
- | Preset | Matches | Example commit → ticket |
84
- | -------------- | ------------------------------------------------ | -------------------------------------- |
85
- | `jira` | `ABC-123` style (uppercase project key + digits) | `feat(ABC-123): add login` → `ABC-123` |
86
- | `github` | `#123` style | `Closes #42` → `42` |
87
- | `conventional` | Conventional Commit `type(scope)!` | `feat(auth)!: ...` → `feat(auth)!` |
88
- | `custom` | Your `customPattern` regex | depends on the regex |
107
+ | Preset | Matches | Example commit → match | Column header |
108
+ | -------------- | ------------------------------------------------ | -------------------------------------- | ------------- |
109
+ | `jira` | `ABC-123` style (uppercase project key + digits) | `feat(ABC-123): add login` → `ABC-123` | `Ticket` |
110
+ | `github` | `#123` style | `Closes #42` → `42` | `Issue` |
111
+ | `conventional` | Conventional Commit `type(scope)!` | `feat(auth)!: ...` → `feat(auth)!` | `Type` |
112
+ | `custom` | Your `customPattern` regex | depends on the regex | `Match` |
113
+
114
+ The column header is picked automatically based on the active format. To override it for a specific preset (e.g. call them "Tasks" instead of "Ticket"), set `ticketColumnLabel` in your config.
89
115
 
90
116
  ## Development
91
117
 
package/dist/index.js CHANGED
@@ -1,14 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
+ import chalk2 from "chalk";
4
5
  import { Command } from "commander";
5
6
 
6
7
  // src/git.ts
7
- import { execFile } from "child_process";
8
+ import { execFile, spawn } from "child_process";
8
9
  import { promisify } from "util";
9
10
  var execFileAsync = promisify(execFile);
10
11
  var FIELD_SEP = "";
11
12
  var RECORD_SEP = "";
13
+ var TRUNK_BRANCHES = /* @__PURE__ */ new Set(["main", "master"]);
14
+ var pad2 = (n) => String(n).padStart(2, "0");
15
+ function formatLocalDateTime(iso) {
16
+ const d = new Date(iso);
17
+ if (Number.isNaN(d.getTime())) {
18
+ return { date: "", time: "" };
19
+ }
20
+ const date = `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
21
+ const time = `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
22
+ return { date, time };
23
+ }
12
24
  function extractTicket(message, pattern) {
13
25
  const match = message.match(pattern);
14
26
  if (!match) {
@@ -16,6 +28,80 @@ function extractTicket(message, pattern) {
16
28
  }
17
29
  return match[1] ?? match[0];
18
30
  }
31
+ function normalizeBranchName(rawName) {
32
+ const clean = rawName.replace(/[~^].*$/, "").trim();
33
+ if (!clean || clean === "undefined" || TRUNK_BRANCHES.has(clean)) {
34
+ return null;
35
+ }
36
+ return clean;
37
+ }
38
+ async function runGitWithStdin(cwd, args, input) {
39
+ return new Promise((resolve, reject) => {
40
+ const proc = spawn("git", args, { cwd });
41
+ let stdout = "";
42
+ let stderr = "";
43
+ proc.stdout.on("data", (chunk) => {
44
+ stdout += chunk.toString();
45
+ });
46
+ proc.stderr.on("data", (chunk) => {
47
+ stderr += chunk.toString();
48
+ });
49
+ proc.on("error", reject);
50
+ proc.on("close", (code) => {
51
+ if (code !== 0) {
52
+ reject(new Error(`git ${args.join(" ")} failed: ${stderr.trim()}`));
53
+ return;
54
+ }
55
+ resolve(stdout);
56
+ });
57
+ proc.stdin.write(input);
58
+ proc.stdin.end();
59
+ });
60
+ }
61
+ async function getBranchMap(cwd, shas) {
62
+ const map = /* @__PURE__ */ new Map();
63
+ if (shas.length === 0) {
64
+ return map;
65
+ }
66
+ let stdout;
67
+ try {
68
+ stdout = await runGitWithStdin(
69
+ cwd,
70
+ ["name-rev", "--stdin", "--refs=refs/heads/*"],
71
+ shas.join("\n") + "\n"
72
+ );
73
+ } catch {
74
+ return map;
75
+ }
76
+ for (const line of stdout.split("\n")) {
77
+ const m = line.match(/^([0-9a-fA-F]+)\s+\((.+)\)\s*$/);
78
+ if (!m) {
79
+ continue;
80
+ }
81
+ const sha = m[1];
82
+ const rawName = m[2];
83
+ if (!sha || !rawName) {
84
+ continue;
85
+ }
86
+ map.set(sha, normalizeBranchName(rawName));
87
+ }
88
+ return map;
89
+ }
90
+ async function assertGitRepo(cwd) {
91
+ try {
92
+ await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
93
+ cwd
94
+ });
95
+ } catch (err) {
96
+ if (err.code === "ENOENT") {
97
+ throw new Error("git is not installed or not on PATH", { cause: err });
98
+ }
99
+ throw new Error(
100
+ `not inside a git repository: ${cwd} \u2014 cd into a repo or pass --repo <path>`,
101
+ { cause: err }
102
+ );
103
+ }
104
+ }
19
105
  async function getGitUserName(cwd) {
20
106
  try {
21
107
  const { stdout } = await execFileAsync("git", ["config", "user.name"], {
@@ -37,13 +123,14 @@ async function getGitUserName(cwd) {
37
123
  }
38
124
  }
39
125
  async function getCommits(opts) {
126
+ await assertGitRepo(opts.cwd);
40
127
  const args = [
41
128
  "log",
42
129
  "--all",
43
130
  "--author-date-order",
44
131
  "--regexp-ignore-case",
45
132
  `--author=${opts.author}`,
46
- `--pretty=format:%cs${FIELD_SEP}%s${RECORD_SEP}`
133
+ `--pretty=format:%H${FIELD_SEP}%cI${FIELD_SEP}%s${RECORD_SEP}`
47
134
  ];
48
135
  if (opts.from) {
49
136
  args.push(`--after=${opts.from} 00:00`);
@@ -51,43 +138,127 @@ async function getCommits(opts) {
51
138
  if (opts.to) {
52
139
  args.push(`--before=${opts.to} 23:59`);
53
140
  }
54
- const { stdout } = await execFileAsync("git", args, {
55
- cwd: opts.cwd,
56
- maxBuffer: 32 * 1024 * 1024
57
- });
58
- return stdout.split(RECORD_SEP).map((r) => r.trim()).filter((r) => r.length > 0).map((record) => {
59
- const [date = "", subject = ""] = record.split(FIELD_SEP);
141
+ if (opts.limit !== void 0) {
142
+ args.push(`--max-count=${opts.limit}`);
143
+ }
144
+ let stdout;
145
+ try {
146
+ const result = await execFileAsync("git", args, {
147
+ cwd: opts.cwd,
148
+ maxBuffer: 32 * 1024 * 1024
149
+ });
150
+ stdout = result.stdout;
151
+ } catch (err) {
152
+ const stderr = String(err.stderr ?? "");
153
+ if (stderr.includes("does not have any commits") || stderr.includes("bad default revision") || stderr.includes("unknown revision")) {
154
+ return [];
155
+ }
156
+ throw err;
157
+ }
158
+ const parsed = stdout.split(RECORD_SEP).map((r) => r.trim()).filter((r) => r.length > 0).map((record) => {
159
+ const [sha = "", iso = "", subject = ""] = record.split(FIELD_SEP);
160
+ const { date, time } = formatLocalDateTime(iso);
60
161
  return {
162
+ sha,
61
163
  date,
164
+ time,
62
165
  ticket: extractTicket(subject, opts.pattern),
63
166
  description: subject
64
167
  };
65
168
  });
169
+ const branchMap = await getBranchMap(
170
+ opts.cwd,
171
+ parsed.map((p) => p.sha).filter((s) => s.length > 0)
172
+ );
173
+ return parsed.map((p) => ({
174
+ date: p.date,
175
+ time: p.time,
176
+ ticket: p.ticket,
177
+ description: p.description,
178
+ branch: branchMap.get(p.sha) ?? null
179
+ }));
66
180
  }
67
181
 
68
182
  // src/format.ts
69
183
  import Table from "cli-table3";
70
184
  import chalk from "chalk";
71
- function renderTable(entries) {
185
+ function renderDateCell(entry) {
186
+ if (!entry.time) {
187
+ return entry.date;
188
+ }
189
+ return `${entry.date} ${chalk.dim(entry.time)}`;
190
+ }
191
+ function renderDescriptionCell(entry) {
192
+ if (!entry.branch) {
193
+ return entry.description;
194
+ }
195
+ return `${entry.description} ${chalk.magenta(`[${entry.branch}]`)}`;
196
+ }
197
+ function renderTable(entries, ticketColumnLabel = "Ticket") {
72
198
  const table = new Table({
73
199
  head: [
74
200
  chalk.bold.cyan("Date"),
75
- chalk.bold.cyan("Ticket"),
201
+ chalk.bold.cyan(ticketColumnLabel),
202
+ chalk.bold.cyan("Description")
203
+ ],
204
+ style: { head: [], border: [] },
205
+ wordWrap: true,
206
+ colWidths: [18, 14, 80]
207
+ });
208
+ for (const entry of entries) {
209
+ table.push([
210
+ renderDateCell(entry),
211
+ entry.ticket ? chalk.yellow(entry.ticket) : chalk.gray("\u2014"),
212
+ renderDescriptionCell(entry)
213
+ ]);
214
+ }
215
+ return table.toString();
216
+ }
217
+ function renderTableGroupedByDay(entries, ticketColumnLabel = "Ticket") {
218
+ const table = new Table({
219
+ head: [
220
+ chalk.bold.cyan("Time"),
221
+ chalk.bold.cyan(ticketColumnLabel),
76
222
  chalk.bold.cyan("Description")
77
223
  ],
78
224
  style: { head: [], border: [] },
79
225
  wordWrap: true,
80
- colWidths: [12, 14, 80]
226
+ colWidths: [8, 14, 90]
81
227
  });
228
+ let currentDate = "";
82
229
  for (const entry of entries) {
230
+ if (entry.date && entry.date !== currentDate) {
231
+ table.push([
232
+ {
233
+ content: chalk.bold(entry.date),
234
+ colSpan: 3,
235
+ hAlign: "left"
236
+ }
237
+ ]);
238
+ currentDate = entry.date;
239
+ }
83
240
  table.push([
84
- chalk.dim(entry.date),
241
+ entry.time ? chalk.dim(entry.time) : chalk.gray("\u2014"),
85
242
  entry.ticket ? chalk.yellow(entry.ticket) : chalk.gray("\u2014"),
86
- entry.description
243
+ renderDescriptionCell(entry)
87
244
  ]);
88
245
  }
89
246
  return table.toString();
90
247
  }
248
+ function toJsonKey(label) {
249
+ return label.toLowerCase().replace(/\s+/g, "_");
250
+ }
251
+ function renderJson(entries, ticketColumnLabel = "Ticket") {
252
+ const key = toJsonKey(ticketColumnLabel);
253
+ const transformed = entries.map((e) => ({
254
+ date: e.date,
255
+ time: e.time,
256
+ [key]: e.ticket,
257
+ description: e.description,
258
+ branch: e.branch
259
+ }));
260
+ return JSON.stringify(transformed, null, 2);
261
+ }
91
262
  function renderEmpty() {
92
263
  return chalk.gray("No commits found for the given filters.");
93
264
  }
@@ -100,6 +271,15 @@ import { homedir } from "os";
100
271
  import { readFile } from "fs/promises";
101
272
  import { join } from "path";
102
273
  import { cosmiconfig } from "cosmiconfig";
274
+ var DEFAULT_COLUMN_LABELS = {
275
+ jira: "Ticket",
276
+ github: "Issue",
277
+ conventional: "Type",
278
+ custom: "Match"
279
+ };
280
+ function getColumnLabel(format, override) {
281
+ return override ?? DEFAULT_COLUMN_LABELS[format];
282
+ }
103
283
  var PRESET_PATTERNS = {
104
284
  jira: /\b([A-Z][A-Z0-9]+-\d+)\b/,
105
285
  github: /#(\d+)/,
@@ -111,12 +291,28 @@ var VALID_FORMATS = [
111
291
  "conventional",
112
292
  "custom"
113
293
  ];
294
+ var MAX_CUSTOM_PATTERN_LENGTH = 500;
295
+ function compileUserRegex(pattern) {
296
+ if (pattern.length > MAX_CUSTOM_PATTERN_LENGTH) {
297
+ throw new Error(
298
+ `customPattern is ${pattern.length} characters; limit is ${MAX_CUSTOM_PATTERN_LENGTH}`
299
+ );
300
+ }
301
+ try {
302
+ return new RegExp(pattern);
303
+ } catch (err) {
304
+ throw new Error(
305
+ `customPattern is not a valid regex: ${err.message}`,
306
+ { cause: err }
307
+ );
308
+ }
309
+ }
114
310
  function getTicketPattern(format, customPattern) {
115
311
  if (format === "custom") {
116
312
  if (!customPattern) {
117
313
  throw new Error('format "custom" requires customPattern to be set');
118
314
  }
119
- return new RegExp(customPattern);
315
+ return compileUserRegex(customPattern);
120
316
  }
121
317
  return PRESET_PATTERNS[format];
122
318
  }
@@ -147,6 +343,11 @@ function validateConfig(raw) {
147
343
  if (typeof obj.customPattern !== "string") {
148
344
  throw new Error("customPattern must be a string");
149
345
  }
346
+ if (obj.customPattern.length > MAX_CUSTOM_PATTERN_LENGTH) {
347
+ throw new Error(
348
+ `customPattern is ${obj.customPattern.length} characters; limit is ${MAX_CUSTOM_PATTERN_LENGTH}`
349
+ );
350
+ }
150
351
  cfg.customPattern = obj.customPattern;
151
352
  }
152
353
  if (cfg.format === "custom" && !cfg.customPattern) {
@@ -164,6 +365,12 @@ function validateConfig(raw) {
164
365
  }
165
366
  cfg.defaultRepos = obj.defaultRepos;
166
367
  }
368
+ if ("ticketColumnLabel" in obj) {
369
+ if (typeof obj.ticketColumnLabel !== "string") {
370
+ throw new Error("ticketColumnLabel must be a string");
371
+ }
372
+ cfg.ticketColumnLabel = obj.ticketColumnLabel;
373
+ }
167
374
  return cfg;
168
375
  }
169
376
  function globalConfigPath() {
@@ -195,6 +402,23 @@ async function loadConfig(cwd) {
195
402
  }
196
403
 
197
404
  // src/index.ts
405
+ function parseLimit(raw) {
406
+ if (raw === void 0) {
407
+ return void 0;
408
+ }
409
+ const n = Number.parseInt(raw, 10);
410
+ if (!Number.isInteger(n) || n < 1 || String(n) !== raw.trim()) {
411
+ throw new Error(`invalid --limit "${raw}" \u2014 must be a positive integer`);
412
+ }
413
+ return n;
414
+ }
415
+ function shouldDisableColor(options) {
416
+ if (options.color === false) {
417
+ return true;
418
+ }
419
+ const noColor = process.env.NO_COLOR ?? "";
420
+ return noColor.length > 0;
421
+ }
198
422
  var VALID_PRESETS = [
199
423
  "jira",
200
424
  "github",
@@ -242,21 +466,32 @@ async function run(dateArg, options) {
242
466
  from = day;
243
467
  to = day;
244
468
  }
245
- const allEntries = [];
246
- for (const cwd of repos) {
247
- const author = options.author ?? config.defaultAuthor ?? await getGitUserName(cwd);
248
- const entries = await getCommits({ author, from, to, cwd, pattern });
249
- allEntries.push(...entries);
469
+ const limit = parseLimit(options.limit);
470
+ const perRepoEntries = await Promise.all(
471
+ repos.map(async (cwd) => {
472
+ const author = options.author ?? config.defaultAuthor ?? await getGitUserName(cwd);
473
+ return getCommits({ author, from, to, cwd, pattern, limit });
474
+ })
475
+ );
476
+ const allEntries = perRepoEntries.flat();
477
+ allEntries.sort(
478
+ (a, b) => `${b.date} ${b.time}`.localeCompare(`${a.date} ${a.time}`)
479
+ );
480
+ const display = limit !== void 0 ? allEntries.slice(0, limit) : allEntries;
481
+ const ticketColumnLabel = getColumnLabel(format, config.ticketColumnLabel);
482
+ if (options.json) {
483
+ process.stdout.write(renderJson(display, ticketColumnLabel) + "\n");
484
+ return;
250
485
  }
251
- allEntries.sort((a, b) => b.date.localeCompare(a.date));
252
- if (allEntries.length === 0) {
486
+ if (display.length === 0) {
253
487
  process.stdout.write(renderEmpty() + "\n");
254
488
  return;
255
489
  }
256
- process.stdout.write(renderTable(allEntries) + "\n");
490
+ const rendered = options.groupByDay ? renderTableGroupedByDay(display, ticketColumnLabel) : renderTable(display, ticketColumnLabel);
491
+ process.stdout.write(rendered + "\n");
257
492
  }
258
493
  var program = new Command();
259
- program.name("wdid").description("What did I do? \u2014 summarize your git commits as a table").version("0.1.3", "-V, --version", "output the version number").argument("[date]", 'a YYYY-MM-DD date or "today"; omit to show all history').option("--from <date>", 'start date (YYYY-MM-DD or "today")').option("--to <date>", 'end date (YYYY-MM-DD or "today")').option(
494
+ program.name("wdid").description("What did I do? \u2014 summarize your git commits as a table").version("0.2.0", "-V, --version", "output the version number").argument("[date]", 'a YYYY-MM-DD date or "today"; omit to show all history').option("--from <date>", 'start date (YYYY-MM-DD or "today")').option("--to <date>", 'end date (YYYY-MM-DD or "today")').option(
260
495
  "--author <name>",
261
496
  "override the git author (defaults to git config user.name, then defaultAuthor in config)"
262
497
  ).option(
@@ -268,7 +503,19 @@ program.name("wdid").description("What did I do? \u2014 summarize your git commi
268
503
  ).option(
269
504
  "--ticket-pattern <regex>",
270
505
  "custom regex for ticket extraction (implies --format custom; overrides --format)"
271
- ).action(async (dateArg, options) => {
506
+ ).option(
507
+ "--no-color",
508
+ "disable colored output (also honored via the NO_COLOR env var)"
509
+ ).option(
510
+ "--limit <N>",
511
+ "cap the table to the most recent N rows (positive integer)"
512
+ ).option(
513
+ "--group-by-day",
514
+ "group rows under a bold date heading per day (time-only in row)"
515
+ ).option("--json", "emit a JSON array of commit entries instead of the table").action(async (dateArg, options) => {
516
+ if (shouldDisableColor(options)) {
517
+ chalk2.level = 0;
518
+ }
272
519
  try {
273
520
  await run(dateArg, options);
274
521
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drazenbebic/wdid",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "What did I do? — summarize your git activity per day as a tidy table, grouped by JIRA ticket.",
5
5
  "keywords": [
6
6
  "git",