devdaily-ai 0.6.0 → 0.6.1
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/CHANGELOG.md +6 -0
- package/dist/index.js +143 -142
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.6.1] - 2026-02-15
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Slack webhook formatting** — Standup messages sent to Slack via `--send` / `--slack` now correctly render bold, italic, and other formatting. Previously, raw Markdown syntax (e.g. `**bold**`) was sent directly into Slack's `mrkdwn` blocks, which don't support double-asterisk bold. The notification pipeline now converts Markdown → Slack mrkdwn format using the existing `formatOutput()` formatter before sending, so `**text**` becomes `*text*`, `*italic*` becomes `_italic_`, headers become bold lines, and links use Slack's `<url|text>` syntax.
|
|
13
|
+
|
|
8
14
|
## [0.5.0] - 2025-07-13
|
|
9
15
|
|
|
10
16
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -3386,6 +3386,147 @@ Requirements:
|
|
|
3386
3386
|
}
|
|
3387
3387
|
};
|
|
3388
3388
|
|
|
3389
|
+
// src/utils/formatter.ts
|
|
3390
|
+
function formatOutput(markdown, format = "markdown", meta) {
|
|
3391
|
+
switch (format) {
|
|
3392
|
+
case "plain":
|
|
3393
|
+
return { text: toPlain(markdown), raw: markdown, format };
|
|
3394
|
+
case "slack":
|
|
3395
|
+
return { text: toSlack(markdown), raw: markdown, format };
|
|
3396
|
+
case "json":
|
|
3397
|
+
return { text: toJSON(markdown, meta), raw: markdown, format };
|
|
3398
|
+
case "markdown":
|
|
3399
|
+
default:
|
|
3400
|
+
return { text: markdown, raw: markdown, format };
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
function validateFormat(value, fallback = "markdown") {
|
|
3404
|
+
const valid = ["markdown", "slack", "plain", "json"];
|
|
3405
|
+
if (valid.includes(value)) {
|
|
3406
|
+
return value;
|
|
3407
|
+
}
|
|
3408
|
+
return fallback;
|
|
3409
|
+
}
|
|
3410
|
+
function toPlain(md) {
|
|
3411
|
+
let text = md;
|
|
3412
|
+
text = text.replace(/^[ \t]*([-*_]){3,}[ \t]*$/gm, "");
|
|
3413
|
+
text = text.replace(/^#{1,6}\s+(.+)$/gm, (_match, heading) => {
|
|
3414
|
+
const cleaned = heading.trim();
|
|
3415
|
+
if (/[.:!?]$/.test(cleaned)) {
|
|
3416
|
+
return cleaned;
|
|
3417
|
+
}
|
|
3418
|
+
return `${cleaned}:`;
|
|
3419
|
+
});
|
|
3420
|
+
text = text.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
3421
|
+
text = text.replace(/__(.+?)__/g, "$1");
|
|
3422
|
+
text = text.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "$1");
|
|
3423
|
+
text = text.replace(/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, "$1");
|
|
3424
|
+
text = text.replace(/~~(.+?)~~/g, "$1");
|
|
3425
|
+
text = text.replace(/`([^`]+)`/g, "$1");
|
|
3426
|
+
text = text.replace(/```[\s\S]*?\n([\s\S]*?)```/g, "$1");
|
|
3427
|
+
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)");
|
|
3428
|
+
text = text.replace(/!\[([^\]]*)\]\([^)]+\)/g, "[Image: $1]");
|
|
3429
|
+
text = text.replace(/^[ \t]*[-*]\s+/gm, "\u2022 ");
|
|
3430
|
+
text = text.replace(/^>+\s?/gm, "");
|
|
3431
|
+
text = text.replace(/\n{3,}/g, "\n\n");
|
|
3432
|
+
return text.trim();
|
|
3433
|
+
}
|
|
3434
|
+
function toSlack(md) {
|
|
3435
|
+
let text = md;
|
|
3436
|
+
const PH_START = "\uE000";
|
|
3437
|
+
const PH_END = "\uE001";
|
|
3438
|
+
const codeBlocks = [];
|
|
3439
|
+
text = text.replace(/```\w*\n([\s\S]*?)```/g, (_match, code) => {
|
|
3440
|
+
const placeholder = `${PH_START}CB${codeBlocks.length}${PH_END}`;
|
|
3441
|
+
codeBlocks.push("```\n" + code + "```");
|
|
3442
|
+
return placeholder;
|
|
3443
|
+
});
|
|
3444
|
+
const inlineCodes = [];
|
|
3445
|
+
text = text.replace(/`([^`]+)`/g, (_match, code) => {
|
|
3446
|
+
const placeholder = `${PH_START}IC${inlineCodes.length}${PH_END}`;
|
|
3447
|
+
inlineCodes.push("`" + code + "`");
|
|
3448
|
+
return placeholder;
|
|
3449
|
+
});
|
|
3450
|
+
text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, "<$2|$1>");
|
|
3451
|
+
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<$2|$1>");
|
|
3452
|
+
text = text.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "_$1_");
|
|
3453
|
+
text = text.replace(/\*\*(.+?)\*\*/g, "*$1*");
|
|
3454
|
+
text = text.replace(/(?<!\uE000)__(.+?)__(?!\uE001)/g, "*$1*");
|
|
3455
|
+
text = text.replace(/^#{1,6}\s+(.+)$/gm, "*$1*");
|
|
3456
|
+
text = text.replace(/~~(.+?)~~/g, "~$1~");
|
|
3457
|
+
text = text.replace(/^[ \t]*[-*]\s+/gm, "\u2022 ");
|
|
3458
|
+
text = text.replace(/^[ \t]*([-*_]){3,}[ \t]*$/gm, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
3459
|
+
for (let i = 0; i < codeBlocks.length; i++) {
|
|
3460
|
+
text = text.replace(`${PH_START}CB${i}${PH_END}`, codeBlocks[i]);
|
|
3461
|
+
}
|
|
3462
|
+
for (let i = 0; i < inlineCodes.length; i++) {
|
|
3463
|
+
text = text.replace(`${PH_START}IC${i}${PH_END}`, inlineCodes[i]);
|
|
3464
|
+
}
|
|
3465
|
+
text = text.replace(/\n{3,}/g, "\n\n");
|
|
3466
|
+
return text.trim();
|
|
3467
|
+
}
|
|
3468
|
+
function toJSON(md, meta) {
|
|
3469
|
+
const sections = parseSections(md);
|
|
3470
|
+
const output = {
|
|
3471
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3472
|
+
};
|
|
3473
|
+
if (meta) {
|
|
3474
|
+
output.meta = { ...meta };
|
|
3475
|
+
}
|
|
3476
|
+
output.sections = sections;
|
|
3477
|
+
output.raw = md;
|
|
3478
|
+
return JSON.stringify(output, null, 2);
|
|
3479
|
+
}
|
|
3480
|
+
function parseSections(md) {
|
|
3481
|
+
const lines = md.split("\n");
|
|
3482
|
+
const sections = [];
|
|
3483
|
+
let current = null;
|
|
3484
|
+
let bodyLines = [];
|
|
3485
|
+
const flushCurrent = () => {
|
|
3486
|
+
if (current) {
|
|
3487
|
+
current.body = bodyLines.join("\n").trim();
|
|
3488
|
+
current.items = extractListItems(current.body);
|
|
3489
|
+
sections.push(current);
|
|
3490
|
+
}
|
|
3491
|
+
};
|
|
3492
|
+
for (const line of lines) {
|
|
3493
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
3494
|
+
if (headingMatch) {
|
|
3495
|
+
flushCurrent();
|
|
3496
|
+
current = {
|
|
3497
|
+
heading: headingMatch[2].trim(),
|
|
3498
|
+
level: headingMatch[1].length,
|
|
3499
|
+
items: [],
|
|
3500
|
+
body: ""
|
|
3501
|
+
};
|
|
3502
|
+
bodyLines = [];
|
|
3503
|
+
} else {
|
|
3504
|
+
bodyLines.push(line);
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
flushCurrent();
|
|
3508
|
+
if (sections.length === 0 && md.trim().length > 0) {
|
|
3509
|
+
const body = md.trim();
|
|
3510
|
+
sections.push({
|
|
3511
|
+
heading: "Content",
|
|
3512
|
+
level: 0,
|
|
3513
|
+
items: extractListItems(body),
|
|
3514
|
+
body
|
|
3515
|
+
});
|
|
3516
|
+
}
|
|
3517
|
+
return sections;
|
|
3518
|
+
}
|
|
3519
|
+
function extractListItems(body) {
|
|
3520
|
+
const items = [];
|
|
3521
|
+
for (const line of body.split("\n")) {
|
|
3522
|
+
const match = line.match(/^[ \t]*(?:[-*]|\d+[.)]) \s*(.+)$/);
|
|
3523
|
+
if (match) {
|
|
3524
|
+
items.push(match[1].trim());
|
|
3525
|
+
}
|
|
3526
|
+
}
|
|
3527
|
+
return items;
|
|
3528
|
+
}
|
|
3529
|
+
|
|
3389
3530
|
// src/core/notifications.ts
|
|
3390
3531
|
function getTypeEmoji(type) {
|
|
3391
3532
|
switch (type) {
|
|
@@ -3427,11 +3568,12 @@ function formatSlackMessage(message) {
|
|
|
3427
3568
|
}
|
|
3428
3569
|
});
|
|
3429
3570
|
}
|
|
3571
|
+
const slackText = formatOutput(message.text, "slack").text;
|
|
3430
3572
|
blocks.push({
|
|
3431
3573
|
type: "section",
|
|
3432
3574
|
text: {
|
|
3433
3575
|
type: "mrkdwn",
|
|
3434
|
-
text:
|
|
3576
|
+
text: slackText
|
|
3435
3577
|
}
|
|
3436
3578
|
});
|
|
3437
3579
|
if (message.ticketLinks && message.ticketLinks.length > 0) {
|
|
@@ -3988,147 +4130,6 @@ function getWeekEnd(weeksAgo = 0) {
|
|
|
3988
4130
|
return end;
|
|
3989
4131
|
}
|
|
3990
4132
|
|
|
3991
|
-
// src/utils/formatter.ts
|
|
3992
|
-
function formatOutput(markdown, format = "markdown", meta) {
|
|
3993
|
-
switch (format) {
|
|
3994
|
-
case "plain":
|
|
3995
|
-
return { text: toPlain(markdown), raw: markdown, format };
|
|
3996
|
-
case "slack":
|
|
3997
|
-
return { text: toSlack(markdown), raw: markdown, format };
|
|
3998
|
-
case "json":
|
|
3999
|
-
return { text: toJSON(markdown, meta), raw: markdown, format };
|
|
4000
|
-
case "markdown":
|
|
4001
|
-
default:
|
|
4002
|
-
return { text: markdown, raw: markdown, format };
|
|
4003
|
-
}
|
|
4004
|
-
}
|
|
4005
|
-
function validateFormat(value, fallback = "markdown") {
|
|
4006
|
-
const valid = ["markdown", "slack", "plain", "json"];
|
|
4007
|
-
if (valid.includes(value)) {
|
|
4008
|
-
return value;
|
|
4009
|
-
}
|
|
4010
|
-
return fallback;
|
|
4011
|
-
}
|
|
4012
|
-
function toPlain(md) {
|
|
4013
|
-
let text = md;
|
|
4014
|
-
text = text.replace(/^[ \t]*([-*_]){3,}[ \t]*$/gm, "");
|
|
4015
|
-
text = text.replace(/^#{1,6}\s+(.+)$/gm, (_match, heading) => {
|
|
4016
|
-
const cleaned = heading.trim();
|
|
4017
|
-
if (/[.:!?]$/.test(cleaned)) {
|
|
4018
|
-
return cleaned;
|
|
4019
|
-
}
|
|
4020
|
-
return `${cleaned}:`;
|
|
4021
|
-
});
|
|
4022
|
-
text = text.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
4023
|
-
text = text.replace(/__(.+?)__/g, "$1");
|
|
4024
|
-
text = text.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "$1");
|
|
4025
|
-
text = text.replace(/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, "$1");
|
|
4026
|
-
text = text.replace(/~~(.+?)~~/g, "$1");
|
|
4027
|
-
text = text.replace(/`([^`]+)`/g, "$1");
|
|
4028
|
-
text = text.replace(/```[\s\S]*?\n([\s\S]*?)```/g, "$1");
|
|
4029
|
-
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)");
|
|
4030
|
-
text = text.replace(/!\[([^\]]*)\]\([^)]+\)/g, "[Image: $1]");
|
|
4031
|
-
text = text.replace(/^[ \t]*[-*]\s+/gm, "\u2022 ");
|
|
4032
|
-
text = text.replace(/^>+\s?/gm, "");
|
|
4033
|
-
text = text.replace(/\n{3,}/g, "\n\n");
|
|
4034
|
-
return text.trim();
|
|
4035
|
-
}
|
|
4036
|
-
function toSlack(md) {
|
|
4037
|
-
let text = md;
|
|
4038
|
-
const PH_START = "\uE000";
|
|
4039
|
-
const PH_END = "\uE001";
|
|
4040
|
-
const codeBlocks = [];
|
|
4041
|
-
text = text.replace(/```\w*\n([\s\S]*?)```/g, (_match, code) => {
|
|
4042
|
-
const placeholder = `${PH_START}CB${codeBlocks.length}${PH_END}`;
|
|
4043
|
-
codeBlocks.push("```\n" + code + "```");
|
|
4044
|
-
return placeholder;
|
|
4045
|
-
});
|
|
4046
|
-
const inlineCodes = [];
|
|
4047
|
-
text = text.replace(/`([^`]+)`/g, (_match, code) => {
|
|
4048
|
-
const placeholder = `${PH_START}IC${inlineCodes.length}${PH_END}`;
|
|
4049
|
-
inlineCodes.push("`" + code + "`");
|
|
4050
|
-
return placeholder;
|
|
4051
|
-
});
|
|
4052
|
-
text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, "<$2|$1>");
|
|
4053
|
-
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<$2|$1>");
|
|
4054
|
-
text = text.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "_$1_");
|
|
4055
|
-
text = text.replace(/\*\*(.+?)\*\*/g, "*$1*");
|
|
4056
|
-
text = text.replace(/(?<!\uE000)__(.+?)__(?!\uE001)/g, "*$1*");
|
|
4057
|
-
text = text.replace(/^#{1,6}\s+(.+)$/gm, "*$1*");
|
|
4058
|
-
text = text.replace(/~~(.+?)~~/g, "~$1~");
|
|
4059
|
-
text = text.replace(/^[ \t]*[-*]\s+/gm, "\u2022 ");
|
|
4060
|
-
text = text.replace(/^[ \t]*([-*_]){3,}[ \t]*$/gm, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
4061
|
-
for (let i = 0; i < codeBlocks.length; i++) {
|
|
4062
|
-
text = text.replace(`${PH_START}CB${i}${PH_END}`, codeBlocks[i]);
|
|
4063
|
-
}
|
|
4064
|
-
for (let i = 0; i < inlineCodes.length; i++) {
|
|
4065
|
-
text = text.replace(`${PH_START}IC${i}${PH_END}`, inlineCodes[i]);
|
|
4066
|
-
}
|
|
4067
|
-
text = text.replace(/\n{3,}/g, "\n\n");
|
|
4068
|
-
return text.trim();
|
|
4069
|
-
}
|
|
4070
|
-
function toJSON(md, meta) {
|
|
4071
|
-
const sections = parseSections(md);
|
|
4072
|
-
const output = {
|
|
4073
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4074
|
-
};
|
|
4075
|
-
if (meta) {
|
|
4076
|
-
output.meta = { ...meta };
|
|
4077
|
-
}
|
|
4078
|
-
output.sections = sections;
|
|
4079
|
-
output.raw = md;
|
|
4080
|
-
return JSON.stringify(output, null, 2);
|
|
4081
|
-
}
|
|
4082
|
-
function parseSections(md) {
|
|
4083
|
-
const lines = md.split("\n");
|
|
4084
|
-
const sections = [];
|
|
4085
|
-
let current = null;
|
|
4086
|
-
let bodyLines = [];
|
|
4087
|
-
const flushCurrent = () => {
|
|
4088
|
-
if (current) {
|
|
4089
|
-
current.body = bodyLines.join("\n").trim();
|
|
4090
|
-
current.items = extractListItems(current.body);
|
|
4091
|
-
sections.push(current);
|
|
4092
|
-
}
|
|
4093
|
-
};
|
|
4094
|
-
for (const line of lines) {
|
|
4095
|
-
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
4096
|
-
if (headingMatch) {
|
|
4097
|
-
flushCurrent();
|
|
4098
|
-
current = {
|
|
4099
|
-
heading: headingMatch[2].trim(),
|
|
4100
|
-
level: headingMatch[1].length,
|
|
4101
|
-
items: [],
|
|
4102
|
-
body: ""
|
|
4103
|
-
};
|
|
4104
|
-
bodyLines = [];
|
|
4105
|
-
} else {
|
|
4106
|
-
bodyLines.push(line);
|
|
4107
|
-
}
|
|
4108
|
-
}
|
|
4109
|
-
flushCurrent();
|
|
4110
|
-
if (sections.length === 0 && md.trim().length > 0) {
|
|
4111
|
-
const body = md.trim();
|
|
4112
|
-
sections.push({
|
|
4113
|
-
heading: "Content",
|
|
4114
|
-
level: 0,
|
|
4115
|
-
items: extractListItems(body),
|
|
4116
|
-
body
|
|
4117
|
-
});
|
|
4118
|
-
}
|
|
4119
|
-
return sections;
|
|
4120
|
-
}
|
|
4121
|
-
function extractListItems(body) {
|
|
4122
|
-
const items = [];
|
|
4123
|
-
for (const line of body.split("\n")) {
|
|
4124
|
-
const match = line.match(/^[ \t]*(?:[-*]|\d+[.)]) \s*(.+)$/);
|
|
4125
|
-
if (match) {
|
|
4126
|
-
items.push(match[1].trim());
|
|
4127
|
-
}
|
|
4128
|
-
}
|
|
4129
|
-
return items;
|
|
4130
|
-
}
|
|
4131
|
-
|
|
4132
4133
|
// src/core/work-journal.ts
|
|
4133
4134
|
import {
|
|
4134
4135
|
existsSync as existsSync2,
|