mcoda 0.1.31 → 0.1.33
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.
|
@@ -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,
|
|
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;AAqHF,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
|
|
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 =
|
|
103
|
-
if (value
|
|
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 =
|
|
150
|
-
if (value
|
|
155
|
+
const value = parsePositiveInteger(arg.split("=")[1]);
|
|
156
|
+
if (value !== undefined) {
|
|
151
157
|
parsed.velocityWindow = value;
|
|
152
158
|
}
|
|
153
159
|
}
|
|
@@ -170,11 +176,73 @@ export const parseEstimateArgs = (argv) => {
|
|
|
170
176
|
};
|
|
171
177
|
const ANSI_REGEX = /\x1b\[[0-9;]*m/g;
|
|
172
178
|
const stripAnsi = (value) => value.replace(ANSI_REGEX, "");
|
|
173
|
-
|
|
179
|
+
// Mirrors common wcwidth full-width ranges so CJK/emoji cells align in terminal output.
|
|
180
|
+
const isFullWidthCodePoint = (codePoint) => {
|
|
181
|
+
if (codePoint >= 0x1100 && codePoint <= 0x115f)
|
|
182
|
+
return true;
|
|
183
|
+
if (codePoint >= 0x2329 && codePoint <= 0x232a)
|
|
184
|
+
return true;
|
|
185
|
+
if (codePoint >= 0x2e80 && codePoint <= 0x303e)
|
|
186
|
+
return true;
|
|
187
|
+
if (codePoint >= 0x3040 && codePoint <= 0xa4cf)
|
|
188
|
+
return true;
|
|
189
|
+
if (codePoint >= 0xac00 && codePoint <= 0xd7a3)
|
|
190
|
+
return true;
|
|
191
|
+
if (codePoint >= 0xf900 && codePoint <= 0xfaff)
|
|
192
|
+
return true;
|
|
193
|
+
if (codePoint >= 0xfe10 && codePoint <= 0xfe19)
|
|
194
|
+
return true;
|
|
195
|
+
if (codePoint >= 0xfe30 && codePoint <= 0xfe6f)
|
|
196
|
+
return true;
|
|
197
|
+
if (codePoint >= 0xff00 && codePoint <= 0xff60)
|
|
198
|
+
return true;
|
|
199
|
+
if (codePoint >= 0xffe0 && codePoint <= 0xffe6)
|
|
200
|
+
return true;
|
|
201
|
+
if (codePoint >= 0x1f300 && codePoint <= 0x1f64f)
|
|
202
|
+
return true;
|
|
203
|
+
if (codePoint >= 0x1f900 && codePoint <= 0x1f9ff)
|
|
204
|
+
return true;
|
|
205
|
+
if (codePoint >= 0x20000 && codePoint <= 0x3fffd)
|
|
206
|
+
return true;
|
|
207
|
+
return false;
|
|
208
|
+
};
|
|
209
|
+
const isCombiningCodePoint = (codePoint) => {
|
|
210
|
+
if (codePoint >= 0x0300 && codePoint <= 0x036f)
|
|
211
|
+
return true;
|
|
212
|
+
if (codePoint >= 0x1ab0 && codePoint <= 0x1aff)
|
|
213
|
+
return true;
|
|
214
|
+
if (codePoint >= 0x1dc0 && codePoint <= 0x1dff)
|
|
215
|
+
return true;
|
|
216
|
+
if (codePoint >= 0x20d0 && codePoint <= 0x20ff)
|
|
217
|
+
return true;
|
|
218
|
+
if (codePoint >= 0xfe20 && codePoint <= 0xfe2f)
|
|
219
|
+
return true;
|
|
220
|
+
return false;
|
|
221
|
+
};
|
|
222
|
+
const visibleLength = (value) => {
|
|
223
|
+
const plain = stripAnsi(value);
|
|
224
|
+
if (!plain)
|
|
225
|
+
return 0;
|
|
226
|
+
let width = 0;
|
|
227
|
+
for (const ch of plain) {
|
|
228
|
+
// Zero-width shaping/selectors.
|
|
229
|
+
if (ch === "\u200d" || ch === "\ufe0e" || ch === "\ufe0f")
|
|
230
|
+
continue;
|
|
231
|
+
const codePoint = ch.codePointAt(0);
|
|
232
|
+
if (codePoint === undefined || isCombiningCodePoint(codePoint))
|
|
233
|
+
continue;
|
|
234
|
+
width += isFullWidthCodePoint(codePoint) ? 2 : 1;
|
|
235
|
+
}
|
|
236
|
+
return width;
|
|
237
|
+
};
|
|
174
238
|
const padVisible = (value, width) => {
|
|
175
239
|
const diff = width - visibleLength(value);
|
|
176
240
|
return diff > 0 ? `${value}${" ".repeat(diff)}` : value;
|
|
177
241
|
};
|
|
242
|
+
const padVisibleStart = (value, width) => {
|
|
243
|
+
const diff = width - visibleLength(value);
|
|
244
|
+
return diff > 0 ? `${" ".repeat(diff)}${value}` : value;
|
|
245
|
+
};
|
|
178
246
|
const colorize = (enabled, code, value) => enabled ? `\x1b[${code}m${value}\x1b[0m` : value;
|
|
179
247
|
const style = {
|
|
180
248
|
bold: (enabled, value) => colorize(enabled, 1, value),
|
|
@@ -196,10 +264,20 @@ const formatPanel = (lines) => {
|
|
|
196
264
|
const formatBoxTable = (headers, rows, options) => {
|
|
197
265
|
const widths = headers.map((header, idx) => Math.max(visibleLength(header), ...rows.map((row) => visibleLength(row[idx] ?? ""))));
|
|
198
266
|
const lineStyle = options?.lineStyle ?? ((value) => value);
|
|
267
|
+
const align = options?.align ?? [];
|
|
199
268
|
const border = (left, join, right) => lineStyle(`${left}${widths.map((width) => "─".repeat(width + 2)).join(join)}${right}`);
|
|
200
269
|
const verticalLine = lineStyle("│");
|
|
201
270
|
const headerLine = `${verticalLine}${headers.map((header, idx) => ` ${padVisible(header, widths[idx])} `).join(verticalLine)}${verticalLine}`;
|
|
202
|
-
const rowLines = rows.map((row) =>
|
|
271
|
+
const rowLines = rows.map((row) => {
|
|
272
|
+
const cells = row.map((cell, idx) => {
|
|
273
|
+
const value = cell ?? "";
|
|
274
|
+
if (align[idx] === "right") {
|
|
275
|
+
return ` ${padVisibleStart(value, widths[idx])} `;
|
|
276
|
+
}
|
|
277
|
+
return ` ${padVisible(value, widths[idx])} `;
|
|
278
|
+
});
|
|
279
|
+
return `${verticalLine}${cells.join(verticalLine)}${verticalLine}`;
|
|
280
|
+
});
|
|
203
281
|
return [
|
|
204
282
|
border("╭", "┬", "╮"),
|
|
205
283
|
headerLine,
|
|
@@ -302,7 +380,7 @@ const renderProgressSection = (result, colorEnabled) => {
|
|
|
302
380
|
const done = result.completion.done;
|
|
303
381
|
const labels = [
|
|
304
382
|
"🛠️ Work on tasks",
|
|
305
|
-
"🧪 Ready to
|
|
383
|
+
"🧪 Ready to QA",
|
|
306
384
|
"✅ Done",
|
|
307
385
|
];
|
|
308
386
|
const maxLabel = Math.max(...labels.map((label) => visibleLength(label)));
|
|
@@ -318,7 +396,7 @@ const renderProgressSection = (result, colorEnabled) => {
|
|
|
318
396
|
partial: (value) => style.blue(colorEnabled, value),
|
|
319
397
|
empty: (value) => style.dim(colorEnabled, value),
|
|
320
398
|
}),
|
|
321
|
-
formatLine("🧪 Ready to
|
|
399
|
+
formatLine("🧪 Ready to QA", qa, {
|
|
322
400
|
full: (value) => style.yellow(colorEnabled, value),
|
|
323
401
|
partial: (value) => style.magenta(colorEnabled, value),
|
|
324
402
|
empty: (value) => style.dim(colorEnabled, value),
|
|
@@ -330,6 +408,10 @@ const renderProgressSection = (result, colorEnabled) => {
|
|
|
330
408
|
}),
|
|
331
409
|
]);
|
|
332
410
|
};
|
|
411
|
+
const formatKeyValueLines = (entries) => {
|
|
412
|
+
const maxLabel = Math.max(0, ...entries.map((entry) => visibleLength(entry.label)));
|
|
413
|
+
return entries.map((entry) => `${padVisible(entry.label, maxLabel)} : ${entry.value}`);
|
|
414
|
+
};
|
|
333
415
|
const renderResult = (result, options) => {
|
|
334
416
|
const { colorEnabled } = options;
|
|
335
417
|
const purpleTableLines = (value) => style.magenta(colorEnabled, value);
|
|
@@ -380,17 +462,20 @@ const renderResult = (result, options) => {
|
|
|
380
462
|
style.bold(colorEnabled, "STORY POINTS"),
|
|
381
463
|
style.bold(colorEnabled, spHeader.toUpperCase()),
|
|
382
464
|
style.bold(colorEnabled, "TIME LEFT"),
|
|
383
|
-
], rows, { lineStyle: purpleTableLines }));
|
|
465
|
+
], rows, { lineStyle: purpleTableLines, align: ["left", "right", "right", "right"] }));
|
|
384
466
|
const counts = result.statusCounts;
|
|
467
|
+
const statusLines = formatKeyValueLines([
|
|
468
|
+
{ label: style.bold(colorEnabled, "Total tasks"), value: `${counts.total}` },
|
|
469
|
+
{ label: style.cyan(colorEnabled, "Ready to code review"), value: `${counts.readyToCodeReview}` },
|
|
470
|
+
{ label: style.yellow(colorEnabled, "Ready to QA"), value: `${counts.readyToQa}` },
|
|
471
|
+
{ label: style.blue(colorEnabled, "In progress"), value: `${counts.inProgress}` },
|
|
472
|
+
{ label: style.red(colorEnabled, "Failed"), value: `${counts.failed}` },
|
|
473
|
+
{ label: style.green(colorEnabled, "Completed"), value: `${counts.completed}` },
|
|
474
|
+
]);
|
|
385
475
|
// eslint-disable-next-line no-console
|
|
386
476
|
console.log(formatPanel([
|
|
387
477
|
style.bold(colorEnabled, "📌 Task Status"),
|
|
388
|
-
|
|
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}`,
|
|
478
|
+
...statusLines,
|
|
394
479
|
]));
|
|
395
480
|
// eslint-disable-next-line no-console
|
|
396
481
|
console.log(renderProgressSection(result, colorEnabled));
|
|
@@ -399,11 +484,17 @@ const renderResult = (result, options) => {
|
|
|
399
484
|
const fallbackNote = velocity.requestedMode && velocity.requestedMode !== velocity.source
|
|
400
485
|
? ` (requested ${velocity.requestedMode}; no empirical samples, using config)`
|
|
401
486
|
: "";
|
|
487
|
+
const velocityLines = formatKeyValueLines([
|
|
488
|
+
{ label: style.bold(colorEnabled, "Velocity source"), value: `${velocity.source}${fallbackNote}` },
|
|
489
|
+
{
|
|
490
|
+
label: `${style.bold(colorEnabled, "Samples")}${windowLabel}`,
|
|
491
|
+
value: `impl=${samples.implementation ?? 0}, review=${samples.review ?? 0}, qa=${samples.qa ?? 0}`,
|
|
492
|
+
},
|
|
493
|
+
]);
|
|
402
494
|
// eslint-disable-next-line no-console
|
|
403
495
|
console.log(formatPanel([
|
|
404
496
|
style.bold(colorEnabled, "📈 Velocity"),
|
|
405
|
-
|
|
406
|
-
`${style.bold(colorEnabled, "Samples")}${windowLabel} : impl=${samples.implementation ?? 0}, review=${samples.review ?? 0}, qa=${samples.qa ?? 0}`,
|
|
497
|
+
...velocityLines,
|
|
407
498
|
]));
|
|
408
499
|
// eslint-disable-next-line no-console
|
|
409
500
|
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.
|
|
3
|
+
"version": "0.1.33",
|
|
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.
|
|
49
|
-
"@mcoda/shared": "0.1.
|
|
48
|
+
"@mcoda/core": "0.1.33",
|
|
49
|
+
"@mcoda/shared": "0.1.33"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@mcoda/db": "0.1.
|
|
53
|
-
"@mcoda/integrations": "0.1.
|
|
52
|
+
"@mcoda/db": "0.1.33",
|
|
53
|
+
"@mcoda/integrations": "0.1.33"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "tsc -p tsconfig.json",
|