mcoda 0.1.16 → 0.1.18

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.
@@ -18,6 +18,7 @@ interface ParsedArgs {
18
18
  debug: boolean;
19
19
  }
20
20
  export declare const parseEstimateArgs: (argv: string[]) => ParsedArgs;
21
+ export declare const formatTimeLeft: (hours: number | null | undefined) => string;
21
22
  export declare class EstimateCommands {
22
23
  static run(argv: string[]): Promise<void>;
23
24
  }
@@ -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,UAkHlD,CAAC;AA4IF,qBAAa,gBAAgB;WACd,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAgEhD"}
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;AAkBF,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,MAwBjE,CAAC;AA4HF,qBAAa,gBAAgB;WACd,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAgEhD"}
@@ -34,6 +34,8 @@ export const parseEstimateArgs = (argv) => {
34
34
  quiet: false,
35
35
  noColor: false,
36
36
  noTelemetry: false,
37
+ velocityMode: "empirical",
38
+ velocityWindow: 50,
37
39
  };
38
40
  for (let i = 0; i < argv.length; i += 1) {
39
41
  const arg = argv[i];
@@ -87,7 +89,12 @@ export const parseEstimateArgs = (argv) => {
87
89
  i += 1;
88
90
  break;
89
91
  case "--velocity-mode":
90
- parsed.velocityMode = parseVelocityMode(argv[i + 1]);
92
+ {
93
+ const mode = parseVelocityMode(argv[i + 1]);
94
+ if (mode) {
95
+ parsed.velocityMode = mode;
96
+ }
97
+ }
91
98
  i += 1;
92
99
  break;
93
100
  case "--velocity-window":
@@ -133,7 +140,10 @@ export const parseEstimateArgs = (argv) => {
133
140
  parsed.assignee = arg.split("=")[1];
134
141
  }
135
142
  else if (arg.startsWith("--velocity-mode=")) {
136
- parsed.velocityMode = parseVelocityMode(arg.split("=")[1]);
143
+ const mode = parseVelocityMode(arg.split("=")[1]);
144
+ if (mode) {
145
+ parsed.velocityMode = mode;
146
+ }
137
147
  }
138
148
  else if (arg.startsWith("--velocity-window=") || arg.startsWith("--window=")) {
139
149
  const value = parseNumber(arg.split("=")[1]);
@@ -173,6 +183,32 @@ const fmt = (value) => {
173
183
  return `${value}`;
174
184
  return value.toFixed(2);
175
185
  };
186
+ export const formatTimeLeft = (hours) => {
187
+ if (hours === null || hours === undefined)
188
+ return "N/A";
189
+ if (!Number.isFinite(hours) || hours <= 0)
190
+ return "0h";
191
+ let remainingHours = Math.max(1, Math.round(hours));
192
+ const monthHours = 24 * 30;
193
+ const weekHours = 24 * 7;
194
+ const dayHours = 24;
195
+ const months = Math.floor(remainingHours / monthHours);
196
+ remainingHours -= months * monthHours;
197
+ const weeks = Math.floor(remainingHours / weekHours);
198
+ remainingHours -= weeks * weekHours;
199
+ const days = Math.floor(remainingHours / dayHours);
200
+ remainingHours -= days * dayHours;
201
+ const parts = [];
202
+ if (months > 0)
203
+ parts.push(`${months}mo`);
204
+ if (weeks > 0)
205
+ parts.push(`${weeks}w`);
206
+ if (days > 0)
207
+ parts.push(`${days}d`);
208
+ if (remainingHours > 0 || parts.length === 0)
209
+ parts.push(`${remainingHours}h`);
210
+ return parts.join("");
211
+ };
176
212
  const pad2 = (value) => `${value}`.padStart(2, "0");
177
213
  const formatLocalDateTime = (date) => `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())} ${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
178
214
  const formatRelativeDuration = (targetMs, nowMs) => {
@@ -226,35 +262,35 @@ const renderResult = (result) => {
226
262
  "Implementation",
227
263
  fmt(result.backlogTotals.implementation.story_points),
228
264
  fmt(result.effectiveVelocity.implementationSpPerHour),
229
- fmt(result.durationsHours.implementationHours),
265
+ formatTimeLeft(result.durationsHours.implementationHours),
230
266
  ],
231
267
  [
232
268
  "Review",
233
269
  fmt(result.backlogTotals.review.story_points),
234
270
  fmt(result.effectiveVelocity.reviewSpPerHour),
235
- fmt(result.durationsHours.reviewHours),
271
+ formatTimeLeft(result.durationsHours.reviewHours),
236
272
  ],
237
273
  [
238
274
  "QA",
239
275
  fmt(result.backlogTotals.qa.story_points),
240
276
  fmt(result.effectiveVelocity.qaSpPerHour),
241
- fmt(result.durationsHours.qaHours),
277
+ formatTimeLeft(result.durationsHours.qaHours),
242
278
  ],
243
279
  [
244
280
  "Done",
245
281
  fmt(result.backlogTotals.done.story_points),
246
282
  fmt(null),
247
- fmt(0),
283
+ formatTimeLeft(0),
248
284
  ],
249
285
  [
250
286
  "Total",
251
287
  fmt(totalSp),
252
288
  fmt(null),
253
- fmt(result.durationsHours.totalHours),
289
+ formatTimeLeft(result.durationsHours.totalHours),
254
290
  ],
255
291
  ];
256
292
  // eslint-disable-next-line no-console
257
- console.log(formatTable(["LANE", "STORY_POINTS", spHeader, "HOURS"], rows));
293
+ console.log(formatTable(["LANE", "STORY_POINTS", spHeader, "TIME_LEFT"], rows));
258
294
  const samples = velocity.samples ?? { implementation: 0, review: 0, qa: 0 };
259
295
  const windowLabel = velocity.windowTasks ? ` (window ${velocity.windowTasks})` : "";
260
296
  const fallbackNote = velocity.requestedMode && velocity.requestedMode !== velocity.source
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcoda",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
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.16",
49
- "@mcoda/shared": "0.1.16"
48
+ "@mcoda/core": "0.1.18",
49
+ "@mcoda/shared": "0.1.18"
50
50
  },
51
51
  "devDependencies": {
52
- "@mcoda/integrations": "0.1.16",
53
- "@mcoda/db": "0.1.16"
52
+ "@mcoda/integrations": "0.1.18",
53
+ "@mcoda/db": "0.1.18"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "tsc -p tsconfig.json",