mcoda 0.1.31 → 0.1.32

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.
@@ -13,7 +13,7 @@ interface ParsedArgs {
13
13
  spPerHourReview?: number;
14
14
  spPerHourQa?: number;
15
15
  velocityMode?: VelocitySource;
16
- velocityWindow?: 10 | 20 | 50;
16
+ velocityWindow?: number;
17
17
  json: boolean;
18
18
  debug: boolean;
19
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"EstimateCommands.d.ts","sourceRoot":"","sources":["../../../src/commands/estimate/EstimateCommands.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,cAAc,EAGf,MAAM,aAAa,CAAC;AAErB,UAAU,UAAU;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,cAAc,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB;AA6BD,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,EAAE,KAAG,UA4HlD,CAAC;AAgEF,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,MAwBjE,CAAC;AA6NF,qBAAa,gBAAgB;WACd,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAkEhD"}
1
+ {"version":3,"file":"EstimateCommands.d.ts","sourceRoot":"","sources":["../../../src/commands/estimate/EstimateCommands.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,cAAc,EAGf,MAAM,aAAa,CAAC;AAErB,UAAU,UAAU;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB;AAmCD,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,EAAE,KAAG,UA4HlD,CAAC;AA8EF,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,MAwBjE,CAAC;AA2OF,qBAAa,gBAAgB;WACd,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAkEhD"}
@@ -11,7 +11,7 @@ const usage = `mcoda estimate \\
11
11
  [--sp-per-hour-review <FLOAT>] \\
12
12
  [--sp-per-hour-qa <FLOAT>] \\
13
13
  [--velocity-mode config|empirical|mixed] \\
14
- [--velocity-window 10|20|50] \\
14
+ [--velocity-window <POSITIVE_INT>] \\
15
15
  [--quiet] [--no-color] [--no-telemetry] \\
16
16
  [--json]`;
17
17
  const parseNumber = (value) => {
@@ -20,6 +20,12 @@ const parseNumber = (value) => {
20
20
  const parsed = Number(value);
21
21
  return Number.isFinite(parsed) ? parsed : undefined;
22
22
  };
23
+ const parsePositiveInteger = (value) => {
24
+ const parsed = parseNumber(value);
25
+ if (parsed === undefined || !Number.isInteger(parsed) || parsed <= 0)
26
+ return undefined;
27
+ return parsed;
28
+ };
23
29
  const parseVelocityMode = (value) => {
24
30
  if (!value)
25
31
  return undefined;
@@ -99,8 +105,8 @@ export const parseEstimateArgs = (argv) => {
99
105
  break;
100
106
  case "--velocity-window":
101
107
  case "--window": {
102
- const value = parseNumber(argv[i + 1]);
103
- if (value === 10 || value === 20 || value === 50) {
108
+ const value = parsePositiveInteger(argv[i + 1]);
109
+ if (value !== undefined) {
104
110
  parsed.velocityWindow = value;
105
111
  }
106
112
  i += 1;
@@ -146,8 +152,8 @@ export const parseEstimateArgs = (argv) => {
146
152
  }
147
153
  }
148
154
  else if (arg.startsWith("--velocity-window=") || arg.startsWith("--window=")) {
149
- const value = parseNumber(arg.split("=")[1]);
150
- if (value === 10 || value === 20 || value === 50) {
155
+ const value = parsePositiveInteger(arg.split("=")[1]);
156
+ if (value !== undefined) {
151
157
  parsed.velocityWindow = value;
152
158
  }
153
159
  }
@@ -175,6 +181,10 @@ const padVisible = (value, width) => {
175
181
  const diff = width - visibleLength(value);
176
182
  return diff > 0 ? `${value}${" ".repeat(diff)}` : value;
177
183
  };
184
+ const padVisibleStart = (value, width) => {
185
+ const diff = width - visibleLength(value);
186
+ return diff > 0 ? `${" ".repeat(diff)}${value}` : value;
187
+ };
178
188
  const colorize = (enabled, code, value) => enabled ? `\x1b[${code}m${value}\x1b[0m` : value;
179
189
  const style = {
180
190
  bold: (enabled, value) => colorize(enabled, 1, value),
@@ -196,10 +206,20 @@ const formatPanel = (lines) => {
196
206
  const formatBoxTable = (headers, rows, options) => {
197
207
  const widths = headers.map((header, idx) => Math.max(visibleLength(header), ...rows.map((row) => visibleLength(row[idx] ?? ""))));
198
208
  const lineStyle = options?.lineStyle ?? ((value) => value);
209
+ const align = options?.align ?? [];
199
210
  const border = (left, join, right) => lineStyle(`${left}${widths.map((width) => "─".repeat(width + 2)).join(join)}${right}`);
200
211
  const verticalLine = lineStyle("│");
201
212
  const headerLine = `${verticalLine}${headers.map((header, idx) => ` ${padVisible(header, widths[idx])} `).join(verticalLine)}${verticalLine}`;
202
- const rowLines = rows.map((row) => `${verticalLine}${row.map((cell, idx) => ` ${padVisible(cell ?? "", widths[idx])} `).join(verticalLine)}${verticalLine}`);
213
+ const rowLines = rows.map((row) => {
214
+ const cells = row.map((cell, idx) => {
215
+ const value = cell ?? "";
216
+ if (align[idx] === "right") {
217
+ return ` ${padVisibleStart(value, widths[idx])} `;
218
+ }
219
+ return ` ${padVisible(value, widths[idx])} `;
220
+ });
221
+ return `${verticalLine}${cells.join(verticalLine)}${verticalLine}`;
222
+ });
203
223
  return [
204
224
  border("╭", "┬", "╮"),
205
225
  headerLine,
@@ -302,7 +322,7 @@ const renderProgressSection = (result, colorEnabled) => {
302
322
  const done = result.completion.done;
303
323
  const labels = [
304
324
  "🛠️ Work on tasks",
305
- "🧪 Ready to qa",
325
+ "🧪 Ready to QA",
306
326
  "✅ Done",
307
327
  ];
308
328
  const maxLabel = Math.max(...labels.map((label) => visibleLength(label)));
@@ -318,7 +338,7 @@ const renderProgressSection = (result, colorEnabled) => {
318
338
  partial: (value) => style.blue(colorEnabled, value),
319
339
  empty: (value) => style.dim(colorEnabled, value),
320
340
  }),
321
- formatLine("🧪 Ready to qa", qa, {
341
+ formatLine("🧪 Ready to QA", qa, {
322
342
  full: (value) => style.yellow(colorEnabled, value),
323
343
  partial: (value) => style.magenta(colorEnabled, value),
324
344
  empty: (value) => style.dim(colorEnabled, value),
@@ -330,6 +350,10 @@ const renderProgressSection = (result, colorEnabled) => {
330
350
  }),
331
351
  ]);
332
352
  };
353
+ const formatKeyValueLines = (entries) => {
354
+ const maxLabel = Math.max(0, ...entries.map((entry) => visibleLength(entry.label)));
355
+ return entries.map((entry) => `${padVisible(entry.label, maxLabel)} : ${entry.value}`);
356
+ };
333
357
  const renderResult = (result, options) => {
334
358
  const { colorEnabled } = options;
335
359
  const purpleTableLines = (value) => style.magenta(colorEnabled, value);
@@ -380,17 +404,20 @@ const renderResult = (result, options) => {
380
404
  style.bold(colorEnabled, "STORY POINTS"),
381
405
  style.bold(colorEnabled, spHeader.toUpperCase()),
382
406
  style.bold(colorEnabled, "TIME LEFT"),
383
- ], rows, { lineStyle: purpleTableLines }));
407
+ ], rows, { lineStyle: purpleTableLines, align: ["left", "right", "right", "right"] }));
384
408
  const counts = result.statusCounts;
409
+ const statusLines = formatKeyValueLines([
410
+ { label: style.bold(colorEnabled, "Total tasks"), value: `${counts.total}` },
411
+ { label: style.cyan(colorEnabled, "Ready to code review"), value: `${counts.readyToCodeReview}` },
412
+ { label: style.yellow(colorEnabled, "Ready to QA"), value: `${counts.readyToQa}` },
413
+ { label: style.blue(colorEnabled, "In progress"), value: `${counts.inProgress}` },
414
+ { label: style.red(colorEnabled, "Failed"), value: `${counts.failed}` },
415
+ { label: style.green(colorEnabled, "Completed"), value: `${counts.completed}` },
416
+ ]);
385
417
  // eslint-disable-next-line no-console
386
418
  console.log(formatPanel([
387
419
  style.bold(colorEnabled, "📌 Task Status"),
388
- `${style.bold(colorEnabled, "Total tasks")} : ${counts.total}`,
389
- `${style.cyan(colorEnabled, "Ready to code review")} : ${counts.readyToCodeReview}`,
390
- `${style.yellow(colorEnabled, "Ready to qa")} : ${counts.readyToQa}`,
391
- `${style.blue(colorEnabled, "In progress")} : ${counts.inProgress}`,
392
- `${style.red(colorEnabled, "Failed")} : ${counts.failed}`,
393
- `${style.green(colorEnabled, "Completed")} : ${counts.completed}`,
420
+ ...statusLines,
394
421
  ]));
395
422
  // eslint-disable-next-line no-console
396
423
  console.log(renderProgressSection(result, colorEnabled));
@@ -399,11 +426,17 @@ const renderResult = (result, options) => {
399
426
  const fallbackNote = velocity.requestedMode && velocity.requestedMode !== velocity.source
400
427
  ? ` (requested ${velocity.requestedMode}; no empirical samples, using config)`
401
428
  : "";
429
+ const velocityLines = formatKeyValueLines([
430
+ { label: style.bold(colorEnabled, "Velocity source"), value: `${velocity.source}${fallbackNote}` },
431
+ {
432
+ label: `${style.bold(colorEnabled, "Samples")}${windowLabel}`,
433
+ value: `impl=${samples.implementation ?? 0}, review=${samples.review ?? 0}, qa=${samples.qa ?? 0}`,
434
+ },
435
+ ]);
402
436
  // eslint-disable-next-line no-console
403
437
  console.log(formatPanel([
404
438
  style.bold(colorEnabled, "📈 Velocity"),
405
- `${style.bold(colorEnabled, "Velocity source")} : ${velocity.source}${fallbackNote}`,
406
- `${style.bold(colorEnabled, "Samples")}${windowLabel} : impl=${samples.implementation ?? 0}, review=${samples.review ?? 0}, qa=${samples.qa ?? 0}`,
439
+ ...velocityLines,
407
440
  ]));
408
441
  // eslint-disable-next-line no-console
409
442
  console.log(style.bold(colorEnabled, style.magenta(colorEnabled, "⏱️ ETAs")));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcoda",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
4
4
  "description": "Local-first CLI for planning, documentation, and execution workflows with agent assistance.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -45,12 +45,12 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "yaml": "^2.4.2",
48
- "@mcoda/core": "0.1.31",
49
- "@mcoda/shared": "0.1.31"
48
+ "@mcoda/shared": "0.1.32",
49
+ "@mcoda/core": "0.1.32"
50
50
  },
51
51
  "devDependencies": {
52
- "@mcoda/db": "0.1.31",
53
- "@mcoda/integrations": "0.1.31"
52
+ "@mcoda/db": "0.1.32",
53
+ "@mcoda/integrations": "0.1.32"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "tsc -p tsconfig.json",